diff --git a/mcp/README.md b/mcp/README.md index be0a690..7851f21 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -1,10 +1,6 @@ -# Model Context Protocol (MCP) Java SDK +# Spring AI MCP SDK -A Java implementation of the [Model Context Protocol](https://modelcontextprotocol.org/docs/concepts/architecture) specification, providing both synchronous and asynchronous clients for MCP server interactions. - -## Overview - -This SDK implements the Model Context Protocol, enabling seamless integration with AI models and tools through a standardized interface. It supports both synchronous and asynchronous communication patterns, making it suitable for various use cases and integration scenarios. +This SDK implements the Model Context Protocol, enabling seamless integration with language models and AI tools. ## Features @@ -46,11 +42,6 @@ The SDK follows a layered architecture with clear separation of concerns: - **Session Layer (McpSession)**: Manages communication patterns and state using DefaultMcpSession implementation. - **Client/Server Layer**: Both use McpSession for sync/async operations, with McpClient handling client-side protocol operations and McpServer managing server-side protocol operations. -Following class diagram illustrates the layered architecture of the MCP SDK, showing the relationships between core interfaces (McpTransport, McpSession), their implementations, and the client/server components. It highlights how the transport layer connects to sessions, which in turn support both synchronous and asynchronous client/server implementations. - - - - ### Key Interactions - **Client/Server Initialization**: Transport setup, protocol compatibility check, capability negotiation, and implementation details exchange. @@ -59,149 +50,183 @@ Following class diagram illustrates the layered architecture of the MCP SDK, sho - **Prompt System**: Discovery, parameter-based retrieval, change notifications, and content management. - **Tool Execution**: Discovery, parameter validation, timeout-aware execution, and result processing. +## Client Usage Examples -### [Reference Documentation](../docs/ref-index.md) -Detailed reference documentation covering the Spring AI implementation of the Model Context Protocol (MCP) specification. - - -## Quick Start - -### McpClient - -Factory for creating `sync` and `async` variants MCP clients. Uses McpSession for communication and handles client-side protocol operations. - -#### Sync MCP Client +### MCP Client (Sync API) ```java -// Create and initialize sync client -McpTransport mcpTransport = ... +// Create a sync client with custom configuration +McpSyncClient client = McpClient.using(transport) + .requestTimeout(Duration.ofSeconds(10)) + .capabilities(ClientCapabilities.builder() + .roots(true) // Enable roots capability + .sampling() // Enable sampling capability + .build()) + .sync(); -try (McpSyncClient client = McpClient.using(mcpTransport).sync()) { - // Initialize connection with protocol version and capabilities - InitializeResult initResult = client.initialize(); +// Initialize connection +client.initialize(); - // List available tools - ListToolsResult tools = client.listTools(); +// List available tools +ListToolsResult tools = client.listTools(); - // Execute a tool - CallToolResult result = client.callTool( - new CallToolRequest("echo", Map.of("message", "Hello!")) - ); +// Call a tool +CallToolResult result = client.callTool( + new CallToolRequest("calculator", + Map.of("operation", "add", "a", 2, "b", 3)) +); - // Resource management - ListResourcesResult resources = client.listResources(); - ReadResourceResult resource = client.readResource( - new ReadResourceRequest("resource-uri") - ); +// List and read resources +ListResourcesResult resources = client.listResources(); +ReadResourceResult resource = client.readResource( + new ReadResourceRequest("resource://uri") +); - // List and retrieve prompts - ListPromptsResult prompts = client.listPrompts(); - GetPromptResult prompt = client.getPrompt( - new GetPromptRequest("prompt-id", Map.of()) - ); -} +// List and use prompts +ListPromptsResult prompts = client.listPrompts(); +GetPromptResult prompt = client.getPrompt( + new GetPromptRequest("greeting", Map.of("name", "Spring")) +); + +// Add/remove roots +client.addRoot(new Root("file:///path", "description")); +client.removeRoot("file:///path"); +client.rootsListChangedNotification(); + +// Close client +client.closeGracefully(); ``` -#### Async MCP Client +### MCP Client (Async API) ```java -McpTransport mcpTransport = ... - -// Initialize async client with custom timeout -McpAsyncClient client = McpClient.using(mcpTransport) - .requestTimeout(Duration.ofSeconds(30)) +// Create an async client with custom configuration +McpAsyncClient client = McpClient.using(transport) + .requestTimeout(Duration.ofSeconds(10)) + .capabilities(ClientCapabilities.builder() + .roots(true) // Enable roots capability + .sampling() // Enable sampling capability + .build()) + .toolsChangeConsumer(tools -> { + logger.info("Tools updated: {}", tools); + }) + .resourcesChangeConsumer(resources -> { + logger.info("Resources updated: {}", resources); + }) + .promptsChangeConsumer(prompts -> { + logger.info("Prompts updated: {}", prompts); + }) .async(); -// Initialize connection and chain operations -var result = client.initialize() - // Connection initialized with protocol version compatibility - .flatMap(initResult -> client.listTools()) - // Process available tools - .flatMap(tools -> client.callTool( - new CallToolRequest("echo", Map.of("message", "Hello MCP!")))) - // Handle tool execution result - .flatMap(toolResult -> client.listPrompts()) - // Process available prompts - .flatMap(prompts -> client.getPrompt(new GetPromptRequest("prompt-id", Map.of())); - -// Subscribe to resource changes -var subscription = client.listResources() - .flatMap(resources -> client.subscribeResource(new SubscribeRequest("resource-uri"))); - -// Handle results reactively or block if needed -GetPromptResult promptResult = result.block(); -subscription.block(); - -// Cleanup -client.closeGracefully().block(); +// Initialize connection +client.initialize() + .flatMap(initResult -> { + // List available tools + return client.listTools(); + }) + .flatMap(tools -> { + // Call a tool + return client.callTool(new CallToolRequest( + "calculator", + Map.of("operation", "add", "a", 2, "b", 3) + )); + }) + .flatMap(result -> { + // List and read resources + return client.listResources() + .flatMap(resources -> + client.readResource(new ReadResourceRequest("resource://uri")) + ); + }) + .flatMap(resource -> { + // List and use prompts + return client.listPrompts() + .flatMap(prompts -> + client.getPrompt(new GetPromptRequest( + "greeting", + Map.of("name", "Spring") + )) + ); + }) + .flatMap(prompt -> { + // Add/remove roots + return client.addRoot(new Root("file:///path", "description")) + .then(client.removeRoot("file:///path")) + .then(client.rootsListChangedNotification()); + }) + .doFinally(signalType -> { + // Close client + client.closeGracefully().subscribe(); + }) + .subscribe(); ``` -#### Client Transports -Clients can use the following transport options: - -##### StdioClientTransport -Standard I/O based transport for process-based communication with MCP servers: +### Client Transport Options +#### StdioClientTransport ```java +// Create transport for process-based communication ServerParameters params = ServerParameters.builder("npx") .args("-y", "@modelcontextprotocol/server-everything", "dir") .build(); McpTransport transport = new StdioClientTransport(params); ``` -##### SseClientTransport -Server-Sent Events (SSE) based transport following the MCP HTTP with SSE transport specification: - +#### SseClientTransport ```java +// Create transport for HTTP streaming WebClient.Builder webClientBuilder = WebClient.builder() .baseUrl("http://your-mcp-server"); McpTransport transport = new SseClientTransport(webClientBuilder); ``` -### MCP Server +## Server Usage Examples -Factory for creating `sync` and `async` variants MCP servers. Uses McpSession for communication and handles server-side protocol operations. - -#### MCP Sync Server +### MCP Server (Sync API) ```java -// Create a sync server with custom configuration +// Create a server with custom configuration McpSyncServer syncServer = McpServer.using(transport) - .serverInfo("my-server", "1.0.0") - .serverCapabilities(new ServerCapabilities(...)) - .addTool(new CalculatorTool()) - .resourcesProvider(cursor -> resources) - .promptsProvider(cursor -> prompts) + .info("my-server", "1.0.0") + .capabilities(ServerCapabilities.builder()...build()) + .tools(new CalculatorTool()) + .resources(resourceRegistration) + .prompts(promptRegistration) .sync(); // Add a tool handler at runtime syncServer.addTool(new CalculatorTool()); -logger.info("Tool added"); -// Remove a tool handler +// Remove a tool handler at runtime syncServer.removeTool("calculator"); -logger.info("Tool removed"); -// Notify clients of changes -syncServer.notifyToolsListChanged(); -syncServer.notifyResourcesListChanged(); -syncServer.notifyPromptsListChanged(); +// Add a resource at runtime +syncServer.addResource(resourceRegistration); + +// Remove a resource at runtime +syncServer.removeResource(resourceUri); + +// Add a prompt at runtime +syncServer.addPrompt(promptRegistration); + +// Remove a prompt at runtime +syncServer.removePrompt(promptName); // Graceful shutdown syncServer.closeGracefully(); ``` -#### MCP Async Server +### MCP Server (Async API) ```java // Create an async server with custom configuration McpAsyncServer asyncServer = McpServer.using(transport) - .serverInfo("my-server", "1.0.0") - .serverCapabilities(new ServerCapabilities(...)) - .addTool(new CalculatorTool()) - .resourcesProvider(cursor -> resources) - .promptsProvider(cursor -> prompts) + .info("my-server", "1.0.0") + .capabilities(ServerCapabilities.builder()...build()) + .tools(new CalculatorTool()) + .resources(resourceRegistration) + .prompts(promptRegistration) .async(); // Add a tool handler at runtime @@ -209,11 +234,31 @@ asyncServer.addTool(new CalculatorTool()) .doOnSuccess(v -> logger.info("Tool added")) .subscribe(); -// Remove a tool handler +// Remove a tool handler at runtime asyncServer.removeTool("calculator") .doOnSuccess(v -> logger.info("Tool removed")) .subscribe(); +// Add a resource at runtime +asyncServer.addResource(resourceRegistration) + .doOnSuccess(v -> logger.info("Resource added")) + .subscribe(); + +// Remove a resource at runtime +asyncServer.removeResource(resourceUri) + .doOnSuccess(v -> logger.info("Resource removed")) + .subscribe(); + +// Add a prompt at runtime +asyncServer.addPrompt(promptRegistration) + .doOnSuccess(v -> logger.info("Prompt added")) + .subscribe(); + +// Remove a prompt at runtime +asyncServer.removePrompt(promptName) + .doOnSuccess(v -> logger.info("Prompt removed")) + .subscribe(); + // Notify clients of changes asyncServer.notifyToolsListChanged().subscribe(); asyncServer.notifyResourcesListChanged().subscribe(); @@ -223,12 +268,9 @@ asyncServer.notifyPromptsListChanged().subscribe(); asyncServer.closeGracefully().subscribe(); ``` -#### Server Transports +### Server Transport Options -Servers can use the following transport options: - -##### StdioServerTransport -Standard I/O based transport for process-based communication: +#### StdioServerTransport ```java // Create transport with custom ObjectMapper ObjectMapper mapper = new ObjectMapper(); @@ -237,8 +279,7 @@ StdioServerTransport transport = new StdioServerTransport(mapper); Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support. -##### SseServerTransport -Server-Sent Events based transport for HTTP streaming: +#### SseServerTransport ```java // Create SSE transport ObjectMapper mapper = new ObjectMapper(); @@ -251,26 +292,65 @@ RouterFunction router = transport.getRouterFunction(); Implements the MCP HTTP with SSE transport specification, providing concurrent client connections through SSE endpoints with message routing, session management, and graceful shutdown capabilities. + +### Server Capabilities + +The server can be configured with various capabilities: + +```java +var capabilities = ServerCapabilities.builder() + .resources(false, true) // Resource support with list changes notifications + .tools(true) // Tool support with list changes notifications + .prompts(true) // Prompt support with list changes notifications + .build(); +``` + +### Tool Registration + +```java +var toolRegistration = new ToolRegistration( + new Tool("calculator", "Basic calculator", Map.of( + "operation", "string", + "a", "number", + "b", "number" + )), + arguments -> { + // Tool implementation + return new CallToolResult(result, false); + } +); +``` + +### Resource Registration + +```java +var resourceRegistration = new ResourceRegistration( + new Resource("custom://resource", "name", "description", "mime-type", null), + request -> { + // Resource read implementation + return new ReadResourceResult(contents); + } +); +``` + +### Prompt Registration + +```java +var promptRegistration = new PromptRegistration( + new Prompt("greeting", "description", List.of( + new PromptArgument("name", "description", true) + )), + request -> { + // Prompt implementation + return new GetPromptResult(description, messages); + } +); +``` + ## Error Handling The SDK provides comprehensive error handling through the McpError class, covering protocol compatibility, transport communication, JSON-RPC messaging, tool execution, resource management, prompt handling, timeouts, and connection issues. This unified error handling approach ensures consistent and reliable error management across both synchronous and asynchronous operations. ## Contributing -Contributions are welcome! Please feel free to submit a Pull Request. - -## License - -Copyright 2024 - 2024 the original author or authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +See [CONTRIBUTING.md](../CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests. diff --git a/mcp/src/main/java/org/springframework/ai/mcp/server/McpServer.java b/mcp/src/main/java/org/springframework/ai/mcp/server/McpServer.java index 4ab0bde..cbabff4 100644 --- a/mcp/src/main/java/org/springframework/ai/mcp/server/McpServer.java +++ b/mcp/src/main/java/org/springframework/ai/mcp/server/McpServer.java @@ -155,19 +155,6 @@ public interface McpServer { return this; } - /** - * Set the server implementation information using name and version. - * @param name The server name - * @param version The server version - * @return This builder instance - */ - public Builder serverInfo(String name, String version) { - Assert.notNull(name, "Server name must not be null"); - Assert.notNull(version, "Server version must not be null"); - this.serverInfo = new McpSchema.Implementation(name, version); - return this; - } - /** * Set the server capabilities. * @param serverCapabilities The server capabilities configuration diff --git a/mcp/src/main/java/org/springframework/ai/mcp/server/McpSyncServer.java b/mcp/src/main/java/org/springframework/ai/mcp/server/McpSyncServer.java index df13512..2650c9d 100644 --- a/mcp/src/main/java/org/springframework/ai/mcp/server/McpSyncServer.java +++ b/mcp/src/main/java/org/springframework/ai/mcp/server/McpSyncServer.java @@ -60,27 +60,6 @@ public class McpSyncServer { this.asyncServer.removeTool(toolName).block(); } - /** - * Notify clients that the list of available tools has changed. - */ - public void notifyToolsListChanged() { - this.asyncServer.notifyToolsListChanged().block(); - } - - /** - * Notify clients that the list of available resources has changed. - */ - public void notifyResourcesListChanged() { - this.asyncServer.notifyResourcesListChanged().block(); - } - - /** - * Notify clients that the list of available prompts has changed. - */ - public void notifyPromptsListChanged() { - this.asyncServer.notifyPromptsListChanged().block(); - } - /** * Add a new resource handler. * @param resourceHandler The resource handler to add @@ -113,6 +92,27 @@ public class McpSyncServer { this.asyncServer.removePrompt(promptName).block(); } + /** + * Notify clients that the list of available tools has changed. + */ + public void notifyToolsListChanged() { + this.asyncServer.notifyToolsListChanged().block(); + } + + /** + * Notify clients that the list of available resources has changed. + */ + public void notifyResourcesListChanged() { + this.asyncServer.notifyResourcesListChanged().block(); + } + + /** + * Notify clients that the list of available prompts has changed. + */ + public void notifyPromptsListChanged() { + this.asyncServer.notifyPromptsListChanged().block(); + } + /** * Close the server gracefully. */ diff --git a/mcp/src/test/java/org/springframework/ai/mcp/attic/DemoServer.java b/mcp/src/test/java/org/springframework/ai/mcp/attic/DemoServer.java index d663f14..a389b4b 100644 --- a/mcp/src/test/java/org/springframework/ai/mcp/attic/DemoServer.java +++ b/mcp/src/test/java/org/springframework/ai/mcp/attic/DemoServer.java @@ -43,7 +43,7 @@ public class DemoServer { SseServerTransport transport = new SseServerTransport(new ObjectMapper(), "/mcp/message"); var mcpServer = McpServer.using(transport) - .serverInfo("Weather Forecast", "1.0.0") + .info("Weather Forecast", "1.0.0") .tool(new McpSchema.Tool("weather", "Weather forecast tool by location", Map.of("city", "String")), (arguments) -> { String city = (String) arguments.get("city"); diff --git a/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpAsyncServerTests.java b/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpAsyncServerTests.java index 4f17973..3799a39 100644 --- a/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpAsyncServerTests.java +++ b/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpAsyncServerTests.java @@ -89,7 +89,7 @@ public abstract class AbstractMcpAsyncServerTests { void testAddTool() { Tool newTool = new McpSchema.Tool("new-tool", "New test tool", Map.of("input", "string")); var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .async(); @@ -105,7 +105,7 @@ public abstract class AbstractMcpAsyncServerTests { Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string")); var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .tool(duplicateTool, args -> new CallToolResult(List.of(), false)) .async(); @@ -126,7 +126,7 @@ public abstract class AbstractMcpAsyncServerTests { Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string")); var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .tool(too, args -> new CallToolResult(List.of(), false)) .async(); @@ -139,7 +139,7 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testRemoveNonexistentTool() { var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .async(); @@ -155,7 +155,7 @@ public abstract class AbstractMcpAsyncServerTests { Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string")); var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .tool(too, args -> new CallToolResult(List.of(), false)) .async(); @@ -167,7 +167,7 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testNotifyResourcesListChanged() { - var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async(); + var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async(); StepVerifier.create(mcpAsyncServer.notifyResourcesListChanged()).verifyComplete(); @@ -176,7 +176,7 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testNotifyPromptsListChanged() { - var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async(); + var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async(); StepVerifier.create(mcpAsyncServer.notifyPromptsListChanged()).verifyComplete(); @@ -185,14 +185,14 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testGracefulShutdown() { - var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async(); + var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async(); StepVerifier.create(mcpAsyncServer.closeGracefully()).verifyComplete(); } @Test void testImmediateClose() { - var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async(); + var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async(); assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException(); } @@ -200,7 +200,7 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testAddResource() { var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().resources(true, false).build()) .async(); @@ -218,7 +218,7 @@ public abstract class AbstractMcpAsyncServerTests { void testAddResourceWithNullRegistration() { var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().resources(true, false).build()) .async(); @@ -232,7 +232,7 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testAddPromptWithNullRegistration() { var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().prompts(false).build()) .async(); @@ -245,7 +245,7 @@ public abstract class AbstractMcpAsyncServerTests { void testAddResourceWithoutCapability() { // Create a server without resource capabilities McpAsyncServer serverWithoutResources = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .async(); Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description", @@ -263,7 +263,7 @@ public abstract class AbstractMcpAsyncServerTests { void testAddPromptWithoutCapability() { // Create a server without prompt capabilities McpAsyncServer serverWithoutPrompts = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .async(); Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); @@ -281,7 +281,7 @@ public abstract class AbstractMcpAsyncServerTests { void testRemoveResourceWithoutCapability() { // Create a server without resource capabilities McpAsyncServer serverWithoutResources = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .async(); StepVerifier.create(serverWithoutResources.removeResource(TEST_RESOURCE_URI)).verifyErrorSatisfies(error -> { @@ -294,7 +294,7 @@ public abstract class AbstractMcpAsyncServerTests { void testRemovePromptWithoutCapability() { // Create a server without prompt capabilities McpAsyncServer serverWithoutPrompts = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .async(); StepVerifier.create(serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).verifyErrorSatisfies(error -> { @@ -313,7 +313,7 @@ public abstract class AbstractMcpAsyncServerTests { List.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))); var mcpAsyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().prompts(true).build()) .prompts(registration) .async(); @@ -326,7 +326,7 @@ public abstract class AbstractMcpAsyncServerTests { @Test void testRemoveNonexistentPrompt() { var mcpAsyncServer2 = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().prompts(true).build()) .async(); diff --git a/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpSyncServerTests.java b/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpSyncServerTests.java index fb268ba..15ef810 100644 --- a/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpSyncServerTests.java +++ b/mcp/src/test/java/org/springframework/ai/mcp/server/AbstractMcpSyncServerTests.java @@ -86,7 +86,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testAddTool() { var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .sync(); @@ -103,7 +103,7 @@ public abstract class AbstractMcpSyncServerTests { Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string")); var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .tool(duplicateTool, args -> new CallToolResult(List.of(), false)) .sync(); @@ -121,7 +121,7 @@ public abstract class AbstractMcpSyncServerTests { Tool tool = new McpSchema.Tool(TEST_TOOL_NAME, "Test tool", Map.of("input", "string")); var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .tool(tool, args -> new CallToolResult(List.of(), false)) .sync(); @@ -134,7 +134,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testRemoveNonexistentTool() { var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) .sync(); @@ -146,7 +146,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testNotifyToolsListChanged() { - var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatCode(() -> mcpSyncServer.notifyToolsListChanged()).doesNotThrowAnyException(); @@ -155,7 +155,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testNotifyResourcesListChanged() { - var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatCode(() -> mcpSyncServer.notifyResourcesListChanged()).doesNotThrowAnyException(); @@ -164,7 +164,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testNotifyPromptsListChanged() { - var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatCode(() -> mcpSyncServer.notifyPromptsListChanged()).doesNotThrowAnyException(); @@ -173,14 +173,14 @@ public abstract class AbstractMcpSyncServerTests { @Test void testGracefulShutdown() { - var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); } @Test void testImmediateClose() { - var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatCode(() -> mcpSyncServer.close()).doesNotThrowAnyException(); } @@ -188,7 +188,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testAddResource() { var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().resources(true, false).build()) .sync(); @@ -205,7 +205,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testAddResourceWithNullRegistration() { var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().resources(true, false).build()) .sync(); @@ -218,7 +218,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testAddPromptWithNullRegistration() { var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().prompts(false).build()) .sync(); @@ -228,7 +228,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testAddResourceWithoutCapability() { - var serverWithoutResources = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var serverWithoutResources = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description", null); @@ -241,7 +241,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testAddPromptWithoutCapability() { - var serverWithoutPrompts = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var serverWithoutPrompts = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); PromptRegistration registration = new PromptRegistration(prompt, req -> new GetPromptResult( @@ -254,7 +254,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testRemoveResourceWithoutCapability() { - var serverWithoutResources = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var serverWithoutResources = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatThrownBy(() -> serverWithoutResources.removeResource(TEST_RESOURCE_URI)).isInstanceOf(McpError.class) .hasMessage("Server must be configured with resource capabilities"); @@ -262,7 +262,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testRemovePromptWithoutCapability() { - var serverWithoutPrompts = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var serverWithoutPrompts = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThatThrownBy(() -> serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).isInstanceOf(McpError.class) .hasMessage("Server must be configured with prompt capabilities"); @@ -277,7 +277,7 @@ public abstract class AbstractMcpSyncServerTests { List.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))); var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().prompts(true).build()) .prompts(registration) .sync(); @@ -292,7 +292,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testRemoveNonexistentPrompt() { var mcpSyncServer = McpServer.using(createMcpTransport()) - .serverInfo("test-server", "1.0.0") + .info("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().prompts(true).build()) .sync(); @@ -304,7 +304,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testGetAsyncServer() { - var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync(); + var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync(); assertThat(mcpSyncServer.getAsyncServer()).isNotNull();