Improve the the MCP server demo code

This commit is contained in:
Christian Tzolov
2025-01-17 14:47:12 +01:00
parent 9a7fcd517f
commit 9766aa7e0f
10 changed files with 37 additions and 310 deletions

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.webmvc.client;
package org.springframework.ai.mcp.sample.servlet.client;
import org.springframework.ai.mcp.client.transport.ServerParameters;
import org.springframework.ai.mcp.client.transport.StdioClientTransport;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.webmvc.client;
package org.springframework.ai.mcp.sample.servlet.client;
import org.springframework.ai.mcp.client.transport.HttpClientSseClientTransport;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.webmvc.client;
package org.springframework.ai.mcp.sample.servlet.client;
import java.util.Map;
@@ -50,15 +50,12 @@ public class SampleClient {
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult weatherResponse = client.callTool(new CallToolRequest("weather", Map.of("city", "Sofia")));
System.out.println("Weather Response = " + weatherResponse);
CallToolResult upperCaseResult = client
.callTool(new CallToolRequest("toUpperCase", Map.of("input", "accountName")));
System.out.println("Upper case Response = " + upperCaseResult);
CallToolResult paymentStatus = client.callTool(
new CallToolRequest("paymentTransactionStatus", Map.of("transactionId", "006", "accountName", "John")));
System.out.println("Payment Status Response = " + paymentStatus);
CallToolResult parks = client.callTool(new CallToolRequest("getBooks", Map.of("title", "Spring Framework")));
System.out.println("Books Response = " + parks);
CallToolResult books = client.callTool(new CallToolRequest("getBooks", Map.of("title", "Spring Framework")));
System.out.println("Books Response = " + books);
// List and demonstrate resources
var resourcesList = client.listResources();

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.mcp.sample.webmvc.server;
package org.springframework.ai.mcp.sample.servlet.server;
import java.util.List;
import java.util.Map;
@@ -16,9 +16,7 @@ import org.springframework.ai.mcp.server.McpServer.ToolRegistration;
import org.springframework.ai.mcp.server.transport.HttpServletSseServerTransport;
import org.springframework.ai.mcp.server.transport.StdioServerTransport;
import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpSchema.CallToolResult;
import org.springframework.ai.mcp.spec.McpSchema.GetPromptResult;
import org.springframework.ai.mcp.spec.McpSchema.LoggingMessageNotification;
import org.springframework.ai.mcp.spec.McpSchema.PromptMessage;
import org.springframework.ai.mcp.spec.McpSchema.Role;
import org.springframework.ai.mcp.spec.McpSchema.TextContent;
@@ -74,32 +72,19 @@ public class McpServerConfig implements WebMvcConfigurer {
// Create the server with both tool and resource capabilities
var server = McpServer.using(transport)
.serverInfo("MCP Demo Server", "1.0.0")
.serverInfo("MCP Demo Servlet Server", "1.0.0")
.capabilities(capabilities)
.resources(systemInfoResourceRegistration())
.prompts(greetingPromptRegistration())
.tools(
ToolHelper.toToolRegistration(
FunctionCallback.builder()
.method("paymentTransactionStatus",String.class, String.class)
.description("Get transaction payment status")
.targetClass(McpServerConfig.class)
.build()),
ToolHelper.toToolRegistration(
FunctionCallback.builder()
.function("toUpperCase", new Function<ToUpperCaseInput, String>() {
@Override
public String apply(ToUpperCaseInput s) {
return s.input().toUpperCase();
}
})
.function("toUpperCase", (Function<ToUpperCaseInput, String>) s -> s.input().toUpperCase())
.description("To upper case")
.inputType(ToUpperCaseInput.class)
.build()))
.tools(openLibraryToolRegistrations(openLibrary))
.async();
server.addTool(weatherToolRegistration(server));
.async();
return server; // @formatter:on
} // @formatter:on
@@ -172,40 +157,6 @@ public class McpServerConfig implements WebMvcConfigurer {
});
}
private static ToolRegistration weatherToolRegistration(McpAsyncServer server) {
String emptyJsonSchema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {"city" : "string"}
}
""";
return new ToolRegistration(new McpSchema.Tool("weather", "Weather forecast tool by location", emptyJsonSchema),
(arguments) -> {
String city = (String) arguments.get("city");
// Create the result
var result = new CallToolResult(
List.of(new TextContent("Weather forecast for " + city + " is sunny")), false);
// Send the logging notification and ignore its completion
server
.loggingNotification(LoggingMessageNotification.builder()
.data("This is a log message from the weather tool")
.build())
.subscribe(null, error -> {
// Log any errors but don't fail the operation
logger.error("Failed to send logging notification", error);
});
return result;
});
}
public static String paymentTransactionStatus(String transactionId, String accountName) {
return "The status for " + transactionId + ", by " + accountName + " is PENDING";
}
@Bean
public OpenLibrary openLibrary() {
return new OpenLibrary(RestClient.builder());

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.mcp.sample.webmvc.server;
package org.springframework.ai.mcp.sample.servlet.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.webmvc.server;
package org.springframework.ai.mcp.sample.servlet.server;
import java.util.List;
import java.util.Map;

View File

@@ -50,19 +50,12 @@ public class SampleClient {
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult weatherResponse = client.callTool(new CallToolRequest("weather", Map.of("city", "Sofia")));
System.out.println("Weather Response = " + weatherResponse);
CallToolResult upperCaseResult = client
.callTool(new CallToolRequest("toUpperCase", Map.of("input", "accountName")));
System.out.println("Upper case Response = " + upperCaseResult);
CallToolResult calcResponse = client
.callTool(new CallToolRequest("calculator", Map.of("operation", "multiply", "a", 2.0, "b", 3.0)));
System.out.println("Calculator Response = " + calcResponse);
CallToolResult paymentStatus = client.callTool(
new CallToolRequest("paymentTransactionStatus", Map.of("transactionId", "006", "accountName", "John")));
System.out.println("Payment Status Response = " + paymentStatus);
CallToolResult parks = client.callTool(new CallToolRequest("getBooks", Map.of("title", "Spring Framework")));
System.out.println("Books Response = " + parks);
CallToolResult books = client.callTool(new CallToolRequest("getBooks", Map.of("title", "Spring Framework")));
System.out.println("Books Response = " + books);
// List and demonstrate resources
var resourcesList = client.listResources();

View File

@@ -74,33 +74,19 @@ public class McpServerConfig {
// Create the server with both tool and resource capabilities
var server = McpServer.using(transport)
.serverInfo("MCP Demo Server", "1.0.0")
.serverInfo("MCP Demo WebFlux Server", "1.0.0")
.capabilities(capabilities)
.resources(systemInfoResourceRegistration())
.prompts(greetingPromptRegistration())
.tools(calculatorToolRegistration(),
ToolHelper.toToolRegistration(
.resources(systemInfoResourceRegistration()) // Resources
.prompts(greetingPromptRegistration()) // Prompts
.tools(openLibraryToolRegistrations(openLibrary)) // Method based tools
.tools(ToolHelper.toToolRegistration( // java.util.Function based tools
FunctionCallback.builder()
.method("paymentTransactionStatus",String.class, String.class)
.description("Get transaction payment status")
.targetClass(McpServerConfig.class)
.build()),
ToolHelper.toToolRegistration(
FunctionCallback.builder()
.function("toUpperCase", new Function<ToUpperCaseInput, String>() {
@Override
public String apply(ToUpperCaseInput s) {
return s.input().toUpperCase();
}
})
.function("toUpperCase", (Function<ToUpperCaseInput, String>) s -> s.input().toUpperCase())
.description("To upper case")
.inputType(ToUpperCaseInput.class)
.build())
)
.tools(openLibraryToolRegistrations(openLibrary))
.build()))
.async();
server.addTool(weatherToolRegistration(server));
return server; // @formatter:on
} // @formatter:on
@@ -173,96 +159,6 @@ public class McpServerConfig {
});
}
private static ToolRegistration weatherToolRegistration(McpAsyncServer server) {
String emptyJsonSchema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {"city" : "string"}
}
""";
return new ToolRegistration(new McpSchema.Tool("weather", "Weather forecast tool by location", emptyJsonSchema),
(arguments) -> {
String city = (String) arguments.get("city");
// Create the result
var result = new CallToolResult(
List.of(new TextContent("Weather forecast for " + city + " is sunny")), false);
// Send the logging notification and ignore its completion
server
.loggingNotification(LoggingMessageNotification.builder()
.data("This is a log message from the weather tool")
.build())
.subscribe(null, error -> {
// Log any errors but don't fail the operation
logger.error("Failed to send logging notification", error);
});
return result;
});
}
private static ToolRegistration calculatorToolRegistration() {
return new ToolRegistration(new McpSchema.Tool("calculator",
"Performs basic arithmetic operations (add, subtract, multiply, divide)", """
{
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "The arithmetic operation to perform"
},
"a": {
"type": "number",
"description": "First operand"
},
"b": {
"type": "number",
"description": "Second operand"
}
},
"required": ["operation", "a", "b"]
}
"""), arguments -> {
String operation = (String) arguments.get("operation");
double a = (Integer) arguments.get("a");
double b = (Integer) arguments.get("b");
double result;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b == 0) {
return new McpSchema.CallToolResult(
java.util.List.of(new McpSchema.TextContent("Division by zero")), true);
}
result = a / b;
break;
default:
return new McpSchema.CallToolResult(
java.util.List.of(new McpSchema.TextContent("Unknown operation: " + operation)),
true);
}
return new McpSchema.CallToolResult(
java.util.List.of(new McpSchema.TextContent(String.valueOf(result))), false);
});
}
public static String paymentTransactionStatus(String transactionId, String accountName) {
return "The status for " + transactionId + ", by " + accountName + " is PENDING";
}
@Bean
public OpenLibrary openLibrary() {
return new OpenLibrary(RestClient.builder());

View File

@@ -50,19 +50,12 @@ public class SampleClient {
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult weatherResponse = client.callTool(new CallToolRequest("weather", Map.of("city", "Sofia")));
System.out.println("Weather Response = " + weatherResponse);
CallToolResult upperCaseResult = client
.callTool(new CallToolRequest("toUpperCase", Map.of("input", "accountName")));
System.out.println("Upper case Response = " + upperCaseResult);
CallToolResult calcResponse = client
.callTool(new CallToolRequest("calculator", Map.of("operation", "multiply", "a", 2.0, "b", 3.0)));
System.out.println("Calculator Response = " + calcResponse);
CallToolResult paymentStatus = client.callTool(
new CallToolRequest("paymentTransactionStatus", Map.of("transactionId", "006", "accountName", "John")));
System.out.println("Payment Status Response = " + paymentStatus);
CallToolResult parks = client.callTool(new CallToolRequest("getBooks", Map.of("title", "Spring Framework")));
System.out.println("Books Response = " + parks);
CallToolResult books = client.callTool(new CallToolRequest("getBooks", Map.of("title", "Spring Framework")));
System.out.println("Books Response = " + books);
// List and demonstrate resources
var resourcesList = client.listResources();

View File

@@ -75,32 +75,19 @@ public class McpServerConfig implements WebMvcConfigurer {
// Create the server with both tool and resource capabilities
var server = McpServer.using(transport)
.serverInfo("MCP Demo Server", "1.0.0")
.serverInfo("MCP Demo WebMVC Server", "1.0.0")
.capabilities(capabilities)
.resources(systemInfoResourceRegistration())
.prompts(greetingPromptRegistration())
.tools(calculatorToolRegistration(),
.tools(
ToolHelper.toToolRegistration(
FunctionCallback.builder()
.method("paymentTransactionStatus",String.class, String.class)
.description("Get transaction payment status")
.targetClass(McpServerConfig.class)
.build()),
ToolHelper.toToolRegistration(
FunctionCallback.builder()
.function("toUpperCase", new Function<ToUpperCaseInput, String>() {
@Override
public String apply(ToUpperCaseInput s) {
return s.input().toUpperCase();
}
})
.description("To upper case")
.inputType(ToUpperCaseInput.class)
.build()))
.function("toUpperCase", (Function<ToUpperCaseInput, String>) s -> s.input().toUpperCase())
.description("To upper case")
.inputType(ToUpperCaseInput.class)
.build()))
.tools(openLibraryToolRegistrations(openLibrary))
.async();
server.addTool(weatherToolRegistration(server));
return server; // @formatter:on
} // @formatter:on
@@ -173,96 +160,6 @@ public class McpServerConfig implements WebMvcConfigurer {
});
}
private static ToolRegistration weatherToolRegistration(McpAsyncServer server) {
String emptyJsonSchema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {"city" : "string"}
}
""";
return new ToolRegistration(new McpSchema.Tool("weather", "Weather forecast tool by location", emptyJsonSchema),
(arguments) -> {
String city = (String) arguments.get("city");
// Create the result
var result = new CallToolResult(
List.of(new TextContent("Weather forecast for " + city + " is sunny")), false);
// Send the logging notification and ignore its completion
server
.loggingNotification(LoggingMessageNotification.builder()
.data("This is a log message from the weather tool")
.build())
.subscribe(null, error -> {
// Log any errors but don't fail the operation
logger.error("Failed to send logging notification", error);
});
return result;
});
}
private static ToolRegistration calculatorToolRegistration() {
return new ToolRegistration(new McpSchema.Tool("calculator",
"Performs basic arithmetic operations (add, subtract, multiply, divide)", """
{
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "The arithmetic operation to perform"
},
"a": {
"type": "number",
"description": "First operand"
},
"b": {
"type": "number",
"description": "Second operand"
}
},
"required": ["operation", "a", "b"]
}
"""), arguments -> {
String operation = (String) arguments.get("operation");
double a = (Integer) arguments.get("a");
double b = (Integer) arguments.get("b");
double result;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b == 0) {
return new McpSchema.CallToolResult(
java.util.List.of(new McpSchema.TextContent("Division by zero")), true);
}
result = a / b;
break;
default:
return new McpSchema.CallToolResult(
java.util.List.of(new McpSchema.TextContent("Unknown operation: " + operation)),
true);
}
return new McpSchema.CallToolResult(
java.util.List.of(new McpSchema.TextContent(String.valueOf(result))), false);
});
}
public static String paymentTransactionStatus(String transactionId, String accountName) {
return "The status for " + transactionId + ", by " + accountName + " is PENDING";
}
@Bean
public OpenLibrary openLibrary() {
return new OpenLibrary(RestClient.builder());