diff --git a/MIGRATION-1.1.md b/MIGRATION-1.1.md new file mode 100644 index 000000000..b7bfaff10 --- /dev/null +++ b/MIGRATION-1.1.md @@ -0,0 +1,238 @@ +# MCP Java SDK Migration Guide: 1.0 → 1.1 + +This document covers the breaking changes in **1.1.0** of the MCP Java SDK. + +Most changes are mechanical import updates caused by the decomposition of the `McpSchema` god-class. An [OpenRewrite](https://docs.openrewrite.org/) recipe module (`mcp-migrate`) is provided to automate these changes. See [mcp-migrate/README.md](mcp-migrate/README.md) for usage instructions. + +--- + +## Table of contents + +1. [McpSchema decomposition — nested types moved to dedicated packages](#1-mcpschema-decomposition) +2. [sealed interfaces opened](#2-sealed-interfaces-opened) +3. [JSONRPC_VERSION constant relocated](#3-jsonrpc_version-constant-relocated) +4. [deserializeJsonRpcMessage method relocated](#4-deserializejsonrpcmessage-method-relocated) +5. [Automated migration with OpenRewrite](#5-automated-migration-with-openrewrite) +6. [Manual migration reference table](#6-manual-migration-reference-table) + +--- + +## 1. McpSchema decomposition + +The `McpSchema` class previously held every domain type in the MCP specification as a nested class or record. In 1.1 those types are top-level classes organised in feature-specific sub-packages. + +**Before (pre-1.1):** +```java +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.McpSchema.CallToolResult; + +Tool tool = Tool.builder().name("my-tool").description("...").build(); +``` + +**After (1.1):** +```java +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; + +Tool tool = Tool.builder().name("my-tool").description("...").build(); +``` + +The new package hierarchy is: + +``` +io.modelcontextprotocol.spec +├── jsonrpc/ JSON-RPC 2.0 protocol types +└── schema/ + ├── resource/ Resource, template, read/subscribe requests & results + ├── prompt/ Prompt definitions and get/list requests & results + ├── tool/ Tool definition, call requests & results + ├── sample/ LLM sampling messages, preferences, and create request/result + └── elicit/ Elicitation request and result +``` + +### Types that remain in McpSchema + +The following types were **not** moved and are still accessed as `McpSchema.*`: + +| Type | Purpose | +|---|---| +| `Meta` | Base interface for `_meta` fields | +| `Request` | Marker interface for request objects | +| `Result` | Marker interface for result objects | +| `Notification` | Marker interface for notification objects | +| `ErrorCodes` | Standard JSON-RPC error code constants | +| `InitializeRequest` / `InitializeResult` | Lifecycle handshake | +| `ClientCapabilities` / `ServerCapabilities` | Capability negotiation | +| `Implementation` | Server/client implementation descriptor | +| `Role` | Enum: `USER` / `ASSISTANT` | +| `Annotations` | Optional client annotations | +| `Content` / `TextContent` / `ImageContent` / `AudioContent` | Content union type and variants | +| `EmbeddedResource` / `ResourceLink` | Embedded resource in content | +| `PaginatedRequest` / `PaginatedResult` | Cursor-based pagination | +| `ProgressNotification` / `ResourcesUpdatedNotification` / `LoggingMessageNotification` | Server-sent notifications | +| `LoggingLevel` / `SetLevelRequest` | Logging control | +| `CompleteReference` / `PromptReference` / `ResourceReference` / `CompleteRequest` / `CompleteResult` | Argument completion | +| `Root` / `ListRootsResult` | Client roots | + +--- + +## 2. sealed interfaces opened + +`Request`, `Result`, `Notification`, and `CompleteReference` were previously `sealed` with an explicit `permits` list. They are now plain interfaces. + +**Before:** +```java +public sealed interface Request extends Meta + permits InitializeRequest, CallToolRequest, CreateMessageRequest, ... { } +``` + +**After:** +```java +public interface Request extends Meta { } +``` + +**Impact on user code:** Code that used exhaustive `switch` or `instanceof` chains relying on the sealed hierarchy will no longer receive a compile-time guarantee that all cases are covered. Review any such patterns and add a default/fallback case if needed. + +--- + +## 3. JSONRPC_VERSION constant relocated + +The version string constant `"2.0"` moved from `McpSchema` to the new `JSONRPC` utility class. + +**Before:** +```java +import io.modelcontextprotocol.spec.McpSchema; + +String version = McpSchema.JSONRPC_VERSION; +``` + +**After:** +```java +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; + +String version = JSONRPC.JSONRPC_VERSION; +``` + +--- + +## 4. deserializeJsonRpcMessage method relocated + +The static helper for deserializing raw JSON into a `JSONRPCMessage` moved from `McpSchema` to the `JSONRPC` utility class. + +**Before:** +```java +JSONRPCMessage msg = McpSchema.deserializeJsonRpcMessage(jsonMapper, rawJson); +``` + +**After:** +```java +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; + +JSONRPCMessage msg = JSONRPC.deserializeJsonRpcMessage(jsonMapper, rawJson); +``` + +--- + +## 5. Automated migration with OpenRewrite + +The `mcp-migrate` artifact ships OpenRewrite recipes that automate all of the import and type-reference changes described in this document. + +**Maven:** +```bash +mvn org.openrewrite.maven:rewrite-maven-plugin:run \ + -Drewrite.recipeArtifactCoordinates=io.modelcontextprotocol.sdk:mcp-migrate:1.1.0 \ + -Drewrite.activeRecipes=io.modelcontextprotocol.sdk.migrations.McpSchemaMigration +``` + +**Gradle:** +```kotlin +rewrite { + activeRecipe("io.modelcontextprotocol.sdk.migrations.McpSchemaMigration") + setRecipeArtifactCoordinates("io.modelcontextprotocol.sdk:mcp-migrate:1.1.0") +} +``` + +See [mcp-migrate/README.md](mcp-migrate/README.md) for full setup instructions, dry-run mode, and per-domain sub-recipes. + +--- + +## 6. Manual migration reference table + +Use this table if you prefer to update imports by hand or if OpenRewrite cannot resolve a reference (e.g. inside generated sources). + +### JSON-RPC types → `io.modelcontextprotocol.spec.jsonrpc` + +| Old (`McpSchema.*`) | New fully-qualified name | +|---|---| +| `McpSchema.JSONRPCMessage` | `io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage` | +| `McpSchema.JSONRPCRequest` | `io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest` | +| `McpSchema.JSONRPCNotification` | `io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification` | +| `McpSchema.JSONRPCResponse` | `io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse` | +| `McpSchema.JSONRPCResponse.JSONRPCError` | `io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse.JSONRPCError` | + +### Resource types → `io.modelcontextprotocol.spec.schema.resource` + +| Old (`McpSchema.*`) | New fully-qualified name | +|---|---| +| `McpSchema.Annotated` | `io.modelcontextprotocol.spec.schema.resource.Annotated` | +| `McpSchema.Identifier` | `io.modelcontextprotocol.spec.schema.resource.Identifier` | +| `McpSchema.ResourceContent` | `io.modelcontextprotocol.spec.schema.resource.ResourceContent` | +| `McpSchema.ResourceContents` | `io.modelcontextprotocol.spec.schema.resource.ResourceContents` | +| `McpSchema.TextResourceContents` | `io.modelcontextprotocol.spec.schema.resource.TextResourceContents` | +| `McpSchema.BlobResourceContents` | `io.modelcontextprotocol.spec.schema.resource.BlobResourceContents` | +| `McpSchema.Resource` | `io.modelcontextprotocol.spec.schema.resource.Resource` | +| `McpSchema.ResourceTemplate` | `io.modelcontextprotocol.spec.schema.resource.ResourceTemplate` | +| `McpSchema.ListResourcesResult` | `io.modelcontextprotocol.spec.schema.resource.ListResourcesResult` | +| `McpSchema.ListResourceTemplatesResult` | `io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult` | +| `McpSchema.ReadResourceRequest` | `io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest` | +| `McpSchema.ReadResourceResult` | `io.modelcontextprotocol.spec.schema.resource.ReadResourceResult` | +| `McpSchema.SubscribeRequest` | `io.modelcontextprotocol.spec.schema.resource.SubscribeRequest` | +| `McpSchema.UnsubscribeRequest` | `io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest` | + +### Prompt types → `io.modelcontextprotocol.spec.schema.prompt` + +| Old (`McpSchema.*`) | New fully-qualified name | +|---|---| +| `McpSchema.Prompt` | `io.modelcontextprotocol.spec.schema.prompt.Prompt` | +| `McpSchema.PromptArgument` | `io.modelcontextprotocol.spec.schema.prompt.PromptArgument` | +| `McpSchema.PromptMessage` | `io.modelcontextprotocol.spec.schema.prompt.PromptMessage` | +| `McpSchema.ListPromptsResult` | `io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult` | +| `McpSchema.GetPromptRequest` | `io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest` | +| `McpSchema.GetPromptResult` | `io.modelcontextprotocol.spec.schema.prompt.GetPromptResult` | + +### Tool types → `io.modelcontextprotocol.spec.schema.tool` + +| Old (`McpSchema.*`) | New fully-qualified name | +|---|---| +| `McpSchema.JsonSchema` | `io.modelcontextprotocol.spec.schema.tool.JsonSchema` | +| `McpSchema.ToolAnnotations` | `io.modelcontextprotocol.spec.schema.tool.ToolAnnotations` | +| `McpSchema.Tool` | `io.modelcontextprotocol.spec.schema.tool.Tool` | +| `McpSchema.ListToolsResult` | `io.modelcontextprotocol.spec.schema.tool.ListToolsResult` | +| `McpSchema.CallToolRequest` | `io.modelcontextprotocol.spec.schema.tool.CallToolRequest` | +| `McpSchema.CallToolResult` | `io.modelcontextprotocol.spec.schema.tool.CallToolResult` | + +### Sampling types → `io.modelcontextprotocol.spec.schema.sample` + +| Old (`McpSchema.*`) | New fully-qualified name | +|---|---| +| `McpSchema.SamplingMessage` | `io.modelcontextprotocol.spec.schema.sample.SamplingMessage` | +| `McpSchema.ModelPreferences` | `io.modelcontextprotocol.spec.schema.sample.ModelPreferences` | +| `McpSchema.ModelHint` | `io.modelcontextprotocol.spec.schema.sample.ModelHint` | +| `McpSchema.CreateMessageRequest` | `io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest` | +| `McpSchema.CreateMessageRequest.ContextInclusionStrategy` | `io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest.ContextInclusionStrategy` | +| `McpSchema.CreateMessageResult` | `io.modelcontextprotocol.spec.schema.sample.CreateMessageResult` | + +### Elicitation types → `io.modelcontextprotocol.spec.schema.elicit` + +| Old (`McpSchema.*`) | New fully-qualified name | +|---|---| +| `McpSchema.ElicitRequest` | `io.modelcontextprotocol.spec.schema.elicit.ElicitRequest` | +| `McpSchema.ElicitResult` | `io.modelcontextprotocol.spec.schema.elicit.ElicitResult` | + +### Static members → `io.modelcontextprotocol.spec.jsonrpc.JSONRPC` + +| Old | New | +|---|---| +| `McpSchema.JSONRPC_VERSION` | `JSONRPC.JSONRPC_VERSION` | +| `McpSchema.deserializeJsonRpcMessage(mapper, json)` | `JSONRPC.deserializeJsonRpcMessage(mapper, json)` | diff --git a/conformance-tests/client-jdk-http-client/src/main/java/io/modelcontextprotocol/conformance/client/ConformanceJdkClientMcpClient.java b/conformance-tests/client-jdk-http-client/src/main/java/io/modelcontextprotocol/conformance/client/ConformanceJdkClientMcpClient.java index 570c4614e..09dba5471 100644 --- a/conformance-tests/client-jdk-http-client/src/main/java/io/modelcontextprotocol/conformance/client/ConformanceJdkClientMcpClient.java +++ b/conformance-tests/client-jdk-http-client/src/main/java/io/modelcontextprotocol/conformance/client/ConformanceJdkClientMcpClient.java @@ -6,6 +6,11 @@ import io.modelcontextprotocol.client.McpSyncClient; import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport; import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; /** * MCP Conformance Test Client - JDK HTTP Client Implementation @@ -120,7 +125,7 @@ private static McpSyncClient createClientWithElicitation(String serverUrl) { } // Return accept action with the defaults applied - return new McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, content, null); + return new ElicitResult(ElicitResult.Action.ACCEPT, content, null); }) .build(); } @@ -161,20 +166,19 @@ private static void runToolsCallScenario(String serverUrl) throws Exception { System.out.println("Successfully connected to MCP server"); // List available tools - McpSchema.ListToolsResult toolsResult = client.listTools(); + ListToolsResult toolsResult = client.listTools(); System.out.println("Successfully listed tools"); // Call the add_numbers tool if it exists if (toolsResult != null && toolsResult.tools() != null) { - for (McpSchema.Tool tool : toolsResult.tools()) { + for (Tool tool : toolsResult.tools()) { if ("add_numbers".equals(tool.name())) { // Call the add_numbers tool with test arguments var arguments = new java.util.HashMap(); arguments.put("a", 5); arguments.put("b", 3); - McpSchema.CallToolResult result = client - .callTool(new McpSchema.CallToolRequest("add_numbers", arguments)); + CallToolResult result = client.callTool(new CallToolRequest("add_numbers", arguments)); System.out.println("Successfully called add_numbers tool"); if (result != null && result.content() != null) { @@ -208,18 +212,18 @@ private static void runElicitationDefaultsScenario(String serverUrl) throws Exce System.out.println("Successfully connected to MCP server"); // List available tools - McpSchema.ListToolsResult toolsResult = client.listTools(); + ListToolsResult toolsResult = client.listTools(); System.out.println("Successfully listed tools"); // Call the test_client_elicitation_defaults tool if it exists if (toolsResult != null && toolsResult.tools() != null) { - for (McpSchema.Tool tool : toolsResult.tools()) { + for (Tool tool : toolsResult.tools()) { if ("test_client_elicitation_defaults".equals(tool.name())) { // Call the tool which will trigger an elicitation request var arguments = new java.util.HashMap(); - McpSchema.CallToolResult result = client - .callTool(new McpSchema.CallToolRequest("test_client_elicitation_defaults", arguments)); + CallToolResult result = client + .callTool(new CallToolRequest("test_client_elicitation_defaults", arguments)); System.out.println("Successfully called test_client_elicitation_defaults tool"); if (result != null && result.content() != null) { @@ -253,19 +257,18 @@ private static void runSSERetryScenario(String serverUrl) throws Exception { System.out.println("Successfully connected to MCP server"); // List available tools - McpSchema.ListToolsResult toolsResult = client.listTools(); + ListToolsResult toolsResult = client.listTools(); System.out.println("Successfully listed tools"); // Call the test_reconnection tool if it exists if (toolsResult != null && toolsResult.tools() != null) { - for (McpSchema.Tool tool : toolsResult.tools()) { + for (Tool tool : toolsResult.tools()) { if ("test_reconnection".equals(tool.name())) { // Call the tool which will trigger SSE stream closure and // reconnection var arguments = new java.util.HashMap(); - McpSchema.CallToolResult result = client - .callTool(new McpSchema.CallToolRequest("test_reconnection", arguments)); + CallToolResult result = client.callTool(new CallToolRequest("test_reconnection", arguments)); System.out.println("Successfully called test_reconnection tool"); if (result != null && result.content() != null) { diff --git a/conformance-tests/server-servlet/src/main/java/io/modelcontextprotocol/conformance/server/ConformanceServlet.java b/conformance-tests/server-servlet/src/main/java/io/modelcontextprotocol/conformance/server/ConformanceServlet.java index 3d162a5de..0b530bce8 100644 --- a/conformance-tests/server-servlet/src/main/java/io/modelcontextprotocol/conformance/server/ConformanceServlet.java +++ b/conformance-tests/server-servlet/src/main/java/io/modelcontextprotocol/conformance/server/ConformanceServlet.java @@ -10,33 +10,33 @@ import io.modelcontextprotocol.server.transport.DefaultServerTransportSecurityValidator; import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema.AudioContent; -import io.modelcontextprotocol.spec.McpSchema.BlobResourceContents; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteResult; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; import io.modelcontextprotocol.spec.McpSchema.EmbeddedResource; -import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; import io.modelcontextprotocol.spec.McpSchema.ImageContent; -import io.modelcontextprotocol.spec.McpSchema.JsonSchema; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; import io.modelcontextprotocol.spec.McpSchema.ProgressNotification; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.PromptArgument; -import io.modelcontextprotocol.spec.McpSchema.PromptMessage; import io.modelcontextprotocol.spec.McpSchema.PromptReference; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; -import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate; import io.modelcontextprotocol.spec.McpSchema.Role; -import io.modelcontextprotocol.spec.McpSchema.SamplingMessage; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; -import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptArgument; +import io.modelcontextprotocol.spec.schema.prompt.PromptMessage; +import io.modelcontextprotocol.spec.schema.resource.BlobResourceContents; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; +import io.modelcontextprotocol.spec.schema.resource.TextResourceContents; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.sample.SamplingMessage; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.JsonSchema; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; import org.apache.catalina.startup.Tomcat; diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java index 93fcc332a..6adf2201d 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java @@ -24,18 +24,31 @@ import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; -import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest; -import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; import io.modelcontextprotocol.util.ToolNameValidator; -import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; import io.modelcontextprotocol.spec.McpSchema.PaginatedRequest; import io.modelcontextprotocol.spec.McpSchema.Root; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.Utils; import org.slf4j.Logger; @@ -234,7 +247,7 @@ public class McpAsyncClient { Map notificationHandlers = new HashMap<>(); // Tools Change Notification - List, Mono>> toolsChangeConsumersFinal = new ArrayList<>(); + List, Mono>> toolsChangeConsumersFinal = new ArrayList<>(); toolsChangeConsumersFinal .add((notification) -> Mono.fromRunnable(() -> logger.debug("Tools changed: {}", notification))); @@ -245,7 +258,7 @@ public class McpAsyncClient { asyncToolsChangeNotificationHandler(toolsChangeConsumersFinal)); // Resources Change Notification - List, Mono>> resourcesChangeConsumersFinal = new ArrayList<>(); + List, Mono>> resourcesChangeConsumersFinal = new ArrayList<>(); resourcesChangeConsumersFinal .add((notification) -> Mono.fromRunnable(() -> logger.debug("Resources changed: {}", notification))); @@ -257,7 +270,7 @@ public class McpAsyncClient { asyncResourcesChangeNotificationHandler(resourcesChangeConsumersFinal)); // Resources Update Notification - List, Mono>> resourcesUpdateConsumersFinal = new ArrayList<>(); + List, Mono>> resourcesUpdateConsumersFinal = new ArrayList<>(); resourcesUpdateConsumersFinal .add((notification) -> Mono.fromRunnable(() -> logger.debug("Resources updated: {}", notification))); @@ -269,7 +282,7 @@ public class McpAsyncClient { asyncResourcesUpdatedNotificationHandler(resourcesUpdateConsumersFinal)); // Prompts Change Notification - List, Mono>> promptsChangeConsumersFinal = new ArrayList<>(); + List, Mono>> promptsChangeConsumersFinal = new ArrayList<>(); promptsChangeConsumersFinal .add((notification) -> Mono.fromRunnable(() -> logger.debug("Prompts changed: {}", notification))); if (!Utils.isEmpty(features.promptsChangeConsumers())) { @@ -547,7 +560,7 @@ private RequestHandler rootsListRequestHandler() { // -------------------------- private RequestHandler samplingCreateMessageHandler() { return params -> { - McpSchema.CreateMessageRequest request = transport.unmarshalFrom(params, CREATE_MESSAGE_REQUEST_TYPE_REF); + CreateMessageRequest request = transport.unmarshalFrom(params, CREATE_MESSAGE_REQUEST_TYPE_REF); return this.samplingHandler.apply(request); }; @@ -568,10 +581,10 @@ private RequestHandler elicitationCreateHandler() { // -------------------------- // Tools // -------------------------- - private static final TypeRef CALL_TOOL_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef CALL_TOOL_RESULT_TYPE_REF = new TypeRef<>() { }; - private static final TypeRef LIST_TOOLS_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef LIST_TOOLS_RESULT_TYPE_REF = new TypeRef<>() { }; /** @@ -581,11 +594,11 @@ private RequestHandler elicitationCreateHandler() { * @param callToolRequest The request containing the tool name and input parameters. * @return A Mono that emits the result of the tool call, including the output and any * errors. - * @see McpSchema.CallToolRequest - * @see McpSchema.CallToolResult + * @see CallToolRequest + * @see CallToolResult * @see #listTools() */ - public Mono callTool(McpSchema.CallToolRequest callToolRequest) { + public Mono callTool(CallToolRequest callToolRequest) { return this.initializer.withInitialization("calling tool", init -> { if (init.initializeResult().capabilities().tools() == null) { return Mono.error(new IllegalStateException("Server does not provide tools capability")); @@ -597,7 +610,7 @@ public Mono callTool(McpSchema.CallToolRequest callToo }); } - private McpSchema.CallToolResult validateToolResult(String toolName, McpSchema.CallToolResult result) { + private CallToolResult validateToolResult(String toolName, CallToolResult result) { if (!this.enableCallToolSchemaCaching || result == null || result.isError() == Boolean.TRUE) { // if tool schema caching is disabled or tool call resulted in an error - skip @@ -629,14 +642,14 @@ private McpSchema.CallToolResult validateToolResult(String toolName, McpSchema.C * Retrieves the list of all tools provided by the server. * @return A Mono that emits the list of all tools result */ - public Mono listTools() { + public Mono listTools() { return this.listTools(McpSchema.FIRST_PAGE).expand(result -> { String next = result.nextCursor(); return (next != null && !next.isEmpty()) ? this.listTools(next) : Mono.empty(); - }).reduce(new McpSchema.ListToolsResult(new ArrayList<>(), null), (allToolsResult, result) -> { + }).reduce(new ListToolsResult(new ArrayList<>(), null), (allToolsResult, result) -> { allToolsResult.tools().addAll(result.tools()); return allToolsResult; - }).map(result -> new McpSchema.ListToolsResult(Collections.unmodifiableList(result.tools()), null)); + }).map(result -> new ListToolsResult(Collections.unmodifiableList(result.tools()), null)); } /** @@ -644,11 +657,11 @@ public Mono listTools() { * @param cursor Optional pagination cursor from a previous list request * @return A Mono that emits the list of tools result */ - public Mono listTools(String cursor) { + public Mono listTools(String cursor) { return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor)); } - private Mono listToolsInternal(Initialization init, String cursor) { + private Mono listToolsInternal(Initialization init, String cursor) { if (init.initializeResult().capabilities().tools() == null) { return Mono.error(new IllegalStateException("Server does not provide tools capability")); @@ -672,7 +685,7 @@ private Mono listToolsInternal(Initialization init, S } private NotificationHandler asyncToolsChangeNotificationHandler( - List, Mono>> toolsChangeConsumers) { + List, Mono>> toolsChangeConsumers) { // TODO: params are not used yet return params -> this.listTools() .flatMap(listToolsResult -> Flux.fromIterable(toolsChangeConsumers) @@ -688,13 +701,13 @@ private NotificationHandler asyncToolsChangeNotificationHandler( // Resources // -------------------------- - private static final TypeRef LIST_RESOURCES_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef LIST_RESOURCES_RESULT_TYPE_REF = new TypeRef<>() { }; - private static final TypeRef READ_RESOURCE_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef READ_RESOURCE_RESULT_TYPE_REF = new TypeRef<>() { }; - private static final TypeRef LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeRef<>() { }; /** @@ -702,17 +715,17 @@ private NotificationHandler asyncToolsChangeNotificationHandler( * kind of UTF-8 encoded data that an MCP server makes available to clients, such as * database records, API responses, log files, and more. * @return A Mono that completes with the list of all resources result - * @see McpSchema.ListResourcesResult + * @see ListResourcesResult * @see #readResource(McpSchema.Resource) */ - public Mono listResources() { + public Mono listResources() { return this.listResources(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listResources(result.nextCursor()) : Mono.empty()) - .reduce(new McpSchema.ListResourcesResult(new ArrayList<>(), null), (allResourcesResult, result) -> { + .reduce(new ListResourcesResult(new ArrayList<>(), null), (allResourcesResult, result) -> { allResourcesResult.resources().addAll(result.resources()); return allResourcesResult; }) - .map(result -> new McpSchema.ListResourcesResult(Collections.unmodifiableList(result.resources()), null)); + .map(result -> new ListResourcesResult(Collections.unmodifiableList(result.resources()), null)); } /** @@ -721,10 +734,10 @@ public Mono listResources() { * as database records, API responses, log files, and more. * @param cursor Optional pagination cursor from a previous list request. * @return A Mono that completes with the list of resources result. - * @see McpSchema.ListResourcesResult + * @see ListResourcesResult * @see #readResource(McpSchema.Resource) */ - public Mono listResources(String cursor) { + public Mono listResources(String cursor) { return this.initializer.withInitialization("listing resources", init -> { if (init.initializeResult().capabilities().resources() == null) { return Mono.error(new IllegalStateException("Server does not provide the resources capability")); @@ -741,11 +754,11 @@ public Mono listResources(String cursor) { * @param resource The resource to read, containing the URI that identifies the * resource. * @return A Mono that completes with the resource content. - * @see McpSchema.Resource - * @see McpSchema.ReadResourceResult + * @see Resource + * @see ReadResourceResult */ - public Mono readResource(McpSchema.Resource resource) { - return this.readResource(new McpSchema.ReadResourceRequest(resource.uri())); + public Mono readResource(Resource resource) { + return this.readResource(new ReadResourceRequest(resource.uri())); } /** @@ -753,10 +766,10 @@ public Mono readResource(McpSchema.Resource resour * method fetches the actual data that the resource represents. * @param readResourceRequest The request containing the URI of the resource to read * @return A Mono that completes with the resource content. - * @see McpSchema.ReadResourceRequest - * @see McpSchema.ReadResourceResult + * @see ReadResourceRequest + * @see ReadResourceResult */ - public Mono readResource(McpSchema.ReadResourceRequest readResourceRequest) { + public Mono readResource(ReadResourceRequest readResourceRequest) { return this.initializer.withInitialization("reading resources", init -> { if (init.initializeResult().capabilities().resources() == null) { return Mono.error(new IllegalStateException("Server does not provide the resources capability")); @@ -771,19 +784,18 @@ public Mono readResource(McpSchema.ReadResourceReq * templates allow servers to expose parameterized resources using URI templates, * enabling dynamic resource access based on variable parameters. * @return A Mono that completes with the list of all resource templates result - * @see McpSchema.ListResourceTemplatesResult + * @see ListResourceTemplatesResult */ - public Mono listResourceTemplates() { + public Mono listResourceTemplates() { return this.listResourceTemplates(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listResourceTemplates(result.nextCursor()) : Mono.empty()) - .reduce(new McpSchema.ListResourceTemplatesResult(new ArrayList<>(), null), - (allResourceTemplatesResult, result) -> { - allResourceTemplatesResult.resourceTemplates().addAll(result.resourceTemplates()); - return allResourceTemplatesResult; - }) - .map(result -> new McpSchema.ListResourceTemplatesResult( - Collections.unmodifiableList(result.resourceTemplates()), null)); + .reduce(new ListResourceTemplatesResult(new ArrayList<>(), null), (allResourceTemplatesResult, result) -> { + allResourceTemplatesResult.resourceTemplates().addAll(result.resourceTemplates()); + return allResourceTemplatesResult; + }) + .map(result -> new ListResourceTemplatesResult(Collections.unmodifiableList(result.resourceTemplates()), + null)); } /** @@ -792,9 +804,9 @@ public Mono listResourceTemplates() { * enabling dynamic resource access based on variable parameters. * @param cursor Optional pagination cursor from a previous list request. * @return A Mono that completes with the list of resource templates result. - * @see McpSchema.ListResourceTemplatesResult + * @see ListResourceTemplatesResult */ - public Mono listResourceTemplates(String cursor) { + public Mono listResourceTemplates(String cursor) { return this.initializer.withInitialization("listing resource templates", init -> { if (init.initializeResult().capabilities().resources() == null) { return Mono.error(new IllegalStateException("Server does not provide the resources capability")); @@ -811,10 +823,10 @@ public Mono listResourceTemplates(String * notification handler. * @param subscribeRequest The subscribe request containing the URI of the resource. * @return A Mono that completes when the subscription is complete. - * @see McpSchema.SubscribeRequest + * @see SubscribeRequest * @see #unsubscribeResource(McpSchema.UnsubscribeRequest) */ - public Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { + public Mono subscribeResource(SubscribeRequest subscribeRequest) { return this.initializer.withInitialization("subscribing to resources", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_RESOURCES_SUBSCRIBE, subscribeRequest, VOID_TYPE_REFERENCE)); } @@ -825,16 +837,16 @@ public Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest) * @param unsubscribeRequest The unsubscribe request containing the URI of the * resource. * @return A Mono that completes when the unsubscription is complete. - * @see McpSchema.UnsubscribeRequest + * @see UnsubscribeRequest * @see #subscribeResource(McpSchema.SubscribeRequest) */ - public Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { + public Mono unsubscribeResource(UnsubscribeRequest unsubscribeRequest) { return this.initializer.withInitialization("unsubscribing from resources", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_RESOURCES_UNSUBSCRIBE, unsubscribeRequest, VOID_TYPE_REFERENCE)); } private NotificationHandler asyncResourcesChangeNotificationHandler( - List, Mono>> resourcesChangeConsumers) { + List, Mono>> resourcesChangeConsumers) { return params -> listResources().flatMap(listResourcesResult -> Flux.fromIterable(resourcesChangeConsumers) .flatMap(consumer -> consumer.apply(listResourcesResult.resources())) .onErrorResume(error -> { @@ -845,13 +857,13 @@ private NotificationHandler asyncResourcesChangeNotificationHandler( } private NotificationHandler asyncResourcesUpdatedNotificationHandler( - List, Mono>> resourcesUpdateConsumers) { + List, Mono>> resourcesUpdateConsumers) { return params -> { McpSchema.ResourcesUpdatedNotification resourcesUpdatedNotification = transport.unmarshalFrom(params, new TypeRef<>() { }); - return readResource(new McpSchema.ReadResourceRequest(resourcesUpdatedNotification.uri())) + return readResource(new ReadResourceRequest(resourcesUpdatedNotification.uri())) .flatMap(readResourceResult -> Flux.fromIterable(resourcesUpdateConsumers) .flatMap(consumer -> consumer.apply(readResourceResult.contents())) .onErrorResume(error -> { @@ -865,16 +877,16 @@ private NotificationHandler asyncResourcesUpdatedNotificationHandler( // -------------------------- // Prompts // -------------------------- - private static final TypeRef LIST_PROMPTS_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef LIST_PROMPTS_RESULT_TYPE_REF = new TypeRef<>() { }; - private static final TypeRef GET_PROMPT_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef GET_PROMPT_RESULT_TYPE_REF = new TypeRef<>() { }; /** * Retrieves the list of all prompts provided by the server. * @return A Mono that completes with the list of all prompts result. - * @see McpSchema.ListPromptsResult + * @see ListPromptsResult * @see #getPrompt(GetPromptRequest) */ public Mono listPrompts() { @@ -884,14 +896,14 @@ public Mono listPrompts() { allPromptsResult.prompts().addAll(result.prompts()); return allPromptsResult; }) - .map(result -> new McpSchema.ListPromptsResult(Collections.unmodifiableList(result.prompts()), null)); + .map(result -> new ListPromptsResult(Collections.unmodifiableList(result.prompts()), null)); } /** * Retrieves a paginated list of prompts provided by the server. * @param cursor Optional pagination cursor from a previous list request * @return A Mono that completes with the list of prompts result. - * @see McpSchema.ListPromptsResult + * @see ListPromptsResult * @see #getPrompt(GetPromptRequest) */ public Mono listPrompts(String cursor) { @@ -904,8 +916,8 @@ public Mono listPrompts(String cursor) { * including all parameters and instructions for generating AI content. * @param getPromptRequest The request containing the ID of the prompt to retrieve. * @return A Mono that completes with the prompt result. - * @see McpSchema.GetPromptRequest - * @see McpSchema.GetPromptResult + * @see GetPromptRequest + * @see GetPromptResult * @see #listPrompts() */ public Mono getPrompt(GetPromptRequest getPromptRequest) { @@ -914,7 +926,7 @@ public Mono getPrompt(GetPromptRequest getPromptRequest) { } private NotificationHandler asyncPromptsChangeNotificationHandler( - List, Mono>> promptsChangeConsumers) { + List, Mono>> promptsChangeConsumers) { return params -> listPrompts().flatMap(listPromptsResult -> Flux.fromIterable(promptsChangeConsumers) .flatMap(consumer -> consumer.apply(listPromptsResult.prompts())) .onErrorResume(error -> { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java index 12f34e60a..b913ebb9c 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java @@ -10,13 +10,17 @@ import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; import io.modelcontextprotocol.spec.McpSchema.Implementation; import io.modelcontextprotocol.spec.McpSchema.Root; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; import io.modelcontextprotocol.spec.McpTransport; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.Assert; import reactor.core.publisher.Mono; @@ -173,13 +177,13 @@ class SyncSpec { private final Map roots = new HashMap<>(); - private final List>> toolsChangeConsumers = new ArrayList<>(); + private final List>> toolsChangeConsumers = new ArrayList<>(); - private final List>> resourcesChangeConsumers = new ArrayList<>(); + private final List>> resourcesChangeConsumers = new ArrayList<>(); - private final List>> resourcesUpdateConsumers = new ArrayList<>(); + private final List>> resourcesUpdateConsumers = new ArrayList<>(); - private final List>> promptsChangeConsumers = new ArrayList<>(); + private final List>> promptsChangeConsumers = new ArrayList<>(); private final List> loggingConsumers = new ArrayList<>(); @@ -327,7 +331,7 @@ public SyncSpec elicitation(Function elicitationHan * @return This builder instance for method chaining * @throws IllegalArgumentException if toolsChangeConsumer is null */ - public SyncSpec toolsChangeConsumer(Consumer> toolsChangeConsumer) { + public SyncSpec toolsChangeConsumer(Consumer> toolsChangeConsumer) { Assert.notNull(toolsChangeConsumer, "Tools change consumer must not be null"); this.toolsChangeConsumers.add(toolsChangeConsumer); return this; @@ -342,7 +346,7 @@ public SyncSpec toolsChangeConsumer(Consumer> toolsChangeCo * @return This builder instance for method chaining * @throws IllegalArgumentException if resourcesChangeConsumer is null */ - public SyncSpec resourcesChangeConsumer(Consumer> resourcesChangeConsumer) { + public SyncSpec resourcesChangeConsumer(Consumer> resourcesChangeConsumer) { Assert.notNull(resourcesChangeConsumer, "Resources change consumer must not be null"); this.resourcesChangeConsumers.add(resourcesChangeConsumer); return this; @@ -358,7 +362,7 @@ public SyncSpec resourcesChangeConsumer(Consumer> resou * @return This builder instance for method chaining. * @throws IllegalArgumentException If the resourcesUpdateConsumer is null. */ - public SyncSpec resourcesUpdateConsumer(Consumer> resourcesUpdateConsumer) { + public SyncSpec resourcesUpdateConsumer(Consumer> resourcesUpdateConsumer) { Assert.notNull(resourcesUpdateConsumer, "Resources update consumer must not be null"); this.resourcesUpdateConsumers.add(resourcesUpdateConsumer); return this; @@ -373,7 +377,7 @@ public SyncSpec resourcesUpdateConsumer(Consumer> promptsChangeConsumer) { + public SyncSpec promptsChangeConsumer(Consumer> promptsChangeConsumer) { Assert.notNull(promptsChangeConsumer, "Prompts change consumer must not be null"); this.promptsChangeConsumers.add(promptsChangeConsumer); return this; @@ -529,13 +533,13 @@ class AsyncSpec { private final Map roots = new HashMap<>(); - private final List, Mono>> toolsChangeConsumers = new ArrayList<>(); + private final List, Mono>> toolsChangeConsumers = new ArrayList<>(); - private final List, Mono>> resourcesChangeConsumers = new ArrayList<>(); + private final List, Mono>> resourcesChangeConsumers = new ArrayList<>(); - private final List, Mono>> resourcesUpdateConsumers = new ArrayList<>(); + private final List, Mono>> resourcesUpdateConsumers = new ArrayList<>(); - private final List, Mono>> promptsChangeConsumers = new ArrayList<>(); + private final List, Mono>> promptsChangeConsumers = new ArrayList<>(); private final List>> loggingConsumers = new ArrayList<>(); @@ -681,7 +685,7 @@ public AsyncSpec elicitation(Function> elicita * @return This builder instance for method chaining * @throws IllegalArgumentException if toolsChangeConsumer is null */ - public AsyncSpec toolsChangeConsumer(Function, Mono> toolsChangeConsumer) { + public AsyncSpec toolsChangeConsumer(Function, Mono> toolsChangeConsumer) { Assert.notNull(toolsChangeConsumer, "Tools change consumer must not be null"); this.toolsChangeConsumers.add(toolsChangeConsumer); return this; @@ -696,8 +700,7 @@ public AsyncSpec toolsChangeConsumer(Function, Mono> * @return This builder instance for method chaining * @throws IllegalArgumentException if resourcesChangeConsumer is null */ - public AsyncSpec resourcesChangeConsumer( - Function, Mono> resourcesChangeConsumer) { + public AsyncSpec resourcesChangeConsumer(Function, Mono> resourcesChangeConsumer) { Assert.notNull(resourcesChangeConsumer, "Resources change consumer must not be null"); this.resourcesChangeConsumers.add(resourcesChangeConsumer); return this; @@ -713,8 +716,7 @@ public AsyncSpec resourcesChangeConsumer( * @return This builder instance for method chaining. * @throws IllegalArgumentException If the resourcesUpdateConsumer is null. */ - public AsyncSpec resourcesUpdateConsumer( - Function, Mono> resourcesUpdateConsumer) { + public AsyncSpec resourcesUpdateConsumer(Function, Mono> resourcesUpdateConsumer) { Assert.notNull(resourcesUpdateConsumer, "Resources update consumer must not be null"); this.resourcesUpdateConsumers.add(resourcesUpdateConsumer); return this; @@ -729,7 +731,7 @@ public AsyncSpec resourcesUpdateConsumer( * @return This builder instance for method chaining * @throws IllegalArgumentException if promptsChangeConsumer is null */ - public AsyncSpec promptsChangeConsumer(Function, Mono> promptsChangeConsumer) { + public AsyncSpec promptsChangeConsumer(Function, Mono> promptsChangeConsumer) { Assert.notNull(promptsChangeConsumer, "Prompts change consumer must not be null"); this.promptsChangeConsumers.add(promptsChangeConsumer); return this; diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java index 127d53337..97b01b4ad 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java @@ -13,6 +13,14 @@ import java.util.function.Function; import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.Utils; import reactor.core.publisher.Mono; @@ -65,15 +73,14 @@ class McpClientFeatures { * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ record Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List, Mono>> toolsChangeConsumers, - List, Mono>> resourcesChangeConsumers, - List, Mono>> resourcesUpdateConsumers, - List, Mono>> promptsChangeConsumers, + Map roots, List, Mono>> toolsChangeConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, List>> loggingConsumers, List>> progressConsumers, - Function> samplingHandler, - Function> elicitationHandler, - boolean enableCallToolSchemaCaching) { + Function> samplingHandler, + Function> elicitationHandler, boolean enableCallToolSchemaCaching) { /** * Create an instance and validate the arguments. @@ -89,16 +96,14 @@ record Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, - List, Mono>> toolsChangeConsumers, - List, Mono>> resourcesChangeConsumers, - List, Mono>> resourcesUpdateConsumers, - List, Mono>> promptsChangeConsumers, + Map roots, List, Mono>> toolsChangeConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, List>> loggingConsumers, List>> progressConsumers, - Function> samplingHandler, - Function> elicitationHandler, - boolean enableCallToolSchemaCaching) { + Function> samplingHandler, + Function> elicitationHandler, boolean enableCallToolSchemaCaching) { Assert.notNull(clientInfo, "Client info must not be null"); this.clientInfo = clientInfo; @@ -124,14 +129,13 @@ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c * @deprecated Only exists for backwards-compatibility purposes. */ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, - List, Mono>> toolsChangeConsumers, - List, Mono>> resourcesChangeConsumers, - List, Mono>> resourcesUpdateConsumers, - List, Mono>> promptsChangeConsumers, + Map roots, List, Mono>> toolsChangeConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, List>> loggingConsumers, - Function> samplingHandler, - Function> elicitationHandler) { + Function> samplingHandler, + Function> elicitationHandler) { this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, resourcesChangeConsumers, resourcesUpdateConsumers, promptsChangeConsumers, loggingConsumers, List.of(), samplingHandler, elicitationHandler, false); @@ -146,26 +150,26 @@ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c * user. */ public static Async fromSync(Sync syncSpec) { - List, Mono>> toolsChangeConsumers = new ArrayList<>(); - for (Consumer> consumer : syncSpec.toolsChangeConsumers()) { + List, Mono>> toolsChangeConsumers = new ArrayList<>(); + for (Consumer> consumer : syncSpec.toolsChangeConsumers()) { toolsChangeConsumers.add(t -> Mono.fromRunnable(() -> consumer.accept(t)) .subscribeOn(Schedulers.boundedElastic())); } - List, Mono>> resourcesChangeConsumers = new ArrayList<>(); - for (Consumer> consumer : syncSpec.resourcesChangeConsumers()) { + List, Mono>> resourcesChangeConsumers = new ArrayList<>(); + for (Consumer> consumer : syncSpec.resourcesChangeConsumers()) { resourcesChangeConsumers.add(r -> Mono.fromRunnable(() -> consumer.accept(r)) .subscribeOn(Schedulers.boundedElastic())); } - List, Mono>> resourcesUpdateConsumers = new ArrayList<>(); - for (Consumer> consumer : syncSpec.resourcesUpdateConsumers()) { + List, Mono>> resourcesUpdateConsumers = new ArrayList<>(); + for (Consumer> consumer : syncSpec.resourcesUpdateConsumers()) { resourcesUpdateConsumers.add(r -> Mono.fromRunnable(() -> consumer.accept(r)) .subscribeOn(Schedulers.boundedElastic())); } - List, Mono>> promptsChangeConsumers = new ArrayList<>(); - for (Consumer> consumer : syncSpec.promptsChangeConsumers()) { + List, Mono>> promptsChangeConsumers = new ArrayList<>(); + for (Consumer> consumer : syncSpec.promptsChangeConsumers()) { promptsChangeConsumers.add(p -> Mono.fromRunnable(() -> consumer.accept(p)) .subscribeOn(Schedulers.boundedElastic())); } @@ -182,11 +186,11 @@ public static Async fromSync(Sync syncSpec) { .subscribeOn(Schedulers.boundedElastic())); } - Function> samplingHandler = r -> Mono + Function> samplingHandler = r -> Mono .fromCallable(() -> syncSpec.samplingHandler().apply(r)) .subscribeOn(Schedulers.boundedElastic()); - Function> elicitationHandler = r -> Mono + Function> elicitationHandler = r -> Mono .fromCallable(() -> syncSpec.elicitationHandler().apply(r)) .subscribeOn(Schedulers.boundedElastic()); @@ -214,15 +218,14 @@ public static Async fromSync(Sync syncSpec) { * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ public record Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List>> toolsChangeConsumers, - List>> resourcesChangeConsumers, - List>> resourcesUpdateConsumers, - List>> promptsChangeConsumers, + Map roots, List>> toolsChangeConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, List> loggingConsumers, List> progressConsumers, - Function samplingHandler, - Function elicitationHandler, - boolean enableCallToolSchemaCaching) { + Function samplingHandler, + Function elicitationHandler, boolean enableCallToolSchemaCaching) { /** * Create an instance and validate the arguments. @@ -240,15 +243,14 @@ public record Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabili * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List>> toolsChangeConsumers, - List>> resourcesChangeConsumers, - List>> resourcesUpdateConsumers, - List>> promptsChangeConsumers, + Map roots, List>> toolsChangeConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, List> loggingConsumers, List> progressConsumers, - Function samplingHandler, - Function elicitationHandler, - boolean enableCallToolSchemaCaching) { + Function samplingHandler, + Function elicitationHandler, boolean enableCallToolSchemaCaching) { Assert.notNull(clientInfo, "Client info must not be null"); this.clientInfo = clientInfo; @@ -274,13 +276,13 @@ public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities cl * @deprecated Only exists for backwards-compatibility purposes. */ public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List>> toolsChangeConsumers, - List>> resourcesChangeConsumers, - List>> resourcesUpdateConsumers, - List>> promptsChangeConsumers, + Map roots, List>> toolsChangeConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, List> loggingConsumers, - Function samplingHandler, - Function elicitationHandler) { + Function samplingHandler, + Function elicitationHandler) { this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, resourcesChangeConsumers, resourcesUpdateConsumers, promptsChangeConsumers, loggingConsumers, List.of(), samplingHandler, elicitationHandler, false); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java index 7fdaa8941..34f9195a0 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java @@ -13,9 +13,19 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; -import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest; -import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; -import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import io.modelcontextprotocol.util.Assert; import reactor.core.publisher.Mono; @@ -232,7 +242,7 @@ public Object ping() { * (text, images, or embedded resources) representing the tool's output - isError: * Boolean indicating if the execution failed (true) or succeeded (false/absent) */ - public McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolRequest) { + public CallToolResult callTool(CallToolRequest callToolRequest) { return withProvidedContext(this.delegate.callTool(callToolRequest)).block(); } @@ -243,7 +253,7 @@ public McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolReque * each with a name, description, and input schema - nextCursor: Optional cursor for * pagination if more tools are available */ - public McpSchema.ListToolsResult listTools() { + public ListToolsResult listTools() { return withProvidedContext(this.delegate.listTools()).block(); } @@ -254,7 +264,7 @@ public McpSchema.ListToolsResult listTools() { * with a name, description, and input schema - nextCursor: Optional cursor for * pagination if more tools are available */ - public McpSchema.ListToolsResult listTools(String cursor) { + public ListToolsResult listTools(String cursor) { return withProvidedContext(this.delegate.listTools(cursor)).block(); } @@ -267,7 +277,7 @@ public McpSchema.ListToolsResult listTools(String cursor) { * Retrieves the list of all resources provided by the server. * @return The list of all resources result */ - public McpSchema.ListResourcesResult listResources() { + public ListResourcesResult listResources() { return withProvidedContext(this.delegate.listResources()).block(); } @@ -277,7 +287,7 @@ public McpSchema.ListResourcesResult listResources() { * @param cursor Optional pagination cursor from a previous list request * @return The list of resources result */ - public McpSchema.ListResourcesResult listResources(String cursor) { + public ListResourcesResult listResources(String cursor) { return withProvidedContext(this.delegate.listResources(cursor)).block(); } @@ -287,7 +297,7 @@ public McpSchema.ListResourcesResult listResources(String cursor) { * @param resource the resource to read * @return the resource content. */ - public McpSchema.ReadResourceResult readResource(McpSchema.Resource resource) { + public ReadResourceResult readResource(Resource resource) { return withProvidedContext(this.delegate.readResource(resource)).block(); } @@ -297,7 +307,7 @@ public McpSchema.ReadResourceResult readResource(McpSchema.Resource resource) { * @param readResourceRequest the read resource request. * @return the resource content. */ - public McpSchema.ReadResourceResult readResource(McpSchema.ReadResourceRequest readResourceRequest) { + public ReadResourceResult readResource(ReadResourceRequest readResourceRequest) { return withProvidedContext(this.delegate.readResource(readResourceRequest)).block(); } @@ -306,7 +316,7 @@ public McpSchema.ReadResourceResult readResource(McpSchema.ReadResourceRequest r * Retrieves the list of all resource templates provided by the server. * @return The list of all resource templates result. */ - public McpSchema.ListResourceTemplatesResult listResourceTemplates() { + public ListResourceTemplatesResult listResourceTemplates() { return withProvidedContext(this.delegate.listResourceTemplates()).block(); } @@ -319,7 +329,7 @@ public McpSchema.ListResourceTemplatesResult listResourceTemplates() { * @param cursor Optional pagination cursor from a previous list request * @return The list of resource templates result. */ - public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor) { + public ListResourceTemplatesResult listResourceTemplates(String cursor) { return withProvidedContext(this.delegate.listResourceTemplates(cursor)).block(); } @@ -333,7 +343,7 @@ public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor * @param subscribeRequest the subscribe request contains the uri of the resource to * subscribe to. */ - public void subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { + public void subscribeResource(SubscribeRequest subscribeRequest) { withProvidedContext(this.delegate.subscribeResource(subscribeRequest)).block(); } @@ -343,7 +353,7 @@ public void subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { * @param unsubscribeRequest the unsubscribe request contains the uri of the resource * to unsubscribe from. */ - public void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { + public void unsubscribeResource(UnsubscribeRequest unsubscribeRequest) { withProvidedContext(this.delegate.unsubscribeResource(unsubscribeRequest)).block(); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java index be4e4cf97..cd2eabe6c 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java @@ -27,10 +27,10 @@ import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.HttpHeaders; import io.modelcontextprotocol.spec.McpClientTransport; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage; import io.modelcontextprotocol.spec.McpTransportException; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.Utils; import reactor.core.Disposable; @@ -362,7 +362,7 @@ public Mono connect(Function, Mono> h } } else if (MESSAGE_EVENT_TYPE.equals(responseEvent.sseEvent().event())) { - JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(jsonMapper, + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(jsonMapper, responseEvent.sseEvent().data()); sink.success(); return Flux.just(message); @@ -376,8 +376,7 @@ else if (MESSAGE_EVENT_TYPE.equals(responseEvent.sseEvent().event())) { sink.error(new McpTransportException("Error processing SSE event", e)); } } - return Flux.error( - new RuntimeException("Failed to send message: " + responseEvent)); + return Flux.error(new RuntimeException("Failed to send message: " + responseEvent)); }) .flatMap(jsonRpcMessage -> handler.apply(Mono.just(jsonRpcMessage))) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java index d6b01e17f..765f467f7 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java @@ -33,12 +33,14 @@ import io.modelcontextprotocol.spec.DefaultMcpTransportStream; import io.modelcontextprotocol.spec.HttpHeaders; import io.modelcontextprotocol.spec.McpClientTransport; -import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpTransportException; import io.modelcontextprotocol.spec.McpTransportSession; import io.modelcontextprotocol.spec.McpTransportSessionNotFoundException; import io.modelcontextprotocol.spec.McpTransportStream; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.Utils; import org.reactivestreams.Publisher; @@ -121,7 +123,7 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport { private final AtomicReference> activeSession = new AtomicReference<>(); - private final AtomicReference, Mono>> handler = new AtomicReference<>(); + private final AtomicReference, Mono>> handler = new AtomicReference<>(); private final AtomicReference> exceptionHandler = new AtomicReference<>(); @@ -159,7 +161,7 @@ public static Builder builder(String baseUri) { } @Override - public Mono connect(Function, Mono> handler) { + public Mono connect(Function, Mono> handler) { return Mono.deferContextual(ctx -> { this.handler.set(handler); if (this.openConnectionOnStartup) { @@ -311,10 +313,10 @@ private Mono reconnect(McpTransportStream stream) { // We don't support batching ATM and probably // won't since the next version considers // removing it. - McpSchema.JSONRPCMessage message = McpSchema - .deserializeJsonRpcMessage(this.jsonMapper, data); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(this.jsonMapper, + data); - Tuple2, Iterable> idWithMessages = Tuples + Tuple2, Iterable> idWithMessages = Tuples .of(Optional.ofNullable(responseEvent.sseEvent().id()), List.of(message)); @@ -327,7 +329,7 @@ private Mono reconnect(McpTransportStream stream) { } catch (IOException ioException) { - return Flux.error(new McpTransportException( + return Flux.error(new McpTransportException( "Error parsing JSON-RPC message: " + responseEvent, ioException)); } } @@ -352,9 +354,9 @@ else if (statusCode == NOT_FOUND) { String sessionIdRepresentation = sessionIdOrPlaceholder(transportSession); McpTransportSessionNotFoundException exception = new McpTransportSessionNotFoundException( "Session not found for session ID: " + sessionIdRepresentation); - return Flux.error(exception); + return Flux.error(exception); } - return Flux.error( + return Flux.error( new McpTransportException("Server Not Found. Status code:" + statusCode + ", response-event:" + responseEvent)); } @@ -366,19 +368,19 @@ else if (statusCode == BAD_REQUEST) { String sessionIdRepresentation = sessionIdOrPlaceholder(transportSession); McpTransportSessionNotFoundException exception = new McpTransportSessionNotFoundException( "Session not found for session ID: " + sessionIdRepresentation); - return Flux.error(exception); + return Flux.error(exception); } - return Flux.error( - new McpTransportException("Bad Request. Status code:" + statusCode - + ", response-event:" + responseEvent)); + return Flux + .error(new McpTransportException("Bad Request. Status code:" + + statusCode + ", response-event:" + responseEvent)); } - return Flux.error(new McpTransportException( + return Flux.error(new McpTransportException( "Received unrecognized SSE event type: " + responseEvent.sseEvent().event())); - }).flatMap( - jsonrpcMessage -> this.handler.get().apply(Mono.just(jsonrpcMessage))) + }) + .flatMap( + jsonrpcMessage -> this.handler.get().apply(Mono.just(jsonrpcMessage))) .onErrorMap(CompletionException.class, t -> t.getCause()) .onErrorComplete(t -> { this.handleException(t); @@ -425,7 +427,7 @@ else if (contentType.contains(APPLICATION_JSON)) { } - public String toString(McpSchema.JSONRPCMessage message) { + public String toString(JSONRPCMessage message) { try { return this.jsonMapper.writeValueAsString(message); } @@ -434,7 +436,7 @@ public String toString(McpSchema.JSONRPCMessage message) { } } - public Mono sendMessage(McpSchema.JSONRPCMessage sentMessage) { + public Mono sendMessage(JSONRPCMessage sentMessage) { return Mono.create(deliveredSink -> { logger.debug("Sending message {}", sentMessage); @@ -529,10 +531,9 @@ else if (contentType.contains(TEXT_EVENT_STREAM)) { // won't // since the // next version considers removing it. - McpSchema.JSONRPCMessage message = McpSchema - .deserializeJsonRpcMessage(this.jsonMapper, data); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(this.jsonMapper, data); - Tuple2, Iterable> idWithMessages = Tuples + Tuple2, Iterable> idWithMessages = Tuples .of(Optional.ofNullable(sseEvent.id()), List.of(message)); McpTransportStream sessionStream = new DefaultMcpTransportStream<>( @@ -545,7 +546,7 @@ else if (contentType.contains(TEXT_EVENT_STREAM)) { return Flux.from(sessionStream.consumeSseStream(Flux.just(idWithMessages))); } catch (IOException ioException) { - return Flux.error(new McpTransportException( + return Flux.error(new McpTransportException( "Error parsing JSON-RPC message: " + responseEvent, ioException)); } }); @@ -553,14 +554,14 @@ else if (contentType.contains(TEXT_EVENT_STREAM)) { else if (contentType.contains(APPLICATION_JSON)) { deliveredSink.success(); String data = ((ResponseSubscribers.AggregateResponseEvent) responseEvent).data(); - if (sentMessage instanceof McpSchema.JSONRPCNotification) { + if (sentMessage instanceof JSONRPCNotification) { logger.warn("Notification: {} received non-compliant response: {}", sentMessage, Utils.hasText(data) ? data : "[empty]"); return Mono.empty(); } try { - return Mono.just(McpSchema.deserializeJsonRpcMessage(jsonMapper, data)); + return Mono.just(JSONRPC.deserializeJsonRpcMessage(jsonMapper, data)); } catch (IOException e) { return Mono.error(new McpTransportException( @@ -570,8 +571,8 @@ else if (contentType.contains(APPLICATION_JSON)) { logger.warn("Unknown media type {} returned for POST in session {}", contentType, sessionRepresentation); - return Flux.error( - new RuntimeException("Unknown media type returned: " + contentType)); + return Flux + .error(new RuntimeException("Unknown media type returned: " + contentType)); } else if (statusCode == NOT_FOUND) { if (transportSession != null && transportSession.sessionId().isPresent()) { @@ -580,9 +581,9 @@ else if (statusCode == NOT_FOUND) { logger.debug("Session not found for session ID: {}", transportSession.sessionId().get()); McpTransportSessionNotFoundException exception = new McpTransportSessionNotFoundException( "Session not found for session ID: " + sessionRepresentation); - return Flux.error(exception); + return Flux.error(exception); } - return Flux.error(new McpTransportException( + return Flux.error(new McpTransportException( "Server Not Found. Status code:" + statusCode + ", response-event:" + responseEvent)); } else if (statusCode == BAD_REQUEST) { @@ -596,14 +597,13 @@ else if (statusCode == BAD_REQUEST) { // response is 404, we consider it a session not found error. McpTransportSessionNotFoundException exception = new McpTransportSessionNotFoundException( "Session not found for session ID: " + sessionRepresentation); - return Flux.error(exception); + return Flux.error(exception); } - return Flux.error(new McpTransportException( + return Flux.error(new McpTransportException( "Bad Request. Status code:" + statusCode + ", response-event:" + responseEvent)); } - return Flux.error( - new RuntimeException("Failed to send message: " + responseEvent)); + return Flux.error(new RuntimeException("Failed to send message: " + responseEvent)); }) .flatMap(jsonRpcMessage -> this.handler.get().apply(Mono.just(jsonRpcMessage))) .onErrorMap(CompletionException.class, t -> t.getCause()) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/StdioClientTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/StdioClientTransport.java index 1b4eaca97..90fde9ef9 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/StdioClientTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/StdioClientTransport.java @@ -18,8 +18,8 @@ import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.spec.McpClientTransport; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import io.modelcontextprotocol.util.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -250,7 +250,7 @@ private void startInboundProcessing() { String line; while (!isClosing && (line = processReader.readLine()) != null) { try { - JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(this.jsonMapper, line); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(this.jsonMapper, line); if (!this.inboundSink.tryEmitNext(message).isSuccess()) { if (!isClosing) { logger.error("Failed to enqueue inbound message: {}", message); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/DefaultMcpStatelessServerHandler.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/DefaultMcpStatelessServerHandler.java index 660a15e6a..9a5bf2ec3 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/DefaultMcpStatelessServerHandler.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/DefaultMcpStatelessServerHandler.java @@ -4,15 +4,19 @@ package io.modelcontextprotocol.server; +import java.util.Map; + import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; -import java.util.Map; - class DefaultMcpStatelessServerHandler implements McpStatelessServerHandler { private static final Logger logger = LoggerFactory.getLogger(DefaultMcpStatelessServerHandler.class); @@ -28,8 +32,7 @@ public DefaultMcpStatelessServerHandler(Map handleRequest(McpTransportContext transportContext, - McpSchema.JSONRPCRequest request) { + public Mono handleRequest(McpTransportContext transportContext, JSONRPCRequest request) { McpStatelessRequestHandler requestHandler = this.requestHandlers.get(request.method()); if (requestHandler == null) { return Mono.error(McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND) @@ -37,23 +40,21 @@ public Mono handleRequest(McpTransportContext transpo .build()); } return requestHandler.handle(transportContext, request.params()) - .map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null)) + .map(result -> new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), result, null)) .onErrorResume(t -> { - McpSchema.JSONRPCResponse.JSONRPCError error; + JSONRPCResponse.JSONRPCError error; if (t instanceof McpError mcpError && mcpError.getJsonRpcError() != null) { error = mcpError.getJsonRpcError(); } else { - error = new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, - t.getMessage(), null); + error = new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, t.getMessage(), null); } - return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, error)); + return Mono.just(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, error)); }); } @Override - public Mono handleNotification(McpTransportContext transportContext, - McpSchema.JSONRPCNotification notification) { + public Mono handleNotification(McpTransportContext transportContext, JSONRPCNotification notification) { McpStatelessNotificationHandler notificationHandler = this.notificationHandlers.get(notification.method()); if (notificationHandler == null) { logger.warn("Missing handler for notification type: {}", notification.method()); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index b078493ef..30c050a1b 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -23,18 +23,32 @@ import io.modelcontextprotocol.spec.McpClientSession; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.ResourceReference; import io.modelcontextprotocol.spec.McpSchema.SetLevelRequest; -import io.modelcontextprotocol.spec.McpSchema.Tool; import io.modelcontextprotocol.spec.McpServerSession; import io.modelcontextprotocol.spec.McpServerTransportProvider; import io.modelcontextprotocol.spec.McpServerTransportProviderBase; import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.DefaultMcpUriTemplateManagerFactory; import io.modelcontextprotocol.util.McpUriTemplateManagerFactory; @@ -359,9 +373,9 @@ public Mono addTool(McpServerFeatures.AsyncToolSpecification toolSpecifica } private static class StructuredOutputCallToolHandler - implements BiFunction> { + implements BiFunction> { - private final BiFunction> delegateCallToolResult; + private final BiFunction> delegateCallToolResult; private final JsonSchemaValidator jsonSchemaValidator; @@ -369,7 +383,7 @@ private static class StructuredOutputCallToolHandler public StructuredOutputCallToolHandler(JsonSchemaValidator jsonSchemaValidator, Map outputSchema, - BiFunction> delegateHandler) { + BiFunction> delegateHandler) { Assert.notNull(jsonSchemaValidator, "JsonSchemaValidator must not be null"); Assert.notNull(delegateHandler, "Delegate call tool result handler must not be null"); @@ -380,7 +394,7 @@ public StructuredOutputCallToolHandler(JsonSchemaValidator jsonSchemaValidator, } @Override - public Mono apply(McpAsyncServerExchange exchange, McpSchema.CallToolRequest request) { + public Mono apply(McpAsyncServerExchange exchange, CallToolRequest request) { return this.delegateCallToolResult.apply(exchange, request).map(result -> { @@ -518,19 +532,18 @@ public Mono notifyToolsListChanged() { return this.mcpTransportProvider.notifyClients(McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED, null); } - private McpRequestHandler toolsListRequestHandler() { + private McpRequestHandler toolsListRequestHandler() { return (exchange, params) -> { List tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList(); - return Mono.just(new McpSchema.ListToolsResult(tools, null)); + return Mono.just(new ListToolsResult(tools, null)); }; } private McpRequestHandler toolsCallRequestHandler() { return (exchange, params) -> { - McpSchema.CallToolRequest callToolRequest = jsonMapper.convertValue(params, - new TypeRef() { - }); + CallToolRequest callToolRequest = jsonMapper.convertValue(params, new TypeRef() { + }); Optional toolSpecification = this.tools.stream() .filter(tr -> callToolRequest.name().equals(tr.tool().name())) @@ -585,7 +598,7 @@ public Mono addResource(McpServerFeatures.AsyncResourceSpecification resou * List all registered resources. * @return A Flux stream of all registered resources */ - public Flux listResources() { + public Flux listResources() { return Flux.fromIterable(this.resources.values()).map(McpServerFeatures.AsyncResourceSpecification::resource); } @@ -654,7 +667,7 @@ public Mono addResourceTemplate( * List all registered resource templates. * @return A Flux stream of all registered resource templates */ - public Flux listResourceTemplates() { + public Flux listResourceTemplates() { return Flux.fromIterable(this.resourceTemplates.values()) .map(McpServerFeatures.AsyncResourceTemplateSpecification::resourceTemplate); } @@ -729,9 +742,8 @@ private void removeSessionSubscriptions(String sessionId) { private McpRequestHandler resourcesSubscribeRequestHandler() { return (exchange, params) -> Mono.defer(() -> { - McpSchema.SubscribeRequest subscribeRequest = jsonMapper.convertValue(params, - new TypeRef() { - }); + SubscribeRequest subscribeRequest = jsonMapper.convertValue(params, new TypeRef() { + }); String uri = subscribeRequest.uri(); String sessionId = exchange.sessionId(); this.resourceSubscriptions.computeIfAbsent(uri, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())) @@ -744,9 +756,8 @@ private McpRequestHandler resourcesSubscribeRequestHandler() { private McpRequestHandler resourcesUnsubscribeRequestHandler() { return (exchange, params) -> Mono.defer(() -> { - McpSchema.UnsubscribeRequest unsubscribeRequest = jsonMapper.convertValue(params, - new TypeRef() { - }); + UnsubscribeRequest unsubscribeRequest = jsonMapper.convertValue(params, new TypeRef() { + }); String uri = unsubscribeRequest.uri(); String sessionId = exchange.sessionId(); Set sessions = this.resourceSubscriptions.get(uri); @@ -761,29 +772,29 @@ private McpRequestHandler resourcesUnsubscribeRequestHandler() { }); } - private McpRequestHandler resourcesListRequestHandler() { + private McpRequestHandler resourcesListRequestHandler() { return (exchange, params) -> { var resourceList = this.resources.values() .stream() .map(McpServerFeatures.AsyncResourceSpecification::resource) .toList(); - return Mono.just(new McpSchema.ListResourcesResult(resourceList, null)); + return Mono.just(new ListResourcesResult(resourceList, null)); }; } - private McpRequestHandler resourceTemplateListRequestHandler() { + private McpRequestHandler resourceTemplateListRequestHandler() { return (exchange, params) -> { var resourceList = this.resourceTemplates.values() .stream() .map(McpServerFeatures.AsyncResourceTemplateSpecification::resourceTemplate) .toList(); - return Mono.just(new McpSchema.ListResourceTemplatesResult(resourceList, null)); + return Mono.just(new ListResourceTemplatesResult(resourceList, null)); }; } - private McpRequestHandler resourcesReadRequestHandler() { + private McpRequestHandler resourcesReadRequestHandler() { return (ex, params) -> { - McpSchema.ReadResourceRequest resourceRequest = jsonMapper.convertValue(params, new TypeRef<>() { + ReadResourceRequest resourceRequest = jsonMapper.convertValue(params, new TypeRef<>() { }); var resourceUri = resourceRequest.uri(); @@ -855,7 +866,7 @@ public Mono addPrompt(McpServerFeatures.AsyncPromptSpecification promptSpe * List all registered prompts. * @return A Flux stream of all registered prompts */ - public Flux listPrompts() { + public Flux listPrompts() { return Flux.fromIterable(this.prompts.values()).map(McpServerFeatures.AsyncPromptSpecification::prompt); } @@ -897,7 +908,7 @@ public Mono notifyPromptsListChanged() { return this.mcpTransportProvider.notifyClients(McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED, null); } - private McpRequestHandler promptsListRequestHandler() { + private McpRequestHandler promptsListRequestHandler() { return (exchange, params) -> { // TODO: Implement pagination // McpSchema.PaginatedRequest request = objectMapper.convertValue(params, @@ -909,15 +920,14 @@ private McpRequestHandler promptsListRequestHandler .map(McpServerFeatures.AsyncPromptSpecification::prompt) .toList(); - return Mono.just(new McpSchema.ListPromptsResult(promptList, null)); + return Mono.just(new ListPromptsResult(promptList, null)); }; } - private McpRequestHandler promptsGetRequestHandler() { + private McpRequestHandler promptsGetRequestHandler() { return (exchange, params) -> { - McpSchema.GetPromptRequest promptRequest = jsonMapper.convertValue(params, - new TypeRef() { - }); + GetPromptRequest promptRequest = jsonMapper.convertValue(params, new TypeRef() { + }); // Implement prompt retrieval logic here McpServerFeatures.AsyncPromptSpecification specification = this.prompts.get(promptRequest.name()); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServerExchange.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServerExchange.java index 40a76045b..a1b1274ea 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServerExchange.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServerExchange.java @@ -14,6 +14,10 @@ import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; import io.modelcontextprotocol.spec.McpSession; import io.modelcontextprotocol.util.Assert; import reactor.core.publisher.Mono; @@ -37,13 +41,13 @@ public class McpAsyncServerExchange { private final McpTransportContext transportContext; - private static final TypeRef CREATE_MESSAGE_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef CREATE_MESSAGE_RESULT_TYPE_REF = new TypeRef<>() { }; private static final TypeRef LIST_ROOTS_RESULT_TYPE_REF = new TypeRef<>() { }; - private static final TypeRef ELICITATION_RESULT_TYPE_REF = new TypeRef<>() { + private static final TypeRef ELICITATION_RESULT_TYPE_REF = new TypeRef<>() { }; public static final TypeRef OBJECT_TYPE_REF = new TypeRef<>() { @@ -112,13 +116,13 @@ public String sessionId() { * include context from MCP servers in their prompts. * @param createMessageRequest The request to create a new message * @return A Mono that completes when the message has been created - * @see McpSchema.CreateMessageRequest - * @see McpSchema.CreateMessageResult + * @see CreateMessageRequest + * @see CreateMessageResult * @see Sampling * Specification */ - public Mono createMessage(McpSchema.CreateMessageRequest createMessageRequest) { + public Mono createMessage(CreateMessageRequest createMessageRequest) { if (this.clientCapabilities == null) { return Mono .error(new IllegalStateException("Client must be initialized. Call the initialize method first!")); @@ -138,13 +142,13 @@ public Mono createMessage(McpSchema.CreateMessage * structured data from users with optional JSON schemas to validate responses. * @param elicitRequest The request to create a new elicitation * @return A Mono that completes when the elicitation has been resolved. - * @see McpSchema.ElicitRequest - * @see McpSchema.ElicitResult + * @see ElicitRequest + * @see ElicitResult * @see Elicitation * Specification */ - public Mono createElicitation(McpSchema.ElicitRequest elicitRequest) { + public Mono createElicitation(ElicitRequest elicitRequest) { if (this.clientCapabilities == null) { return Mono .error(new IllegalStateException("Client must be initialized. Call the initialize method first!")); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java index 360eb607d..125a54831 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java @@ -18,10 +18,12 @@ import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.schema.JsonSchemaValidator; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpServerTransportProvider; import io.modelcontextprotocol.spec.McpStatelessServerTransport; import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.DefaultMcpUriTemplateManagerFactory; import io.modelcontextprotocol.util.McpUriTemplateManagerFactory; @@ -450,12 +452,12 @@ public AsyncSpecification capabilities(McpSchema.ServerCapabilities serverCap * @param callHandler The function that implements the tool's logic. Must not be * null. The function's first argument is an {@link McpAsyncServerExchange} upon * which the server can interact with the connected client. The second argument is - * the {@link McpSchema.CallToolRequest} object containing the tool call + * the {@link CallToolRequest} object containing the tool call * @return This builder instance for method chaining * @throws IllegalArgumentException if tool or handler is null */ - public AsyncSpecification toolCall(McpSchema.Tool tool, - BiFunction> callHandler) { + public AsyncSpecification toolCall(Tool tool, + BiFunction> callHandler) { Assert.notNull(tool, "Tool must not be null"); Assert.notNull(callHandler, "Handler must not be null"); @@ -1037,8 +1039,8 @@ public SyncSpecification capabilities(McpSchema.ServerCapabilities serverCapa * @return This builder instance for method chaining * @throws IllegalArgumentException if tool or handler is null */ - public SyncSpecification toolCall(McpSchema.Tool tool, - BiFunction handler) { + public SyncSpecification toolCall(Tool tool, + BiFunction handler) { Assert.notNull(tool, "Tool must not be null"); Assert.notNull(handler, "Handler must not be null"); validateToolName(tool.name()); @@ -1559,12 +1561,12 @@ public StatelessAsyncSpecification capabilities(McpSchema.ServerCapabilities ser * @param callHandler The function that implements the tool's logic. Must not be * null. The function's first argument is an {@link McpAsyncServerExchange} upon * which the server can interact with the connected client. The second argument is - * the {@link McpSchema.CallToolRequest} object containing the tool call + * the {@link CallToolRequest} object containing the tool call * @return This builder instance for method chaining * @throws IllegalArgumentException if tool or handler is null */ - public StatelessAsyncSpecification toolCall(McpSchema.Tool tool, - BiFunction> callHandler) { + public StatelessAsyncSpecification toolCall(Tool tool, + BiFunction> callHandler) { Assert.notNull(tool, "Tool must not be null"); Assert.notNull(callHandler, "Handler must not be null"); @@ -2042,12 +2044,12 @@ public StatelessSyncSpecification capabilities(McpSchema.ServerCapabilities serv * @param callHandler The function that implements the tool's logic. Must not be * null. The function's first argument is an {@link McpSyncServerExchange} upon * which the server can interact with the connected client. The second argument is - * the {@link McpSchema.CallToolRequest} object containing the tool call + * the {@link CallToolRequest} object containing the tool call * @return This builder instance for method chaining * @throws IllegalArgumentException if tool or handler is null */ - public StatelessSyncSpecification toolCall(McpSchema.Tool tool, - BiFunction callHandler) { + public StatelessSyncSpecification toolCall(Tool tool, + BiFunction callHandler) { Assert.notNull(tool, "Tool must not be null"); Assert.notNull(callHandler, "Handler must not be null"); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java index a0cbae0f2..6fea85ad8 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java @@ -12,7 +12,16 @@ import java.util.function.BiFunction; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.Utils; import reactor.core.publisher.Mono; @@ -217,13 +226,13 @@ record Sync(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities se * @param call Deprecated. Use the {@link AsyncToolSpecification#callHandler} instead. * @param callHandler The function that implements the tool's logic, receiving a * {@link McpAsyncServerExchange} and a - * {@link io.modelcontextprotocol.spec.McpSchema.CallToolRequest} and returning + * {@link io.modelcontextprotocol.spec.schema.tool.CallToolRequest} and returning * results. The function's first argument is an {@link McpAsyncServerExchange} upon * which the server can interact with the connected client. The second arguments is a * map of tool arguments. */ - public record AsyncToolSpecification(McpSchema.Tool tool, - BiFunction> callHandler) { + public record AsyncToolSpecification(Tool tool, + BiFunction> callHandler) { static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec) { return fromSync(syncToolSpec, false); @@ -236,8 +245,7 @@ static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec, boole return null; } - BiFunction> callHandler = ( - exchange, req) -> { + BiFunction> callHandler = (exchange, req) -> { var toolResult = Mono .fromCallable(() -> syncToolSpec.callHandler().apply(new McpSyncServerExchange(exchange), req)); return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic()); @@ -251,9 +259,9 @@ static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec, boole */ public static class Builder { - private McpSchema.Tool tool; + private Tool tool; - private BiFunction> callHandler; + private BiFunction> callHandler; /** * Sets the tool definition. @@ -261,7 +269,7 @@ public static class Builder { * schema * @return this builder instance */ - public Builder tool(McpSchema.Tool tool) { + public Builder tool(Tool tool) { this.tool = tool; return this; } @@ -272,7 +280,7 @@ public Builder tool(McpSchema.Tool tool) { * @return this builder instance */ public Builder callHandler( - BiFunction> callHandler) { + BiFunction> callHandler) { this.callHandler = callHandler; return this; } @@ -331,10 +339,10 @@ public static Builder builder() { * @param readHandler The function that handles resource read requests. The function's * first argument is an {@link McpAsyncServerExchange} upon which the server can * interact with the connected client. The second arguments is a - * {@link io.modelcontextprotocol.spec.McpSchema.ReadResourceRequest}. + * {@link io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest}. */ - public record AsyncResourceSpecification(McpSchema.Resource resource, - BiFunction> readHandler) { + public record AsyncResourceSpecification(Resource resource, + BiFunction> readHandler) { static AsyncResourceSpecification fromSync(SyncResourceSpecification resource, boolean immediateExecution) { // FIXME: This is temporary, proper validation should be implemented @@ -370,11 +378,10 @@ static AsyncResourceSpecification fromSync(SyncResourceSpecification resource, b * @param readHandler The function that handles resource read requests. The function's * first argument is an {@link McpSyncServerExchange} upon which the server can * interact with the connected client. The second arguments is a - * {@link McpSchema.ReadResourceRequest}. {@link McpSchema.ResourceTemplate} - * {@link McpSchema.ReadResourceResult} + * {@link ReadResourceRequest}. {@link ResourceTemplate} {@link ReadResourceResult} */ - public record AsyncResourceTemplateSpecification(McpSchema.ResourceTemplate resourceTemplate, - BiFunction> readHandler) { + public record AsyncResourceTemplateSpecification(ResourceTemplate resourceTemplate, + BiFunction> readHandler) { static AsyncResourceTemplateSpecification fromSync(SyncResourceTemplateSpecification resource, boolean immediateExecution) { @@ -419,10 +426,10 @@ static AsyncResourceTemplateSpecification fromSync(SyncResourceTemplateSpecifica * formatted templates. The function's first argument is an * {@link McpAsyncServerExchange} upon which the server can interact with the * connected client. The second arguments is a - * {@link io.modelcontextprotocol.spec.McpSchema.GetPromptRequest}. + * {@link io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest}. */ - public record AsyncPromptSpecification(McpSchema.Prompt prompt, - BiFunction> promptHandler) { + public record AsyncPromptSpecification(Prompt prompt, + BiFunction> promptHandler) { static AsyncPromptSpecification fromSync(SyncPromptSpecification prompt, boolean immediateExecution) { // FIXME: This is temporary, proper validation should be implemented @@ -507,22 +514,22 @@ static AsyncCompletionSpecification fromSync(SyncCompletionSpecification complet * @param tool The tool definition including name, description, and parameter schema * @param callHandler The function that implements the tool's logic, receiving a * {@link McpSyncServerExchange} and a - * {@link io.modelcontextprotocol.spec.McpSchema.CallToolRequest} and returning + * {@link io.modelcontextprotocol.spec.schema.tool.CallToolRequest} and returning * results. The function's first argument is an {@link McpSyncServerExchange} upon * which the server can interact with the client. The second argument is a request * object containing the arguments passed to the tool. */ - public record SyncToolSpecification(McpSchema.Tool tool, - BiFunction callHandler) { + public record SyncToolSpecification(Tool tool, + BiFunction callHandler) { /** * Builder for creating SyncToolSpecification instances. */ public static class Builder { - private McpSchema.Tool tool; + private Tool tool; - private BiFunction callHandler; + private BiFunction callHandler; /** * Sets the tool definition. @@ -530,7 +537,7 @@ public static class Builder { * schema * @return this builder instance */ - public Builder tool(McpSchema.Tool tool) { + public Builder tool(Tool tool) { this.tool = tool; return this; } @@ -540,8 +547,7 @@ public Builder tool(McpSchema.Tool tool) { * @param callHandler The function that implements the tool's logic * @return this builder instance */ - public Builder callHandler( - BiFunction callHandler) { + public Builder callHandler(BiFunction callHandler) { this.callHandler = callHandler; return this; } @@ -602,10 +608,10 @@ public static Builder builder() { * @param readHandler The function that handles resource read requests. The function's * first argument is an {@link McpSyncServerExchange} upon which the server can * interact with the connected client. The second arguments is a - * {@link io.modelcontextprotocol.spec.McpSchema.ReadResourceRequest}. + * {@link io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest}. */ - public record SyncResourceSpecification(McpSchema.Resource resource, - BiFunction readHandler) { + public record SyncResourceSpecification(Resource resource, + BiFunction readHandler) { } /** @@ -629,11 +635,10 @@ public record SyncResourceSpecification(McpSchema.Resource resource, * @param readHandler The function that handles resource read requests. The function's * first argument is an {@link McpSyncServerExchange} upon which the server can * interact with the connected client. The second arguments is a - * {@link McpSchema.ReadResourceRequest}. {@link McpSchema.ResourceTemplate} - * {@link McpSchema.ReadResourceResult} + * {@link ReadResourceRequest}. {@link ResourceTemplate} {@link ReadResourceResult} */ - public record SyncResourceTemplateSpecification(McpSchema.ResourceTemplate resourceTemplate, - BiFunction readHandler) { + public record SyncResourceTemplateSpecification(ResourceTemplate resourceTemplate, + BiFunction readHandler) { } /** @@ -665,10 +670,10 @@ public record SyncResourceTemplateSpecification(McpSchema.ResourceTemplate resou * formatted templates. The function's first argument is an * {@link McpSyncServerExchange} upon which the server can interact with the connected * client. The second arguments is a - * {@link io.modelcontextprotocol.spec.McpSchema.GetPromptRequest}. + * {@link io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest}. */ - public record SyncPromptSpecification(McpSchema.Prompt prompt, - BiFunction promptHandler) { + public record SyncPromptSpecification(Prompt prompt, + BiFunction promptHandler) { } /** diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java index c7a1fd0d7..07a83d7a2 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java @@ -11,13 +11,25 @@ import io.modelcontextprotocol.server.McpStatelessServerFeatures.AsyncResourceTemplateSpecification; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.ResourceReference; -import io.modelcontextprotocol.spec.McpSchema.Tool; import io.modelcontextprotocol.spec.McpStatelessServerTransport; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.DefaultMcpUriTemplateManagerFactory; import io.modelcontextprotocol.util.McpUriTemplateManagerFactory; @@ -231,9 +243,9 @@ private static McpStatelessServerFeatures.AsyncToolSpecification withStructuredO } private static class StructuredOutputCallToolHandler - implements BiFunction> { + implements BiFunction> { - private final BiFunction> delegateHandler; + private final BiFunction> delegateHandler; private final JsonSchemaValidator jsonSchemaValidator; @@ -241,7 +253,7 @@ private static class StructuredOutputCallToolHandler public StructuredOutputCallToolHandler(JsonSchemaValidator jsonSchemaValidator, Map outputSchema, - BiFunction> delegateHandler) { + BiFunction> delegateHandler) { Assert.notNull(jsonSchemaValidator, "JsonSchemaValidator must not be null"); Assert.notNull(delegateHandler, "Delegate call tool result handler must not be null"); @@ -252,7 +264,7 @@ public StructuredOutputCallToolHandler(JsonSchemaValidator jsonSchemaValidator, } @Override - public Mono apply(McpTransportContext transportContext, McpSchema.CallToolRequest request) { + public Mono apply(McpTransportContext transportContext, CallToolRequest request) { return this.delegateHandler.apply(transportContext, request).map(result -> { @@ -383,20 +395,19 @@ public Mono removeTool(String toolName) { }); } - private McpStatelessRequestHandler toolsListRequestHandler() { + private McpStatelessRequestHandler toolsListRequestHandler() { return (ctx, params) -> { List tools = this.tools.stream() .map(McpStatelessServerFeatures.AsyncToolSpecification::tool) .toList(); - return Mono.just(new McpSchema.ListToolsResult(tools, null)); + return Mono.just(new ListToolsResult(tools, null)); }; } private McpStatelessRequestHandler toolsCallRequestHandler() { return (ctx, params) -> { - McpSchema.CallToolRequest callToolRequest = jsonMapper.convertValue(params, - new TypeRef() { - }); + CallToolRequest callToolRequest = jsonMapper.convertValue(params, new TypeRef() { + }); Optional toolSpecification = this.tools.stream() .filter(tr -> callToolRequest.name().equals(tr.tool().name())) @@ -447,7 +458,7 @@ public Mono addResource(McpStatelessServerFeatures.AsyncResourceSpecificat * List all registered resources. * @return A Flux stream of all registered resources */ - public Flux listResources() { + public Flux listResources() { return Flux.fromIterable(this.resources.values()) .map(McpStatelessServerFeatures.AsyncResourceSpecification::resource); } @@ -509,7 +520,7 @@ public Mono addResourceTemplate( * List all registered resource templates. * @return A Flux stream of all registered resource templates */ - public Flux listResourceTemplates() { + public Flux listResourceTemplates() { return Flux.fromIterable(this.resourceTemplates.values()) .map(McpStatelessServerFeatures.AsyncResourceTemplateSpecification::resourceTemplate); } @@ -539,29 +550,29 @@ public Mono removeResourceTemplate(String uriTemplate) { }); } - private McpStatelessRequestHandler resourcesListRequestHandler() { + private McpStatelessRequestHandler resourcesListRequestHandler() { return (ctx, params) -> { var resourceList = this.resources.values() .stream() .map(McpStatelessServerFeatures.AsyncResourceSpecification::resource) .toList(); - return Mono.just(new McpSchema.ListResourcesResult(resourceList, null)); + return Mono.just(new ListResourcesResult(resourceList, null)); }; } - private McpStatelessRequestHandler resourceTemplateListRequestHandler() { + private McpStatelessRequestHandler resourceTemplateListRequestHandler() { return (exchange, params) -> { var resourceList = this.resourceTemplates.values() .stream() .map(AsyncResourceTemplateSpecification::resourceTemplate) .toList(); - return Mono.just(new McpSchema.ListResourceTemplatesResult(resourceList, null)); + return Mono.just(new ListResourceTemplatesResult(resourceList, null)); }; } - private McpStatelessRequestHandler resourcesReadRequestHandler() { + private McpStatelessRequestHandler resourcesReadRequestHandler() { return (ctx, params) -> { - McpSchema.ReadResourceRequest resourceRequest = jsonMapper.convertValue(params, new TypeRef<>() { + ReadResourceRequest resourceRequest = jsonMapper.convertValue(params, new TypeRef<>() { }); var resourceUri = resourceRequest.uri(); @@ -630,7 +641,7 @@ public Mono addPrompt(McpStatelessServerFeatures.AsyncPromptSpecification * List all registered prompts. * @return A Flux stream of all registered prompts */ - public Flux listPrompts() { + public Flux listPrompts() { return Flux.fromIterable(this.prompts.values()) .map(McpStatelessServerFeatures.AsyncPromptSpecification::prompt); } @@ -663,7 +674,7 @@ public Mono removePrompt(String promptName) { }); } - private McpStatelessRequestHandler promptsListRequestHandler() { + private McpStatelessRequestHandler promptsListRequestHandler() { return (ctx, params) -> { // TODO: Implement pagination // McpSchema.PaginatedRequest request = objectMapper.convertValue(params, @@ -675,15 +686,14 @@ private McpStatelessRequestHandler promptsListReque .map(McpStatelessServerFeatures.AsyncPromptSpecification::prompt) .toList(); - return Mono.just(new McpSchema.ListPromptsResult(promptList, null)); + return Mono.just(new ListPromptsResult(promptList, null)); }; } - private McpStatelessRequestHandler promptsGetRequestHandler() { + private McpStatelessRequestHandler promptsGetRequestHandler() { return (ctx, params) -> { - McpSchema.GetPromptRequest promptRequest = jsonMapper.convertValue(params, - new TypeRef() { - }); + GetPromptRequest promptRequest = jsonMapper.convertValue(params, new TypeRef() { + }); // Implement prompt retrieval logic here McpStatelessServerFeatures.AsyncPromptSpecification specification = this.prompts.get(promptRequest.name()); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java index a15681ba5..8fb125f89 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java @@ -12,7 +12,16 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.Utils; import reactor.core.publisher.Mono; @@ -196,8 +205,8 @@ record Sync(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities se * @param callHandler The function that implements the tool's logic, receiving a * {@link CallToolRequest} and returning the result. */ - public record AsyncToolSpecification(McpSchema.Tool tool, - BiFunction> callHandler) { + public record AsyncToolSpecification(Tool tool, + BiFunction> callHandler) { static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec) { return fromSync(syncToolSpec, false); @@ -210,8 +219,7 @@ static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec, boole return null; } - BiFunction> callHandler = (ctx, - req) -> { + BiFunction> callHandler = (ctx, req) -> { var toolResult = Mono.fromCallable(() -> syncToolSpec.callHandler().apply(ctx, req)); return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic()); }; @@ -224,9 +232,9 @@ static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec, boole */ public static class Builder { - private McpSchema.Tool tool; + private Tool tool; - private BiFunction> callHandler; + private BiFunction> callHandler; /** * Sets the tool definition. @@ -234,7 +242,7 @@ public static class Builder { * schema * @return this builder instance */ - public Builder tool(McpSchema.Tool tool) { + public Builder tool(Tool tool) { this.tool = tool; return this; } @@ -245,7 +253,7 @@ public Builder tool(McpSchema.Tool tool) { * @return this builder instance */ public Builder callHandler( - BiFunction> callHandler) { + BiFunction> callHandler) { this.callHandler = callHandler; return this; } @@ -286,10 +294,10 @@ public static Builder builder() { * * @param resource The resource definition including name, description, and MIME type * @param readHandler The function that handles resource read requests. The function's - * argument is a {@link McpSchema.ReadResourceRequest}. + * argument is a {@link ReadResourceRequest}. */ - public record AsyncResourceSpecification(McpSchema.Resource resource, - BiFunction> readHandler) { + public record AsyncResourceSpecification(Resource resource, + BiFunction> readHandler) { static AsyncResourceSpecification fromSync(SyncResourceSpecification resource, boolean immediateExecution) { // FIXME: This is temporary, proper validation should be implemented @@ -323,12 +331,11 @@ static AsyncResourceSpecification fromSync(SyncResourceSpecification resource, b * description, and parameter schema * @param readHandler The function that handles resource read requests. The function's * first argument is an {@link McpTransportContext} upon which the server can interact - * with the connected client. The second arguments is a - * {@link McpSchema.ReadResourceRequest}. {@link McpSchema.ResourceTemplate} - * {@link McpSchema.ReadResourceResult} + * with the connected client. The second arguments is a {@link ReadResourceRequest}. + * {@link ResourceTemplate} {@link ReadResourceResult} */ - public record AsyncResourceTemplateSpecification(McpSchema.ResourceTemplate resourceTemplate, - BiFunction> readHandler) { + public record AsyncResourceTemplateSpecification(ResourceTemplate resourceTemplate, + BiFunction> readHandler) { static AsyncResourceTemplateSpecification fromSync(SyncResourceTemplateSpecification resource, boolean immediateExecution) { @@ -356,11 +363,10 @@ static AsyncResourceTemplateSpecification fromSync(SyncResourceTemplateSpecifica * * @param prompt The prompt definition including name and description * @param promptHandler The function that processes prompt requests and returns - * formatted templates. The function's argument is a - * {@link McpSchema.GetPromptRequest}. + * formatted templates. The function's argument is a {@link GetPromptRequest}. */ - public record AsyncPromptSpecification(McpSchema.Prompt prompt, - BiFunction> promptHandler) { + public record AsyncPromptSpecification(Prompt prompt, + BiFunction> promptHandler) { static AsyncPromptSpecification fromSync(SyncPromptSpecification prompt, boolean immediateExecution) { // FIXME: This is temporary, proper validation should be implemented @@ -421,8 +427,8 @@ static AsyncCompletionSpecification fromSync(SyncCompletionSpecification complet * @param callHandler The function that implements the tool's logic, receiving a * {@link CallToolRequest} and returning results. */ - public record SyncToolSpecification(McpSchema.Tool tool, - BiFunction callHandler) { + public record SyncToolSpecification(Tool tool, + BiFunction callHandler) { public static Builder builder() { return new Builder(); @@ -433,9 +439,9 @@ public static Builder builder() { */ public static class Builder { - private McpSchema.Tool tool; + private Tool tool; - private BiFunction callHandler; + private BiFunction callHandler; /** * Sets the tool definition. @@ -443,7 +449,7 @@ public static class Builder { * schema * @return this builder instance */ - public Builder tool(McpSchema.Tool tool) { + public Builder tool(Tool tool) { this.tool = tool; return this; } @@ -453,8 +459,7 @@ public Builder tool(McpSchema.Tool tool) { * @param callHandler The function that implements the tool's logic * @return this builder instance */ - public Builder callHandler( - BiFunction callHandler) { + public Builder callHandler(BiFunction callHandler) { this.callHandler = callHandler; return this; } @@ -487,10 +492,10 @@ public SyncToolSpecification build() { * * @param resource The resource definition including name, description, and MIME type * @param readHandler The function that handles resource read requests. The function's - * argument is a {@link McpSchema.ReadResourceRequest}. + * argument is a {@link ReadResourceRequest}. */ - public record SyncResourceSpecification(McpSchema.Resource resource, - BiFunction readHandler) { + public record SyncResourceSpecification(Resource resource, + BiFunction readHandler) { } /** @@ -513,12 +518,11 @@ public record SyncResourceSpecification(McpSchema.Resource resource, * description, and parameter schema * @param readHandler The function that handles resource read requests. The function's * first argument is an {@link McpTransportContext} upon which the server can interact - * with the connected client. The second arguments is a - * {@link McpSchema.ReadResourceRequest}. {@link McpSchema.ResourceTemplate} - * {@link McpSchema.ReadResourceResult} + * with the connected client. The second arguments is a {@link ReadResourceRequest}. + * {@link ResourceTemplate} {@link ReadResourceResult} */ - public record SyncResourceTemplateSpecification(McpSchema.ResourceTemplate resourceTemplate, - BiFunction readHandler) { + public record SyncResourceTemplateSpecification(ResourceTemplate resourceTemplate, + BiFunction readHandler) { } /** @@ -534,11 +538,10 @@ public record SyncResourceTemplateSpecification(McpSchema.ResourceTemplate resou * * @param prompt The prompt definition including name and description * @param promptHandler The function that processes prompt requests and returns - * formatted templates. The function's argument is a - * {@link McpSchema.GetPromptRequest}. + * formatted templates. The function's argument is a {@link GetPromptRequest}. */ - public record SyncPromptSpecification(McpSchema.Prompt prompt, - BiFunction promptHandler) { + public record SyncPromptSpecification(Prompt prompt, + BiFunction promptHandler) { } /** diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerHandler.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerHandler.java index cbae58bfd..54aa139ad 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerHandler.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerHandler.java @@ -5,7 +5,9 @@ package io.modelcontextprotocol.server; import io.modelcontextprotocol.common.McpTransportContext; -import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import reactor.core.publisher.Mono; /** @@ -23,8 +25,7 @@ public interface McpStatelessServerHandler { * @param request the request JSON object * @return Mono containing the JSON response */ - Mono handleRequest(McpTransportContext transportContext, - McpSchema.JSONRPCRequest request); + Mono handleRequest(McpTransportContext transportContext, JSONRPCRequest request); /** * Handle the notification. @@ -33,6 +34,6 @@ Mono handleRequest(McpTransportContext transportConte * @param notification the notification JSON object * @return Mono that completes once handling is finished */ - Mono handleNotification(McpTransportContext transportContext, McpSchema.JSONRPCNotification notification); + Mono handleNotification(McpTransportContext transportContext, JSONRPCNotification notification); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessSyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessSyncServer.java index 6849eb8ed..c75b8a44c 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessSyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessSyncServer.java @@ -5,6 +5,10 @@ package io.modelcontextprotocol.server; import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; @@ -78,7 +82,7 @@ public void addTool(McpStatelessServerFeatures.SyncToolSpecification toolSpecifi * List all registered tools. * @return A list of all registered tools */ - public List listTools() { + public List listTools() { return this.asyncServer.listTools().collectList().block(); } @@ -105,7 +109,7 @@ public void addResource(McpStatelessServerFeatures.SyncResourceSpecification res * List all registered resources. * @return A list of all registered resources */ - public List listResources() { + public List listResources() { return this.asyncServer.listResources().collectList().block(); } @@ -133,7 +137,7 @@ public void addResourceTemplate( * List all registered resource templates. * @return A list of all registered resource templates */ - public List listResourceTemplates() { + public List listResourceTemplates() { return this.asyncServer.listResourceTemplates().collectList().block(); } @@ -160,7 +164,7 @@ public void addPrompt(McpStatelessServerFeatures.SyncPromptSpecification promptS * List all registered prompts. * @return A list of all registered prompts */ - public List listPrompts() { + public List listPrompts() { return this.asyncServer.listPrompts().collectList().block(); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServer.java index d33299d02..e7b07d205 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServer.java @@ -7,7 +7,10 @@ import java.util.List; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.Assert; /** @@ -93,7 +96,7 @@ public void addTool(McpServerFeatures.SyncToolSpecification toolHandler) { * List all registered tools. * @return A list of all registered tools */ - public List listTools() { + public List listTools() { return this.asyncServer.listTools().collectList().block(); } @@ -120,7 +123,7 @@ public void addResource(McpServerFeatures.SyncResourceSpecification resourceSpec * List all registered resources. * @return A list of all registered resources */ - public List listResources() { + public List listResources() { return this.asyncServer.listResources().collectList().block(); } @@ -147,7 +150,7 @@ public void addResourceTemplate(McpServerFeatures.SyncResourceTemplateSpecificat * List all registered resource templates. * @return A list of all registered resource templates */ - public List listResourceTemplates() { + public List listResourceTemplates() { return this.asyncServer.listResourceTemplates().collectList().block(); } @@ -174,7 +177,7 @@ public void addPrompt(McpServerFeatures.SyncPromptSpecification promptSpecificat * List all registered prompts. * @return A list of all registered prompts */ - public List listPrompts() { + public List listPrompts() { return this.asyncServer.listPrompts().collectList().block(); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServerExchange.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServerExchange.java index 0b9115b79..7ae519828 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServerExchange.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServerExchange.java @@ -7,6 +7,10 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; /** * Represents a synchronous exchange with a Model Context Protocol (MCP) client. The @@ -72,13 +76,13 @@ public McpTransportContext transportContext() { * include context from MCP servers in their prompts. * @param createMessageRequest The request to create a new message * @return A result containing the details of the sampling response - * @see McpSchema.CreateMessageRequest - * @see McpSchema.CreateMessageResult + * @see CreateMessageRequest + * @see CreateMessageResult * @see Sampling * Specification */ - public McpSchema.CreateMessageResult createMessage(McpSchema.CreateMessageRequest createMessageRequest) { + public CreateMessageResult createMessage(CreateMessageRequest createMessageRequest) { return this.exchange.createMessage(createMessageRequest).block(); } @@ -90,13 +94,13 @@ public McpSchema.CreateMessageResult createMessage(McpSchema.CreateMessageReques * structured data from users with optional JSON schemas to validate responses. * @param elicitRequest The request to create a new elicitation * @return A result containing the elicitation response. - * @see McpSchema.ElicitRequest - * @see McpSchema.ElicitResult + * @see ElicitRequest + * @see ElicitResult * @see Elicitation * Specification */ - public McpSchema.ElicitResult createElicitation(McpSchema.ElicitRequest elicitRequest) { + public ElicitResult createElicitation(ElicitRequest elicitRequest) { return this.exchange.createElicitation(elicitRequest).block(); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java index d3648a06f..926b940cb 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java @@ -25,6 +25,8 @@ import io.modelcontextprotocol.spec.McpServerTransport; import io.modelcontextprotocol.spec.McpServerTransportProvider; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.KeepAliveScheduler; import jakarta.servlet.AsyncContext; @@ -395,7 +397,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } final McpTransportContext transportContext = this.contextExtractor.extract(request); - McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(jsonMapper, body.toString()); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(jsonMapper, body.toString()); // Process the message through the session's handle method // Block for Servlet compatibility @@ -505,7 +507,7 @@ private class HttpServletMcpSessionTransport implements McpServerTransport { * @return A Mono that completes when the message has been sent */ @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { return Mono.fromRunnable(() -> { try { String jsonText = jsonMapper.writeValueAsString(message); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java index 047aeebe8..2c829aef4 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java @@ -22,6 +22,11 @@ import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpStatelessServerTransport; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import io.modelcontextprotocol.util.Assert; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; @@ -161,12 +166,11 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) body.append(line); } - McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(jsonMapper, body.toString()); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(jsonMapper, body.toString()); - if (message instanceof McpSchema.JSONRPCRequest jsonrpcRequest) { + if (message instanceof JSONRPCRequest jsonrpcRequest) { try { - McpSchema.JSONRPCResponse jsonrpcResponse = this.mcpHandler - .handleRequest(transportContext, jsonrpcRequest) + JSONRPCResponse jsonrpcResponse = this.mcpHandler.handleRequest(transportContext, jsonrpcRequest) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, transportContext)) .block(); @@ -187,7 +191,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) .build()); } } - else if (message instanceof McpSchema.JSONRPCNotification jsonrpcNotification) { + else if (message instanceof JSONRPCNotification jsonrpcNotification) { try { this.mcpHandler.handleNotification(transportContext, jsonrpcNotification) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, transportContext)) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java index 95edb63a0..9367e4231 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java @@ -28,6 +28,11 @@ import io.modelcontextprotocol.spec.McpStreamableServerTransport; import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; @@ -436,10 +441,10 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) body.append(line); } - McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(jsonMapper, body.toString()); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(jsonMapper, body.toString()); // Handle initialization request - if (message instanceof McpSchema.JSONRPCRequest jsonrpcRequest + if (message instanceof JSONRPCRequest jsonrpcRequest && jsonrpcRequest.method().equals(McpSchema.METHOD_INITIALIZE)) { if (!badRequestErrors.isEmpty()) { String combinedMessage = String.join("; ", badRequestErrors); @@ -463,8 +468,8 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) response.setHeader(HttpHeaders.MCP_SESSION_ID, init.session().getId()); response.setStatus(HttpServletResponse.SC_OK); - String jsonResponse = jsonMapper.writeValueAsString(new McpSchema.JSONRPCResponse( - McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), initResult, null)); + String jsonResponse = jsonMapper.writeValueAsString( + new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, jsonrpcRequest.id(), initResult, null)); PrintWriter writer = response.getWriter(); writer.write(jsonResponse); @@ -504,19 +509,19 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) return; } - if (message instanceof McpSchema.JSONRPCResponse jsonrpcResponse) { + if (message instanceof JSONRPCResponse jsonrpcResponse) { session.accept(jsonrpcResponse) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, transportContext)) .block(); response.setStatus(HttpServletResponse.SC_ACCEPTED); } - else if (message instanceof McpSchema.JSONRPCNotification jsonrpcNotification) { + else if (message instanceof JSONRPCNotification jsonrpcNotification) { session.accept(jsonrpcNotification) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, transportContext)) .block(); response.setStatus(HttpServletResponse.SC_ACCEPTED); } - else if (message instanceof McpSchema.JSONRPCRequest jsonrpcRequest) { + else if (message instanceof JSONRPCRequest jsonrpcRequest) { // For streaming responses, we need to return SSE response.setContentType(TEXT_EVENT_STREAM); response.setCharacterEncoding(UTF_8); @@ -724,7 +729,7 @@ private class HttpServletStreamableMcpSessionTransport implements McpStreamableS * @return A Mono that completes when the message has been sent */ @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { return sendMessage(message, null); } @@ -736,7 +741,7 @@ public Mono sendMessage(McpSchema.JSONRPCMessage message) { * @return A Mono that completes when the message has been sent */ @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message, String messageId) { + public Mono sendMessage(JSONRPCMessage message, String messageId) { return Mono.fromRunnable(() -> { if (this.closed) { logger.debug("Attempted to send message to closed session: {}", this.sessionId); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java index 66cc304d6..f4d4cd1a0 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java @@ -17,12 +17,12 @@ import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.McpError; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage; import io.modelcontextprotocol.spec.McpServerSession; import io.modelcontextprotocol.spec.McpServerTransport; import io.modelcontextprotocol.spec.McpServerTransportProvider; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.json.McpJsonMapper; import org.slf4j.Logger; @@ -158,7 +158,7 @@ public StdioMcpSessionTransport() { } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { return Mono.zip(inboundReady.asMono(), outboundReady.asMono()).then(Mono.defer(() -> { if (outboundSink.tryEmitNext(message).isSuccess()) { @@ -225,8 +225,7 @@ private void startInboundProcessing() { logger.debug("Received JSON message: {}", line); try { - McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(jsonMapper, - line); + JSONRPCMessage message = JSONRPC.deserializeJsonRpcMessage(jsonMapper, line); if (!this.inboundSink.tryEmitNext(message).isSuccess()) { // logIfNotClosing("Failed to enqueue message"); break; diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/ClosedMcpTransportSession.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/ClosedMcpTransportSession.java index b18364abb..23e32b40a 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/ClosedMcpTransportSession.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/ClosedMcpTransportSession.java @@ -1,6 +1,7 @@ /* * Copyright 2025-2025 the original author or authors. */ + package io.modelcontextprotocol.spec; import java.util.Optional; diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/DefaultMcpTransportStream.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/DefaultMcpTransportStream.java index 8d63fb50d..01bd3df76 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/DefaultMcpTransportStream.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/DefaultMcpTransportStream.java @@ -16,6 +16,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; + /** * An implementation of {@link McpTransportStream} using Project Reactor types. * @@ -63,8 +65,8 @@ public long streamId() { } @Override - public Publisher consumeSseStream( - Publisher, Iterable>> eventStream) { + public Publisher consumeSseStream( + Publisher, Iterable>> eventStream) { // @formatter:off return Flux.deferContextual(ctx -> Flux.from(eventStream) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientSession.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientSession.java index 80b5ae246..8902aa41e 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientSession.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientSession.java @@ -5,6 +5,11 @@ package io.modelcontextprotocol.spec; import io.modelcontextprotocol.json.TypeRef; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import io.modelcontextprotocol.util.Assert; import org.reactivestreams.Publisher; import org.slf4j.Logger; @@ -48,7 +53,7 @@ public class McpClientSession implements McpSession { private final McpClientTransport transport; /** Map of pending responses keyed by request ID */ - private final ConcurrentHashMap> pendingResponses = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> pendingResponses = new ConcurrentHashMap<>(); /** Map of request handlers keyed by method name */ private final ConcurrentHashMap> requestHandlers = new ConcurrentHashMap<>(); @@ -130,8 +135,8 @@ private void dismissPendingResponses() { this.pendingResponses.clear(); } - private void handle(McpSchema.JSONRPCMessage message) { - if (message instanceof McpSchema.JSONRPCResponse response) { + private void handle(JSONRPCMessage message) { + if (message instanceof JSONRPCResponse response) { logger.debug("Received response: {}", response); if (response.id() != null) { var sink = pendingResponses.remove(response.id()); @@ -148,25 +153,24 @@ private void handle(McpSchema.JSONRPCMessage message) { + "leaks as pending requests will never be completed."); } } - else if (message instanceof McpSchema.JSONRPCRequest request) { + else if (message instanceof JSONRPCRequest request) { logger.debug("Received request: {}", request); handleIncomingRequest(request).onErrorResume(error -> { - McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError + JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError && mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError() // TODO: add error message through the data field - : new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, + : new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, error.getMessage(), McpError.aggregateExceptionMessages(error)); - var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, - jsonRpcError); + var errorResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, jsonRpcError); return Mono.just(errorResponse); }).flatMap(this.transport::sendMessage).onErrorComplete(t -> { logger.warn("Issue sending response to the client, ", t); return true; }).subscribe(); } - else if (message instanceof McpSchema.JSONRPCNotification notification) { + else if (message instanceof JSONRPCNotification notification) { logger.debug("Received notification: {}", notification); handleIncomingNotification(notification).onErrorComplete(t -> { logger.error("Error handling notification: {}", t.getMessage()); @@ -183,18 +187,18 @@ else if (message instanceof McpSchema.JSONRPCNotification notification) { * @param request The incoming JSON-RPC request * @return A Mono containing the JSON-RPC response */ - private Mono handleIncomingRequest(McpSchema.JSONRPCRequest request) { + private Mono handleIncomingRequest(JSONRPCRequest request) { return Mono.defer(() -> { var handler = this.requestHandlers.get(request.method()); if (handler == null) { MethodNotFoundError error = getMethodNotFoundError(request.method()); - return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, - new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, - error.message(), error.data()))); + return Mono.just(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, + new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, error.message(), + error.data()))); } return handler.handle(request.params()) - .map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null)); + .map(result -> new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), result, null)); }); } @@ -216,7 +220,7 @@ private MethodNotFoundError getMethodNotFoundError(String method) { * @param notification The incoming JSON-RPC notification * @return A Mono that completes when the notification is processed */ - private Mono handleIncomingNotification(McpSchema.JSONRPCNotification notification) { + private Mono handleIncomingNotification(JSONRPCNotification notification) { return Mono.defer(() -> { var handler = notificationHandlers.get(notification.method()); if (handler == null) { @@ -248,11 +252,11 @@ private String generateRequestId() { public Mono sendRequest(String method, Object requestParams, TypeRef typeRef) { String requestId = this.generateRequestId(); - return Mono.deferContextual(ctx -> Mono.create(pendingResponseSink -> { + return Mono.deferContextual(ctx -> Mono.create(pendingResponseSink -> { logger.debug("Sending message for method {}", method); this.pendingResponses.put(requestId, pendingResponseSink); - McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method, - requestId, requestParams); + JSONRPCRequest jsonrpcRequest = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, method, requestId, + requestParams); this.transport.sendMessage(jsonrpcRequest).contextWrite(ctx).subscribe(v -> { }, error -> { this.pendingResponses.remove(requestId); @@ -282,8 +286,7 @@ public Mono sendRequest(String method, Object requestParams, TypeRef t */ @Override public Mono sendNotification(String method, Object params) { - McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, - method, params); + JSONRPCNotification jsonrpcNotification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, method, params); return this.transport.sendMessage(jsonrpcNotification); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientTransport.java index 22aec831b..0bf339154 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpClientTransport.java @@ -7,6 +7,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import reactor.core.publisher.Mono; /** @@ -29,7 +30,7 @@ public interface McpClientTransport extends McpTransport { * {@link Mono} simply means the client can now be used. An error can be retried * according to the application requirements. */ - Mono connect(Function, Mono> handler); + Mono connect(Function, Mono> handler); /** * Sets the exception handler for exceptions raised on the transport layer. diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpError.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpError.java index a3e7890e6..b9dd99aa1 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpError.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpError.java @@ -4,7 +4,7 @@ package io.modelcontextprotocol.spec; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse.JSONRPCError; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse.JSONRPCError; import io.modelcontextprotocol.util.Assert; import java.util.Map; diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index bb9cead7e..c91ba736a 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -4,21 +4,20 @@ package io.modelcontextprotocol.spec; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; +import io.modelcontextprotocol.spec.schema.resource.Annotated; +import io.modelcontextprotocol.spec.schema.resource.Identifier; +import io.modelcontextprotocol.spec.schema.resource.ResourceContent; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; import io.modelcontextprotocol.util.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,13 +35,11 @@ */ public final class McpSchema { - private static final Logger logger = LoggerFactory.getLogger(McpSchema.class); + public static final Logger logger = LoggerFactory.getLogger(McpSchema.class); private McpSchema() { } - public static final String JSONRPC_VERSION = "2.0"; - public static final String FIRST_PAGE = null; // --------------------------- @@ -160,9 +157,7 @@ public interface Meta { } - public sealed interface Request extends Meta - permits InitializeRequest, CallToolRequest, CreateMessageRequest, ElicitRequest, CompleteRequest, - GetPromptRequest, ReadResourceRequest, SubscribeRequest, UnsubscribeRequest, PaginatedRequest { + public interface Request extends Meta { default Object progressToken() { if (meta() != null && meta().containsKey("progressToken")) { @@ -173,142 +168,17 @@ default Object progressToken() { } - public sealed interface Result extends Meta permits InitializeResult, ListResourcesResult, - ListResourceTemplatesResult, ReadResourceResult, ListPromptsResult, GetPromptResult, ListToolsResult, - CallToolResult, CreateMessageResult, ElicitResult, CompleteResult, ListRootsResult { + public interface Result extends Meta { } - public sealed interface Notification extends Meta - permits ProgressNotification, LoggingMessageNotification, ResourcesUpdatedNotification { + public interface Notification extends Meta { } - private static final TypeRef> MAP_TYPE_REF = new TypeRef<>() { + public static final TypeRef> MAP_TYPE_REF = new TypeRef<>() { }; - /** - * Deserializes a JSON string into a JSONRPCMessage object. - * @param jsonMapper The JsonMapper instance to use for deserialization - * @param jsonText The JSON string to deserialize - * @return A JSONRPCMessage instance using either the {@link JSONRPCRequest}, - * {@link JSONRPCNotification}, or {@link JSONRPCResponse} classes. - * @throws IOException If there's an error during deserialization - * @throws IllegalArgumentException If the JSON structure doesn't match any known - * message type - */ - public static JSONRPCMessage deserializeJsonRpcMessage(McpJsonMapper jsonMapper, String jsonText) - throws IOException { - - logger.debug("Received JSON message: {}", jsonText); - - var map = jsonMapper.readValue(jsonText, MAP_TYPE_REF); - - // Determine message type based on specific JSON structure - if (map.containsKey("method") && map.containsKey("id")) { - return jsonMapper.convertValue(map, JSONRPCRequest.class); - } - else if (map.containsKey("method") && !map.containsKey("id")) { - return jsonMapper.convertValue(map, JSONRPCNotification.class); - } - else if (map.containsKey("result") || map.containsKey("error")) { - return jsonMapper.convertValue(map, JSONRPCResponse.class); - } - - throw new IllegalArgumentException("Cannot deserialize JSONRPCMessage: " + jsonText); - } - - // --------------------------- - // JSON-RPC Message Types - // --------------------------- - public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCNotification, JSONRPCResponse { - - String jsonrpc(); - - } - - /** - * A request that expects a response. - * - * @param jsonrpc The JSON-RPC version (must be "2.0") - * @param method The name of the method to be invoked - * @param id A unique identifier for the request - * @param params Parameters for the method call - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - // @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - public record JSONRPCRequest( // @formatter:off - @JsonProperty("jsonrpc") String jsonrpc, - @JsonProperty("method") String method, - @JsonProperty("id") Object id, - @JsonProperty("params") Object params) implements JSONRPCMessage { // @formatter:on - - /** - * Constructor that validates MCP-specific ID requirements. Unlike base JSON-RPC, - * MCP requires that: (1) Requests MUST include a string or integer ID; (2) The ID - * MUST NOT be null - */ - public JSONRPCRequest { - Assert.notNull(id, "MCP requests MUST include an ID - null IDs are not allowed"); - Assert.isTrue(id instanceof String || id instanceof Integer || id instanceof Long, - "MCP requests MUST have an ID that is either a string or integer"); - } - } - - /** - * A notification which does not expect a response. - * - * @param jsonrpc The JSON-RPC version (must be "2.0") - * @param method The name of the method being notified - * @param params Parameters for the notification - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - // TODO: batching support - // @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - public record JSONRPCNotification( // @formatter:off - @JsonProperty("jsonrpc") String jsonrpc, - @JsonProperty("method") String method, - @JsonProperty("params") Object params) implements JSONRPCMessage { // @formatter:on - } - - /** - * A response to a request (successful, or error). - * - * @param jsonrpc The JSON-RPC version (must be "2.0") - * @param id The request identifier that this response corresponds to - * @param result The result of the successful request; null if error - * @param error Error information if the request failed; null if has result - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - // TODO: batching support - // @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - public record JSONRPCResponse( // @formatter:off - @JsonProperty("jsonrpc") String jsonrpc, - @JsonProperty("id") Object id, - @JsonProperty("result") Object result, - @JsonProperty("error") JSONRPCError error) implements JSONRPCMessage { // @formatter:on - - /** - * A response to a request that indicates an error occurred. - * - * @param code The error type that occurred - * @param message A short description of the error. The message SHOULD be limited - * to a concise single sentence - * @param data Additional information about the error. The value of this member is - * defined by the sender (e.g. detailed error information, nested errors etc.) - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record JSONRPCError( // @formatter:off - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { // @formatter:on - } - } - // --------------------------- // Initialization // --------------------------- @@ -683,19 +553,6 @@ public enum Role { @JsonProperty("assistant") ASSISTANT } // @formatter:on - // --------------------------- - // Resource Interfaces - // --------------------------- - /** - * Base for objects that include optional annotations for the client. The client can - * use annotations to inform how objects are used or displayed - */ - public interface Annotated { - - Annotations annotations(); - - } - /** * Optional annotations for the client. The client can use annotations to inform how * objects are used or displayed. @@ -721,1438 +578,6 @@ public Annotations(List audience, Double priority) { } } - /** - * A common interface for resource content, which includes metadata about the resource - * such as its URI, name, description, MIME type, size, and annotations. This - * interface is implemented by both {@link Resource} and {@link ResourceLink} to - * provide a consistent way to access resource metadata. - */ - public interface ResourceContent extends Identifier, Annotated, Meta { - - // name & title from Identifier - - String uri(); - - String description(); - - String mimeType(); - - Long size(); - - // annotations from Annotated - // meta from Meta - - } - - /** - * Base interface with name (identifier) and title (display name) properties. - */ - public interface Identifier { - - /** - * Intended for programmatic or logical use, but used as a display name in past - * specs or fallback (if title isn't present). - */ - String name(); - - /** - * Intended for UI and end-user contexts — optimized to be human-readable and - * easily understood, even by those unfamiliar with domain-specific terminology. - * - * If not provided, the name should be used for display. - */ - String title(); - - } - - /** - * A known resource that the server is capable of reading. - * - * @param uri the URI of the resource. - * @param name A human-readable name for this resource. This can be used by clients to - * populate UI elements. - * @param title An optional title for this resource. - * @param description A description of what this resource represents. This can be used - * by clients to improve the LLM's understanding of available resources. It can be - * thought of like a "hint" to the model. - * @param mimeType The MIME type of this resource, if known. - * @param size The size of the raw resource content, in bytes (i.e., before base64 - * encoding or any tokenization), if known. This can be used by Hosts to display file - * sizes and estimate context window usage. - * @param annotations Optional annotations for the client. The client can use - * annotations to inform how objects are used or displayed. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record Resource( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("name") String name, - @JsonProperty("title") String title, - @JsonProperty("description") String description, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("size") Long size, - @JsonProperty("annotations") Annotations annotations, - @JsonProperty("_meta") Map meta) implements ResourceContent { // @formatter:on - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private String uri; - - private String name; - - private String title; - - private String description; - - private String mimeType; - - private Long size; - - private Annotations annotations; - - private Map meta; - - public Builder uri(String uri) { - this.uri = uri; - return this; - } - - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder title(String title) { - this.title = title; - return this; - } - - public Builder description(String description) { - this.description = description; - return this; - } - - public Builder mimeType(String mimeType) { - this.mimeType = mimeType; - return this; - } - - public Builder size(Long size) { - this.size = size; - return this; - } - - public Builder annotations(Annotations annotations) { - this.annotations = annotations; - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public Resource build() { - Assert.hasText(uri, "uri must not be empty"); - Assert.hasText(name, "name must not be empty"); - - return new Resource(uri, name, title, description, mimeType, size, annotations, meta); - } - - } - } - - /** - * Resource templates allow servers to expose parameterized resources using URI - * - * @param uriTemplate A URI template that can be used to generate URIs for this - * resource. - * @param name A human-readable name for this resource. This can be used by clients to - * populate UI elements. - * @param title An optional title for this resource. - * @param description A description of what this resource represents. This can be used - * by clients to improve the LLM's understanding of available resources. It can be - * thought of like a "hint" to the model. - * @param mimeType The MIME type of this resource, if known. - * @param annotations Optional annotations for the client. The client can use - * annotations to inform how objects are used or displayed. - * @see RFC 6570 - * @param meta See specification for notes on _meta usage - * - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ResourceTemplate( // @formatter:off - @JsonProperty("uriTemplate") String uriTemplate, - @JsonProperty("name") String name, - @JsonProperty("title") String title, - @JsonProperty("description") String description, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("annotations") Annotations annotations, - @JsonProperty("_meta") Map meta) implements Annotated, Identifier, Meta { // @formatter:on - - public ResourceTemplate(String uriTemplate, String name, String title, String description, String mimeType, - Annotations annotations) { - this(uriTemplate, name, title, description, mimeType, annotations, null); - } - - public ResourceTemplate(String uriTemplate, String name, String description, String mimeType, - Annotations annotations) { - this(uriTemplate, name, null, description, mimeType, annotations); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private String uriTemplate; - - private String name; - - private String title; - - private String description; - - private String mimeType; - - private Annotations annotations; - - private Map meta; - - public Builder uriTemplate(String uri) { - this.uriTemplate = uri; - return this; - } - - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder title(String title) { - this.title = title; - return this; - } - - public Builder description(String description) { - this.description = description; - return this; - } - - public Builder mimeType(String mimeType) { - this.mimeType = mimeType; - return this; - } - - public Builder annotations(Annotations annotations) { - this.annotations = annotations; - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public ResourceTemplate build() { - Assert.hasText(uriTemplate, "uri must not be empty"); - Assert.hasText(name, "name must not be empty"); - - return new ResourceTemplate(uriTemplate, name, title, description, mimeType, annotations, meta); - } - - } - } - - /** - * The server's response to a resources/list request from the client. - * - * @param resources A list of resources that the server provides - * @param nextCursor An opaque token representing the pagination position after the - * last returned result. If present, there may be more results available - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ListResourcesResult( // @formatter:off - @JsonProperty("resources") List resources, - @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public ListResourcesResult(List resources, String nextCursor) { - this(resources, nextCursor, null); - } - } - - /** - * The server's response to a resources/templates/list request from the client. - * - * @param resourceTemplates A list of resource templates that the server provides - * @param nextCursor An opaque token representing the pagination position after the - * last returned result. If present, there may be more results available - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ListResourceTemplatesResult( // @formatter:off - @JsonProperty("resourceTemplates") List resourceTemplates, - @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public ListResourceTemplatesResult(List resourceTemplates, String nextCursor) { - this(resourceTemplates, nextCursor, null); - } - } - - /** - * Sent from the client to the server, to read a specific resource URI. - * - * @param uri The URI of the resource to read. The URI can use any protocol; it is up - * to the server how to interpret it - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ReadResourceRequest( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - public ReadResourceRequest(String uri) { - this(uri, null); - } - } - - /** - * The server's response to a resources/read request from the client. - * - * @param contents The contents of the resource - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ReadResourceResult( // @formatter:off - @JsonProperty("contents") List contents, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public ReadResourceResult(List contents) { - this(contents, null); - } - } - - /** - * Sent from the client to request resources/updated notifications from the server - * whenever a particular resource changes. - * - * @param uri the URI of the resource to subscribe to. The URI can use any protocol; - * it is up to the server how to interpret it. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record SubscribeRequest( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - public SubscribeRequest(String uri) { - this(uri, null); - } - } - - /** - * Sent from the client to request cancellation of resources/updated notifications - * from the server. This should follow a previous resources/subscribe request. - * - * @param uri The URI of the resource to unsubscribe from - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record UnsubscribeRequest( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - public UnsubscribeRequest(String uri) { - this(uri, null); - } - } - - /** - * The contents of a specific resource or sub-resource. - */ - @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) - @JsonSubTypes({ @JsonSubTypes.Type(value = TextResourceContents.class), - @JsonSubTypes.Type(value = BlobResourceContents.class) }) - public sealed interface ResourceContents extends Meta permits TextResourceContents, BlobResourceContents { - - /** - * The URI of this resource. - * @return the URI of this resource. - */ - String uri(); - - /** - * The MIME type of this resource. - * @return the MIME type of this resource. - */ - String mimeType(); - - } - - /** - * Text contents of a resource. - * - * @param uri the URI of this resource. - * @param mimeType the MIME type of this resource. - * @param text the text of the resource. This must only be set if the resource can - * actually be represented as text (not binary data). - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record TextResourceContents( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("text") String text, - @JsonProperty("_meta") Map meta) implements ResourceContents { // @formatter:on - - public TextResourceContents(String uri, String mimeType, String text) { - this(uri, mimeType, text, null); - } - } - - /** - * Binary contents of a resource. - * - * @param uri the URI of this resource. - * @param mimeType the MIME type of this resource. - * @param blob a base64-encoded string representing the binary data of the resource. - * This must only be set if the resource can actually be represented as binary data - * (not text). - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record BlobResourceContents( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("blob") String blob, - @JsonProperty("_meta") Map meta) implements ResourceContents { // @formatter:on - - public BlobResourceContents(String uri, String mimeType, String blob) { - this(uri, mimeType, blob, null); - } - } - - // --------------------------- - // Prompt Interfaces - // --------------------------- - /** - * A prompt or prompt template that the server offers. - * - * @param name The name of the prompt or prompt template. - * @param title An optional title for the prompt. - * @param description An optional description of what this prompt provides. - * @param arguments A list of arguments to use for templating the prompt. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record Prompt( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("title") String title, - @JsonProperty("description") String description, - @JsonProperty("arguments") List arguments, - @JsonProperty("_meta") Map meta) implements Identifier { // @formatter:on - - public Prompt(String name, String description, List arguments) { - this(name, null, description, arguments != null ? arguments : new ArrayList<>()); - } - - public Prompt(String name, String title, String description, List arguments) { - this(name, title, description, arguments != null ? arguments : new ArrayList<>(), null); - } - } - - /** - * Describes an argument that a prompt can accept. - * - * @param name The name of the argument. - * @param title An optional title for the argument, which can be used in UI - * @param description A human-readable description of the argument. - * @param required Whether this argument must be provided. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record PromptArgument( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("title") String title, - @JsonProperty("description") String description, - @JsonProperty("required") Boolean required) implements Identifier { // @formatter:on - - public PromptArgument(String name, String description, Boolean required) { - this(name, null, description, required); - } - } - - /** - * Describes a message returned as part of a prompt. - * - * This is similar to `SamplingMessage`, but also supports the embedding of resources - * from the MCP server. - * - * @param role The sender or recipient of messages and data in a conversation. - * @param content The content of the message of type {@link Content}. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record PromptMessage( // @formatter:off - @JsonProperty("role") Role role, - @JsonProperty("content") Content content) { // @formatter:on - } - - /** - * The server's response to a prompts/list request from the client. - * - * @param prompts A list of prompts that the server provides. - * @param nextCursor An optional cursor for pagination. If present, indicates there - * are more prompts available. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ListPromptsResult( // @formatter:off - @JsonProperty("prompts") List prompts, - @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public ListPromptsResult(List prompts, String nextCursor) { - this(prompts, nextCursor, null); - } - } - - /** - * Used by the client to get a prompt provided by the server. - * - * @param name The name of the prompt or prompt template. - * @param arguments Arguments to use for templating the prompt. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record GetPromptRequest( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("arguments") Map arguments, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - public GetPromptRequest(String name, Map arguments) { - this(name, arguments, null); - } - } - - /** - * The server's response to a prompts/get request from the client. - * - * @param description An optional description for the prompt. - * @param messages A list of messages to display as part of the prompt. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record GetPromptResult( // @formatter:off - @JsonProperty("description") String description, - @JsonProperty("messages") List messages, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public GetPromptResult(String description, List messages) { - this(description, messages, null); - } - } - - // --------------------------- - // Tool Interfaces - // --------------------------- - /** - * The server's response to a tools/list request from the client. - * - * @param tools A list of tools that the server provides. - * @param nextCursor An optional cursor for pagination. If present, indicates there - * are more tools available. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ListToolsResult( // @formatter:off - @JsonProperty("tools") List tools, - @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public ListToolsResult(List tools, String nextCursor) { - this(tools, nextCursor, null); - } - } - - /** - * A JSON Schema object that describes the expected structure of arguments or output. - * - * @param type The type of the schema (e.g., "object") - * @param properties The properties of the schema object - * @param required List of required property names - * @param additionalProperties Whether additional properties are allowed - * @param defs Schema definitions using the newer $defs keyword - * @param definitions Schema definitions using the legacy definitions keyword - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record JsonSchema( // @formatter:off - @JsonProperty("type") String type, - @JsonProperty("properties") Map properties, - @JsonProperty("required") List required, - @JsonProperty("additionalProperties") Boolean additionalProperties, - @JsonProperty("$defs") Map defs, - @JsonProperty("definitions") Map definitions) { // @formatter:on - } - - /** - * Additional properties describing a Tool to clients. - * - * NOTE: all properties in ToolAnnotations are **hints**. They are not guaranteed to - * provide a faithful description of tool behavior (including descriptive properties - * like `title`). - * - * Clients should never make tool use decisions based on ToolAnnotations received from - * untrusted servers. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ToolAnnotations( // @formatter:off - @JsonProperty("title") String title, - @JsonProperty("readOnlyHint") Boolean readOnlyHint, - @JsonProperty("destructiveHint") Boolean destructiveHint, - @JsonProperty("idempotentHint") Boolean idempotentHint, - @JsonProperty("openWorldHint") Boolean openWorldHint, - @JsonProperty("returnDirect") Boolean returnDirect) { // @formatter:on - } - - /** - * Represents a tool that the server provides. Tools enable servers to expose - * executable functionality to the system. Through these tools, you can interact with - * external systems, perform computations, and take actions in the real world. - * - * @param name A unique identifier for the tool. This name is used when calling the - * tool. - * @param title A human-readable title for the tool. - * @param description A human-readable description of what the tool does. This can be - * used by clients to improve the LLM's understanding of available tools. - * @param inputSchema A JSON Schema object that describes the expected structure of - * the arguments when calling this tool. This allows clients to validate tool - * @param outputSchema An optional JSON Schema object defining the structure of the - * tool's output returned in the structuredContent field of a CallToolResult. - * @param annotations Optional additional tool information. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record Tool( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("title") String title, - @JsonProperty("description") String description, - @JsonProperty("inputSchema") JsonSchema inputSchema, - @JsonProperty("outputSchema") Map outputSchema, - @JsonProperty("annotations") ToolAnnotations annotations, - @JsonProperty("_meta") Map meta) { // @formatter:on - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private String name; - - private String title; - - private String description; - - private JsonSchema inputSchema; - - private Map outputSchema; - - private ToolAnnotations annotations; - - private Map meta; - - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder title(String title) { - this.title = title; - return this; - } - - public Builder description(String description) { - this.description = description; - return this; - } - - public Builder inputSchema(JsonSchema inputSchema) { - this.inputSchema = inputSchema; - return this; - } - - public Builder inputSchema(McpJsonMapper jsonMapper, String inputSchema) { - this.inputSchema = parseSchema(jsonMapper, inputSchema); - return this; - } - - public Builder outputSchema(Map outputSchema) { - this.outputSchema = outputSchema; - return this; - } - - public Builder outputSchema(McpJsonMapper jsonMapper, String outputSchema) { - this.outputSchema = schemaToMap(jsonMapper, outputSchema); - return this; - } - - public Builder annotations(ToolAnnotations annotations) { - this.annotations = annotations; - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public Tool build() { - Assert.hasText(name, "name must not be empty"); - return new Tool(name, title, description, inputSchema, outputSchema, annotations, meta); - } - - } - } - - private static Map schemaToMap(McpJsonMapper jsonMapper, String schema) { - try { - return jsonMapper.readValue(schema, MAP_TYPE_REF); - } - catch (IOException e) { - throw new IllegalArgumentException("Invalid schema: " + schema, e); - } - } - - private static JsonSchema parseSchema(McpJsonMapper jsonMapper, String schema) { - try { - return jsonMapper.readValue(schema, JsonSchema.class); - } - catch (IOException e) { - throw new IllegalArgumentException("Invalid schema: " + schema, e); - } - } - - /** - * Used by the client to call a tool provided by the server. - * - * @param name The name of the tool to call. This must match a tool name from - * tools/list. - * @param arguments Arguments to pass to the tool. These must conform to the tool's - * input schema. - * @param meta Optional metadata about the request. This can include additional - * information like `progressToken` - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record CallToolRequest( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("arguments") Map arguments, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - public CallToolRequest(McpJsonMapper jsonMapper, String name, String jsonArguments) { - this(name, parseJsonArguments(jsonMapper, jsonArguments), null); - } - - public CallToolRequest(String name, Map arguments) { - this(name, arguments, null); - } - - private static Map parseJsonArguments(McpJsonMapper jsonMapper, String jsonArguments) { - try { - return jsonMapper.readValue(jsonArguments, MAP_TYPE_REF); - } - catch (IOException e) { - throw new IllegalArgumentException("Invalid arguments: " + jsonArguments, e); - } - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private String name; - - private Map arguments; - - private Map meta; - - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder arguments(Map arguments) { - this.arguments = arguments; - return this; - } - - public Builder arguments(McpJsonMapper jsonMapper, String jsonArguments) { - this.arguments = parseJsonArguments(jsonMapper, jsonArguments); - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public Builder progressToken(Object progressToken) { - if (this.meta == null) { - this.meta = new HashMap<>(); - } - this.meta.put("progressToken", progressToken); - return this; - } - - public CallToolRequest build() { - Assert.hasText(name, "name must not be empty"); - return new CallToolRequest(name, arguments, meta); - } - - } - } - - /** - * The server's response to a tools/call request from the client. - * - * @param content A list of content items representing the tool's output. Each item - * can be text, an image, or an embedded resource. - * @param isError If true, indicates that the tool execution failed and the content - * contains error information. If false or absent, indicates successful execution. - * @param structuredContent An optional JSON object that represents the structured - * result of the tool call. - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record CallToolResult( // @formatter:off - @JsonProperty("content") List content, - @JsonProperty("isError") Boolean isError, - @JsonProperty("structuredContent") Object structuredContent, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - /** - * Creates a builder for {@link CallToolResult}. - * @return a new builder instance - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for {@link CallToolResult}. - */ - public static class Builder { - - private List content = new ArrayList<>(); - - private Boolean isError = false; - - private Object structuredContent; - - private Map meta; - - /** - * Sets the content list for the tool result. - * @param content the content list - * @return this builder - */ - public Builder content(List content) { - Assert.notNull(content, "content must not be null"); - this.content = content; - return this; - } - - public Builder structuredContent(Object structuredContent) { - Assert.notNull(structuredContent, "structuredContent must not be null"); - this.structuredContent = structuredContent; - return this; - } - - public Builder structuredContent(McpJsonMapper jsonMapper, String structuredContent) { - Assert.hasText(structuredContent, "structuredContent must not be empty"); - try { - this.structuredContent = jsonMapper.readValue(structuredContent, MAP_TYPE_REF); - } - catch (IOException e) { - throw new IllegalArgumentException("Invalid structured content: " + structuredContent, e); - } - return this; - } - - /** - * Sets the text content for the tool result. - * @param textContent the text content - * @return this builder - */ - public Builder textContent(List textContent) { - Assert.notNull(textContent, "textContent must not be null"); - textContent.stream().map(TextContent::new).forEach(this.content::add); - return this; - } - - /** - * Adds a content item to the tool result. - * @param contentItem the content item to add - * @return this builder - */ - public Builder addContent(Content contentItem) { - Assert.notNull(contentItem, "contentItem must not be null"); - if (this.content == null) { - this.content = new ArrayList<>(); - } - this.content.add(contentItem); - return this; - } - - /** - * Adds a text content item to the tool result. - * @param text the text content - * @return this builder - */ - public Builder addTextContent(String text) { - Assert.notNull(text, "text must not be null"); - return addContent(new TextContent(text)); - } - - /** - * Sets whether the tool execution resulted in an error. - * @param isError true if the tool execution failed, false otherwise - * @return this builder - */ - public Builder isError(Boolean isError) { - Assert.notNull(isError, "isError must not be null"); - this.isError = isError; - return this; - } - - /** - * Sets the metadata for the tool result. - * @param meta metadata - * @return this builder - */ - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - /** - * Builds a new {@link CallToolResult} instance. - * @return a new CallToolResult instance - */ - public CallToolResult build() { - return new CallToolResult(content, isError, structuredContent, meta); - } - - } - - } - - // --------------------------- - // Sampling Interfaces - // --------------------------- - /** - * The server's preferences for model selection, requested of the client during - * sampling. - * - * @param hints Optional hints to use for model selection. If multiple hints are - * specified, the client MUST evaluate them in order (such that the first match is - * taken). The client SHOULD prioritize these hints over the numeric priorities, but - * MAY still use the priorities to select from ambiguous matches - * @param costPriority How much to prioritize cost when selecting a model. A value of - * 0 means cost is not important, while a value of 1 means cost is the most important - * factor - * @param speedPriority How much to prioritize sampling speed (latency) when selecting - * a model. A value of 0 means speed is not important, while a value of 1 means speed - * is the most important factor - * @param intelligencePriority How much to prioritize intelligence and capabilities - * when selecting a model. A value of 0 means intelligence is not important, while a - * value of 1 means intelligence is the most important factor - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ModelPreferences( // @formatter:off - @JsonProperty("hints") List hints, - @JsonProperty("costPriority") Double costPriority, - @JsonProperty("speedPriority") Double speedPriority, - @JsonProperty("intelligencePriority") Double intelligencePriority) { // @formatter:on - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private List hints; - - private Double costPriority; - - private Double speedPriority; - - private Double intelligencePriority; - - public Builder hints(List hints) { - this.hints = hints; - return this; - } - - public Builder addHint(String name) { - if (this.hints == null) { - this.hints = new ArrayList<>(); - } - this.hints.add(new ModelHint(name)); - return this; - } - - public Builder costPriority(Double costPriority) { - this.costPriority = costPriority; - return this; - } - - public Builder speedPriority(Double speedPriority) { - this.speedPriority = speedPriority; - return this; - } - - public Builder intelligencePriority(Double intelligencePriority) { - this.intelligencePriority = intelligencePriority; - return this; - } - - public ModelPreferences build() { - return new ModelPreferences(hints, costPriority, speedPriority, intelligencePriority); - } - - } - } - - /** - * Hints to use for model selection. - * - * @param name A hint for a model name. The client SHOULD treat this as a substring of - * a model name; for example: `claude-3-5-sonnet` should match - * `claude-3-5-sonnet-20241022`, `sonnet` should match `claude-3-5-sonnet-20241022`, - * `claude-3-sonnet-20240229`, etc., `claude` should match any Claude model. The - * client MAY also map the string to a different provider's model name or a different - * model family, as long as it fills a similar niche - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ModelHint(@JsonProperty("name") String name) { - public static ModelHint of(String name) { - return new ModelHint(name); - } - } - - /** - * Describes a message issued to or received from an LLM API. - * - * @param role The sender or recipient of messages and data in a conversation - * @param content The content of the message - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record SamplingMessage( // @formatter:off - @JsonProperty("role") Role role, - @JsonProperty("content") Content content) { // @formatter:on - } - - /** - * A request from the server to sample an LLM via the client. The client has full - * discretion over which model to select. The client should also inform the user - * before beginning sampling, to allow them to inspect the request (human in the loop) - * and decide whether to approve it. - * - * @param messages The conversation messages to send to the LLM - * @param modelPreferences The server's preferences for which model to select. The - * client MAY ignore these preferences - * @param systemPrompt An optional system prompt the server wants to use for sampling. - * The client MAY modify or omit this prompt - * @param includeContext A request to include context from one or more MCP servers - * (including the caller), to be attached to the prompt. The client MAY ignore this - * request - * @param temperature Optional temperature parameter for sampling - * @param maxTokens The maximum number of tokens to sample, as requested by the - * server. The client MAY choose to sample fewer tokens than requested - * @param stopSequences Optional stop sequences for sampling - * @param metadata Optional metadata to pass through to the LLM provider. The format - * of this metadata is provider-specific - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record CreateMessageRequest( // @formatter:off - @JsonProperty("messages") List messages, - @JsonProperty("modelPreferences") ModelPreferences modelPreferences, - @JsonProperty("systemPrompt") String systemPrompt, - @JsonProperty("includeContext") ContextInclusionStrategy includeContext, - @JsonProperty("temperature") Double temperature, - @JsonProperty("maxTokens") Integer maxTokens, - @JsonProperty("stopSequences") List stopSequences, - @JsonProperty("metadata") Map metadata, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - // backwards compatibility constructor - public CreateMessageRequest(List messages, ModelPreferences modelPreferences, - String systemPrompt, ContextInclusionStrategy includeContext, Double temperature, Integer maxTokens, - List stopSequences, Map metadata) { - this(messages, modelPreferences, systemPrompt, includeContext, temperature, maxTokens, stopSequences, - metadata, null); - } - - public enum ContextInclusionStrategy { - - // @formatter:off - @JsonProperty("none") NONE, - @JsonProperty("thisServer") THIS_SERVER, - @JsonProperty("allServers")ALL_SERVERS - } // @formatter:on - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private List messages; - - private ModelPreferences modelPreferences; - - private String systemPrompt; - - private ContextInclusionStrategy includeContext; - - private Double temperature; - - private Integer maxTokens; - - private List stopSequences; - - private Map metadata; - - private Map meta; - - public Builder messages(List messages) { - this.messages = messages; - return this; - } - - public Builder modelPreferences(ModelPreferences modelPreferences) { - this.modelPreferences = modelPreferences; - return this; - } - - public Builder systemPrompt(String systemPrompt) { - this.systemPrompt = systemPrompt; - return this; - } - - public Builder includeContext(ContextInclusionStrategy includeContext) { - this.includeContext = includeContext; - return this; - } - - public Builder temperature(Double temperature) { - this.temperature = temperature; - return this; - } - - public Builder maxTokens(int maxTokens) { - this.maxTokens = maxTokens; - return this; - } - - public Builder stopSequences(List stopSequences) { - this.stopSequences = stopSequences; - return this; - } - - public Builder metadata(Map metadata) { - this.metadata = metadata; - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public Builder progressToken(Object progressToken) { - if (this.meta == null) { - this.meta = new HashMap<>(); - } - this.meta.put("progressToken", progressToken); - return this; - } - - public CreateMessageRequest build() { - return new CreateMessageRequest(messages, modelPreferences, systemPrompt, includeContext, temperature, - maxTokens, stopSequences, metadata, meta); - } - - } - } - - /** - * The client's response to a sampling/create_message request from the server. The - * client should inform the user before returning the sampled message, to allow them - * to inspect the response (human in the loop) and decide whether to allow the server - * to see it. - * - * @param role The role of the message sender (typically assistant) - * @param content The content of the sampled message - * @param model The name of the model that generated the message - * @param stopReason The reason why sampling stopped, if known - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record CreateMessageResult( // @formatter:off - @JsonProperty("role") Role role, - @JsonProperty("content") Content content, - @JsonProperty("model") String model, - @JsonProperty("stopReason") StopReason stopReason, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public enum StopReason { - - // @formatter:off - @JsonProperty("endTurn") END_TURN("endTurn"), - @JsonProperty("stopSequence") STOP_SEQUENCE("stopSequence"), - @JsonProperty("maxTokens") MAX_TOKENS("maxTokens"), - @JsonProperty("unknown") UNKNOWN("unknown"); - // @formatter:on - - private final String value; - - StopReason(String value) { - this.value = value; - } - - @JsonCreator - private static StopReason of(String value) { - return Arrays.stream(StopReason.values()) - .filter(stopReason -> stopReason.value.equals(value)) - .findFirst() - .orElse(StopReason.UNKNOWN); - } - - } - - public CreateMessageResult(Role role, Content content, String model, StopReason stopReason) { - this(role, content, model, stopReason, null); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private Role role = Role.ASSISTANT; - - private Content content; - - private String model; - - private StopReason stopReason = StopReason.END_TURN; - - private Map meta; - - public Builder role(Role role) { - this.role = role; - return this; - } - - public Builder content(Content content) { - this.content = content; - return this; - } - - public Builder model(String model) { - this.model = model; - return this; - } - - public Builder stopReason(StopReason stopReason) { - this.stopReason = stopReason; - return this; - } - - public Builder message(String message) { - this.content = new TextContent(message); - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public CreateMessageResult build() { - return new CreateMessageResult(role, content, model, stopReason, meta); - } - - } - } - - // Elicitation - /** - * A request from the server to elicit additional information from the user via the - * client. - * - * @param message The message to present to the user - * @param requestedSchema A restricted subset of JSON Schema. Only top-level - * properties are allowed, without nesting - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ElicitRequest( // @formatter:off - @JsonProperty("message") String message, - @JsonProperty("requestedSchema") Map requestedSchema, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on - - // backwards compatibility constructor - public ElicitRequest(String message, Map requestedSchema) { - this(message, requestedSchema, null); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private String message; - - private Map requestedSchema; - - private Map meta; - - public Builder message(String message) { - this.message = message; - return this; - } - - public Builder requestedSchema(Map requestedSchema) { - this.requestedSchema = requestedSchema; - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public Builder progressToken(Object progressToken) { - if (this.meta == null) { - this.meta = new HashMap<>(); - } - this.meta.put("progressToken", progressToken); - return this; - } - - public ElicitRequest build() { - return new ElicitRequest(message, requestedSchema, meta); - } - - } - } - - /** - * The client's response to an elicitation request. - * - * @param action The user action in response to the elicitation. "accept": User - * submitted the form/confirmed the action, "decline": User explicitly declined the - * action, "cancel": User dismissed without making an explicit choice - * @param content The submitted form data, only present when action is "accept". - * Contains values matching the requested schema - * @param meta See specification for notes on _meta usage - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record ElicitResult( // @formatter:off - @JsonProperty("action") Action action, - @JsonProperty("content") Map content, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on - - public enum Action { - - // @formatter:off - @JsonProperty("accept") ACCEPT, - @JsonProperty("decline") DECLINE, - @JsonProperty("cancel") CANCEL - } // @formatter:on - - // backwards compatibility constructor - public ElicitResult(Action action, Map content) { - this(action, content, null); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private Action action; - - private Map content; - - private Map meta; - - public Builder message(Action action) { - this.action = action; - return this; - } - - public Builder content(Map content) { - this.content = content; - return this; - } - - public Builder meta(Map meta) { - this.meta = meta; - return this; - } - - public ElicitResult build() { - return new ElicitResult(action, content, meta); - } - - } - } - // --------------------------- // Pagination Interfaces // --------------------------- @@ -2345,7 +770,7 @@ public record SetLevelRequest(@JsonProperty("level") LoggingLevel level) { // --------------------------- // Autocomplete // --------------------------- - public sealed interface CompleteReference permits PromptReference, ResourceReference { + public interface CompleteReference { String type(); @@ -2512,8 +937,7 @@ public record CompleteCompletion( // @formatter:off @JsonSubTypes.Type(value = AudioContent.class, name = "audio"), @JsonSubTypes.Type(value = EmbeddedResource.class, name = "resource"), @JsonSubTypes.Type(value = ResourceLink.class, name = "resource_link") }) - public sealed interface Content extends Meta - permits TextContent, ImageContent, AudioContent, EmbeddedResource, ResourceLink { + public interface Content extends Meta { default String type() { if (this instanceof TextContent) { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpServerSession.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpServerSession.java index fc011a4e3..bdb7ec33d 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpServerSession.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpServerSession.java @@ -17,6 +17,11 @@ import io.modelcontextprotocol.server.McpInitRequestHandler; import io.modelcontextprotocol.server.McpNotificationHandler; import io.modelcontextprotocol.server.McpRequestHandler; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.util.Assert; import org.slf4j.Logger; @@ -33,7 +38,7 @@ public class McpServerSession implements McpLoggableSession { private static final Logger logger = LoggerFactory.getLogger(McpServerSession.class); - private final ConcurrentHashMap> pendingResponses = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> pendingResponses = new ConcurrentHashMap<>(); private final String id; @@ -151,10 +156,10 @@ public boolean isNotificationForLevelAllowed(McpSchema.LoggingLevel loggingLevel public Mono sendRequest(String method, Object requestParams, TypeRef typeRef) { String requestId = this.generateRequestId(); - return Mono.create(sink -> { + return Mono.create(sink -> { this.pendingResponses.put(requestId, sink); - McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method, - requestId, requestParams); + JSONRPCRequest jsonrpcRequest = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, method, requestId, + requestParams); this.transport.sendMessage(jsonrpcRequest).subscribe(v -> { }, error -> { this.pendingResponses.remove(requestId); @@ -177,8 +182,7 @@ public Mono sendRequest(String method, Object requestParams, TypeRef t @Override public Mono sendNotification(String method, Object params) { - McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, - method, params); + JSONRPCNotification jsonrpcNotification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, method, params); return this.transport.sendMessage(jsonrpcNotification); } @@ -192,13 +196,13 @@ public Mono sendNotification(String method, Object params) { * @param message the incoming JSON-RPC message * @return a Mono that completes when the message is processed */ - public Mono handle(McpSchema.JSONRPCMessage message) { + public Mono handle(JSONRPCMessage message) { return Mono.deferContextual(ctx -> { McpTransportContext transportContext = ctx.getOrDefault(McpTransportContext.KEY, McpTransportContext.EMPTY); // TODO handle errors for communication to without initialization happening // first - if (message instanceof McpSchema.JSONRPCResponse response) { + if (message instanceof JSONRPCResponse response) { logger.debug("Received response: {}", response); if (response.id() != null) { var sink = pendingResponses.remove(response.id()); @@ -216,20 +220,19 @@ public Mono handle(McpSchema.JSONRPCMessage message) { } return Mono.empty(); } - else if (message instanceof McpSchema.JSONRPCRequest request) { + else if (message instanceof JSONRPCRequest request) { logger.debug("Received request: {}", request); return handleIncomingRequest(request, transportContext).onErrorResume(error -> { - McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError + JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError && mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError() - : new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, + : new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, error.getMessage(), McpError.aggregateExceptionMessages(error)); - var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, - jsonRpcError); + var errorResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, jsonRpcError); // TODO: Should the error go to SSE or back as POST return? return this.transport.sendMessage(errorResponse).then(Mono.empty()); }).flatMap(this.transport::sendMessage); } - else if (message instanceof McpSchema.JSONRPCNotification notification) { + else if (message instanceof JSONRPCNotification notification) { // TODO handle errors for communication to without initialization // happening first logger.debug("Received notification: {}", notification); @@ -250,8 +253,7 @@ else if (message instanceof McpSchema.JSONRPCNotification notification) { * @param transportContext * @return A Mono containing the JSON-RPC response */ - private Mono handleIncomingRequest(McpSchema.JSONRPCRequest request, - McpTransportContext transportContext) { + private Mono handleIncomingRequest(JSONRPCRequest request, McpTransportContext transportContext) { return Mono.defer(() -> { Mono resultMono; if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { @@ -270,24 +272,22 @@ private Mono handleIncomingRequest(McpSchema.JSONRPCR var handler = this.requestHandlers.get(request.method()); if (handler == null) { MethodNotFoundError error = getMethodNotFoundError(request.method()); - return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, - new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, - error.message(), error.data()))); + return Mono.just(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, + new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, error.message(), + error.data()))); } resultMono = this.exchangeSink.asMono() .flatMap(exchange -> handler.handle(copyExchange(exchange, transportContext), request.params())); } - return resultMono - .map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null)) + return resultMono.map(result -> new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), result, null)) .onErrorResume(error -> { - McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError + JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError && mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError() // TODO: add error message through the data field - : new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, + : new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, error.getMessage(), McpError.aggregateExceptionMessages(error)); - return Mono.just( - new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, jsonRpcError)); + return Mono.just(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, jsonRpcError)); }); }); } @@ -298,7 +298,7 @@ private Mono handleIncomingRequest(McpSchema.JSONRPCR * @param transportContext * @return A Mono that completes when the notification is processed */ - private Mono handleIncomingNotification(McpSchema.JSONRPCNotification notification, + private Mono handleIncomingNotification(JSONRPCNotification notification, McpTransportContext transportContext) { return Mono.defer(() -> { if (McpSchema.METHOD_NOTIFICATION_INITIALIZED.equals(notification.method())) { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerSession.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerSession.java index 9ec2117bb..ca8f13d17 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerSession.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerSession.java @@ -22,6 +22,11 @@ import io.modelcontextprotocol.server.McpNotificationHandler; import io.modelcontextprotocol.server.McpRequestHandler; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import io.modelcontextprotocol.util.Assert; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -168,7 +173,7 @@ public McpStreamableServerSessionStream listeningStream(McpStreamableServerTrans // TODO: keep track of history by keeping a map from eventId to stream and then // iterate over the events using the lastEventId - public Flux replay(Object lastEventId) { + public Flux replay(Object lastEventId) { return Flux.empty(); } @@ -178,7 +183,7 @@ public Flux replay(Object lastEventId) { * @param transport the SSE transport stream to send messages to * @return Mono which completes once the processing is done */ - public Mono responseStream(McpSchema.JSONRPCRequest jsonrpcRequest, McpStreamableServerTransport transport) { + public Mono responseStream(JSONRPCRequest jsonrpcRequest, McpStreamableServerTransport transport) { return Mono.deferContextual(ctx -> { McpTransportContext transportContext = ctx.getOrDefault(McpTransportContext.KEY, McpTransportContext.EMPTY); @@ -190,24 +195,22 @@ public Mono responseStream(McpSchema.JSONRPCRequest jsonrpcRequest, McpStr // (sink) if (requestHandler == null) { MethodNotFoundError error = getMethodNotFoundError(jsonrpcRequest.method()); - return transport - .sendMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), null, - new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, - error.message(), error.data()))); + return transport.sendMessage(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, jsonrpcRequest.id(), null, + new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, error.message(), + error.data()))); } return requestHandler .handle(new McpAsyncServerExchange(this.id, stream, clientCapabilities.get(), clientInfo.get(), transportContext), jsonrpcRequest.params()) - .map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), result, - null)) + .map(result -> new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, jsonrpcRequest.id(), result, null)) .onErrorResume(e -> { - McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (e instanceof McpError mcpError + JSONRPCResponse.JSONRPCError jsonRpcError = (e instanceof McpError mcpError && mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError() - : new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, + : new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR, e.getMessage(), McpError.aggregateExceptionMessages(e)); - var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), - null, jsonRpcError); + var errorResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, jsonrpcRequest.id(), null, + jsonRpcError); return Mono.just(errorResponse); }) .flatMap(transport::sendMessage) @@ -220,7 +223,7 @@ public Mono responseStream(McpSchema.JSONRPCRequest jsonrpcRequest, McpStr * @param notification MCP notification * @return Mono which completes upon succesful handling */ - public Mono accept(McpSchema.JSONRPCNotification notification) { + public Mono accept(JSONRPCNotification notification) { return Mono.deferContextual(ctx -> { McpTransportContext transportContext = ctx.getOrDefault(McpTransportContext.KEY, McpTransportContext.EMPTY); McpNotificationHandler notificationHandler = this.notificationHandlers.get(notification.method()); @@ -240,7 +243,7 @@ public Mono accept(McpSchema.JSONRPCNotification notification) { * @param response MCP response to the server-initiated request * @return Mono which completes upon successful processing */ - public Mono accept(McpSchema.JSONRPCResponse response) { + public Mono accept(JSONRPCResponse response) { return Mono.defer(() -> { logger.debug("Received response: {}", response); @@ -342,7 +345,7 @@ public record McpStreamableServerSessionInit(McpStreamableServerSession session, */ public final class McpStreamableServerSessionStream implements McpLoggableSession { - private final ConcurrentHashMap> pendingResponses = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> pendingResponses = new ConcurrentHashMap<>(); private final McpStreamableServerTransport transport; @@ -379,10 +382,10 @@ public Mono sendRequest(String method, Object requestParams, TypeRef t McpStreamableServerSession.this.requestIdToStream.put(requestId, this); - return Mono.create(sink -> { + return Mono.create(sink -> { this.pendingResponses.put(requestId, sink); - McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - method, requestId, requestParams); + JSONRPCRequest jsonrpcRequest = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, method, requestId, + requestParams); String messageId = this.uuidGenerator.get(); // TODO: store message in history this.transport.sendMessage(jsonrpcRequest, messageId).subscribe(v -> { @@ -407,8 +410,7 @@ public Mono sendRequest(String method, Object requestParams, TypeRef t @Override public Mono sendNotification(String method, Object params) { - McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification( - McpSchema.JSONRPC_VERSION, method, params); + JSONRPCNotification jsonrpcNotification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, method, params); String messageId = this.uuidGenerator.get(); // TODO: store message in history return this.transport.sendMessage(jsonrpcNotification, messageId); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransport.java index f53c68900..a6a11ac49 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransport.java @@ -4,6 +4,7 @@ package io.modelcontextprotocol.spec; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import reactor.core.publisher.Mono; /** @@ -19,6 +20,6 @@ public interface McpStreamableServerTransport extends McpServerTransport { * @param messageId message id for SSE events * @return Mono which completes when done */ - Mono sendMessage(McpSchema.JSONRPCMessage message, String messageId); + Mono sendMessage(JSONRPCMessage message, String messageId); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransport.java index 0a732bab6..6a66bf449 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransport.java @@ -6,8 +6,8 @@ import java.util.List; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage; import io.modelcontextprotocol.json.TypeRef; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import reactor.core.publisher.Mono; /** diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransportStream.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransportStream.java index 322afda63..e6d102ff8 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransportStream.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpTransportStream.java @@ -9,6 +9,8 @@ import java.util.Optional; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; + /** * A representation of a stream at the transport layer of the MCP protocol. In particular, * it is currently used in the Streamable HTTP implementation to potentially be able to @@ -40,10 +42,10 @@ public interface McpTransportStream { * @param eventStream a {@link Publisher} of tuples (pairs) of an optional identifier * associated with a collection of messages * @return a flattened {@link Publisher} of - * {@link io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage JSON-RPC messages} - * with the identifier stripped away + * {@link io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage JSON-RPC messages} with + * the identifier stripped away */ - Publisher consumeSseStream( - Publisher, Iterable>> eventStream); + Publisher consumeSseStream( + Publisher, Iterable>> eventStream); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPC.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPC.java new file mode 100644 index 000000000..83c5f0bd7 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPC.java @@ -0,0 +1,51 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.jsonrpc; + +import java.io.IOException; + +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.spec.McpSchema; + +/** + * @author Christian Tzolov + */ + +public class JSONRPC { + + public static final String JSONRPC_VERSION = "2.0"; + + /** + * Deserializes a JSON string into a JSONRPCMessage object. + * @param jsonMapper The JsonMapper instance to use for deserialization + * @param jsonText The JSON string to deserialize + * @return A JSONRPCMessage instance using either the {@link JSONRPCRequest}, + * {@link JSONRPCNotification}, or {@link JSONRPCResponse} classes. + * @throws IOException If there's an error during deserialization + * @throws IllegalArgumentException If the JSON structure doesn't match any known + * message type + */ + public static JSONRPCMessage deserializeJsonRpcMessage(McpJsonMapper jsonMapper, String jsonText) + throws IOException { + + McpSchema.logger.debug("Received JSON message: {}", jsonText); + + var map = jsonMapper.readValue(jsonText, McpSchema.MAP_TYPE_REF); + + // Determine message type based on specific JSON structure + if (map.containsKey("method") && map.containsKey("id")) { + return jsonMapper.convertValue(map, JSONRPCRequest.class); + } + else if (map.containsKey("method") && !map.containsKey("id")) { + return jsonMapper.convertValue(map, JSONRPCNotification.class); + } + else if (map.containsKey("result") || map.containsKey("error")) { + return jsonMapper.convertValue(map, JSONRPCResponse.class); + } + + throw new IllegalArgumentException("Cannot deserialize JSONRPCMessage: " + jsonText); + } + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCMessage.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCMessage.java new file mode 100644 index 000000000..54d7b637c --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCMessage.java @@ -0,0 +1,11 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.jsonrpc; + +public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCNotification, JSONRPCResponse { + + String jsonrpc(); + +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCNotification.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCNotification.java new file mode 100644 index 000000000..ea3941f9c --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCNotification.java @@ -0,0 +1,26 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.jsonrpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A notification which does not expect a response. + * + * @param jsonrpc The JSON-RPC version (must be "2.0") + * @param method The name of the method being notified + * @param params Parameters for the notification + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +// TODO: batching support +// @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) +public record JSONRPCNotification( // @formatter:off + @JsonProperty("jsonrpc") String jsonrpc, + @JsonProperty("method") String method, + @JsonProperty("params") Object params) implements JSONRPCMessage { // @formatter:on +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCRequest.java new file mode 100644 index 000000000..223bcf46a --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCRequest.java @@ -0,0 +1,39 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.jsonrpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.util.Assert; + +/** + * A request that expects a response. + * + * @param jsonrpc The JSON-RPC version (must be "2.0") + * @param method The name of the method to be invoked + * @param id A unique identifier for the request + * @param params Parameters for the method call + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +// @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) +public record JSONRPCRequest( // @formatter:off + @JsonProperty("jsonrpc") String jsonrpc, + @JsonProperty("method") String method, + @JsonProperty("id") Object id, + @JsonProperty("params") Object params) implements JSONRPCMessage { // @formatter:on + + /** + * Constructor that validates MCP-specific ID requirements. Unlike base JSON-RPC, MCP + * requires that: (1) Requests MUST include a string or integer ID; (2) The ID MUST + * NOT be null + */ + public JSONRPCRequest { + Assert.notNull(id, "MCP requests MUST include an ID - null IDs are not allowed"); + Assert.isTrue(id instanceof String || id instanceof Integer || id instanceof Long, + "MCP requests MUST have an ID that is either a string or integer"); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCResponse.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCResponse.java new file mode 100644 index 000000000..48e3f83e2 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/jsonrpc/JSONRPCResponse.java @@ -0,0 +1,45 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.jsonrpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A response to a request (successful, or error). + * + * @param jsonrpc The JSON-RPC version (must be "2.0") + * @param id The request identifier that this response corresponds to + * @param result The result of the successful request; null if error + * @param error Error information if the request failed; null if has result + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +// TODO: batching support +// @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) +public record JSONRPCResponse( // @formatter:off + @JsonProperty("jsonrpc") String jsonrpc, + @JsonProperty("id") Object id, + @JsonProperty("result") Object result, + @JsonProperty("error") JSONRPCError error) implements JSONRPCMessage { // @formatter:on + + /** + * A response to a request that indicates an error occurred. + * + * @param code The error type that occurred + * @param message A short description of the error. The message SHOULD be limited to a + * concise single sentence + * @param data Additional information about the error. The value of this member is + * defined by the sender (e.g. detailed error information, nested errors etc.) + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record JSONRPCError( // @formatter:off + @JsonProperty("code") Integer code, + @JsonProperty("message") String message, + @JsonProperty("data") Object data) { // @formatter:on + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/elicit/ElicitRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/elicit/ElicitRequest.java new file mode 100644 index 000000000..dfae3c9d5 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/elicit/ElicitRequest.java @@ -0,0 +1,73 @@ +package io.modelcontextprotocol.spec.schema.elicit; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Request; + +/** + * A request from the server to elicit additional information from the user via the + * client. + * + * @param message The message to present to the user + * @param requestedSchema A restricted subset of JSON Schema. Only top-level properties + * are allowed, without nesting + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ElicitRequest( // @formatter:off + @JsonProperty("message") String message, + @JsonProperty("requestedSchema") Map requestedSchema, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + // backwards compatibility constructor + public ElicitRequest(String message, Map requestedSchema) { + this(message, requestedSchema, null); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String message; + + private Map requestedSchema; + + private Map meta; + + public Builder message(String message) { + this.message = message; + return this; + } + + public Builder requestedSchema(Map requestedSchema) { + this.requestedSchema = requestedSchema; + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public Builder progressToken(Object progressToken) { + if (this.meta == null) { + this.meta = new HashMap<>(); + } + this.meta.put("progressToken", progressToken); + return this; + } + + public ElicitRequest build() { + return new ElicitRequest(message, requestedSchema, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/elicit/ElicitResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/elicit/ElicitResult.java new file mode 100644 index 000000000..fa9bf78c5 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/elicit/ElicitResult.java @@ -0,0 +1,73 @@ +package io.modelcontextprotocol.spec.schema.elicit; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The client's response to an elicitation request. + * + * @param action The user action in response to the elicitation. "accept": User submitted + * the form/confirmed the action, "decline": User explicitly declined the action, + * "cancel": User dismissed without making an explicit choice + * @param content The submitted form data, only present when action is "accept". Contains + * values matching the requested schema + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ElicitResult( // @formatter:off + @JsonProperty("action") Action action, + @JsonProperty("content") Map content, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public enum Action { + + // @formatter:off + @JsonProperty("accept") ACCEPT, + @JsonProperty("decline") DECLINE, + @JsonProperty("cancel") CANCEL + } // @formatter:on + + // backwards compatibility constructor + public ElicitResult(Action action, Map content) { + this(action, content, null); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Action action; + + private Map content; + + private Map meta; + + public Builder message(Action action) { + this.action = action; + return this; + } + + public Builder content(Map content) { + this.content = content; + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public ElicitResult build() { + return new ElicitResult(action, content, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/GetPromptRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/GetPromptRequest.java new file mode 100644 index 000000000..15e85e877 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/GetPromptRequest.java @@ -0,0 +1,31 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.prompt; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Request; + +/** + * Used by the client to get a prompt provided by the server. + * + * @param name The name of the prompt or prompt template. + * @param arguments Arguments to use for templating the prompt. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record GetPromptRequest( // @formatter:off + @JsonProperty("name") String name, + @JsonProperty("arguments") Map arguments, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + public GetPromptRequest(String name, Map arguments) { + this(name, arguments, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/GetPromptResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/GetPromptResult.java new file mode 100644 index 000000000..2e87d2d6e --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/GetPromptResult.java @@ -0,0 +1,32 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.prompt; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The server's response to a prompts/get request from the client. + * + * @param description An optional description for the prompt. + * @param messages A list of messages to display as part of the prompt. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record GetPromptResult( // @formatter:off + @JsonProperty("description") String description, + @JsonProperty("messages") List messages, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public GetPromptResult(String description, List messages) { + this(description, messages, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/ListPromptsResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/ListPromptsResult.java new file mode 100644 index 000000000..75eacddb3 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/ListPromptsResult.java @@ -0,0 +1,33 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.prompt; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The server's response to a prompts/list request from the client. + * + * @param prompts A list of prompts that the server provides. + * @param nextCursor An optional cursor for pagination. If present, indicates there are + * more prompts available. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListPromptsResult( // @formatter:off + @JsonProperty("prompts") List prompts, + @JsonProperty("nextCursor") String nextCursor, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public ListPromptsResult(List prompts, String nextCursor) { + this(prompts, nextCursor, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/Prompt.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/Prompt.java new file mode 100644 index 000000000..be593b901 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/Prompt.java @@ -0,0 +1,41 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.prompt; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.schema.resource.Identifier; + +/** + * A prompt or prompt template that the server offers. + * + * @param name The name of the prompt or prompt template. + * @param title An optional title for the prompt. + * @param description An optional description of what this prompt provides. + * @param arguments A list of arguments to use for templating the prompt. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record Prompt( // @formatter:off + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("arguments") List arguments, + @JsonProperty("_meta") Map meta) implements Identifier { // @formatter:on + + public Prompt(String name, String description, List arguments) { + this(name, null, description, arguments != null ? arguments : new ArrayList<>()); + } + + public Prompt(String name, String title, String description, List arguments) { + this(name, title, description, arguments != null ? arguments : new ArrayList<>(), null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/PromptArgument.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/PromptArgument.java new file mode 100644 index 000000000..857644a59 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/PromptArgument.java @@ -0,0 +1,31 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.prompt; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.schema.resource.Identifier; + +/** + * Describes an argument that a prompt can accept. + * + * @param name The name of the argument. + * @param title An optional title for the argument, which can be used in UI + * @param description A human-readable description of the argument. + * @param required Whether this argument must be provided. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record PromptArgument( // @formatter:off + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("required") Boolean required) implements Identifier { // @formatter:on + + public PromptArgument(String name, String description, Boolean required) { + this(name, null, description, required); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/PromptMessage.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/PromptMessage.java new file mode 100644 index 000000000..90e62f4c6 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/prompt/PromptMessage.java @@ -0,0 +1,27 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.prompt; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Content; +import io.modelcontextprotocol.spec.McpSchema.Role; + +/** + * Describes a message returned as part of a prompt. + * + * This is similar to `SamplingMessage`, but also supports the embedding of resources from + * the MCP server. + * + * @param role The sender or recipient of messages and data in a conversation. + * @param content The content of the message of type {@link Content}. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record PromptMessage( // @formatter:off + @JsonProperty("role") Role role, + @JsonProperty("content") Content content) { // @formatter:on +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Annotated.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Annotated.java new file mode 100644 index 000000000..d08619de7 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Annotated.java @@ -0,0 +1,17 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import io.modelcontextprotocol.spec.McpSchema.Annotations; + +/** + * Base for objects that include optional annotations for the client. The client can use + * annotations to inform how objects are used or displayed + */ +public interface Annotated { + + Annotations annotations(); + +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/BlobResourceContents.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/BlobResourceContents.java new file mode 100644 index 000000000..9419e74b9 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/BlobResourceContents.java @@ -0,0 +1,29 @@ +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Binary contents of a resource. + * + * @param uri the URI of this resource. + * @param mimeType the MIME type of this resource. + * @param blob a base64-encoded string representing the binary data of the resource. This + * must only be set if the resource can actually be represented as binary data (not text). + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record BlobResourceContents( // @formatter:off + @JsonProperty("uri") String uri, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("blob") String blob, + @JsonProperty("_meta") Map meta) implements ResourceContents { // @formatter:on + + public BlobResourceContents(String uri, String mimeType, String blob) { + this(uri, mimeType, blob, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Identifier.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Identifier.java new file mode 100644 index 000000000..0515f863b --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Identifier.java @@ -0,0 +1,26 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +/** + * Base interface with name (identifier) and title (display name) properties. + */ +public interface Identifier { + + /** + * Intended for programmatic or logical use, but used as a display name in past specs + * or fallback (if title isn't present). + */ + String name(); + + /** + * Intended for UI and end-user contexts — optimized to be human-readable and easily + * understood, even by those unfamiliar with domain-specific terminology. + * + * If not provided, the name should be used for display. + */ + String title(); + +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ListResourceTemplatesResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ListResourceTemplatesResult.java new file mode 100644 index 000000000..81677a643 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ListResourceTemplatesResult.java @@ -0,0 +1,30 @@ +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The server's response to a resources/templates/list request from the client. + * + * @param resourceTemplates A list of resource templates that the server provides + * @param nextCursor An opaque token representing the pagination position after the last + * returned result. If present, there may be more results available + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListResourceTemplatesResult( // @formatter:off + @JsonProperty("resourceTemplates") List resourceTemplates, + @JsonProperty("nextCursor") String nextCursor, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public ListResourceTemplatesResult(List resourceTemplates, String nextCursor) { + this(resourceTemplates, nextCursor, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ListResourcesResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ListResourcesResult.java new file mode 100644 index 000000000..6d927c7e4 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ListResourcesResult.java @@ -0,0 +1,30 @@ +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The server's response to a resources/list request from the client. + * + * @param resources A list of resources that the server provides + * @param nextCursor An opaque token representing the pagination position after the last + * returned result. If present, there may be more results available + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListResourcesResult( // @formatter:off + @JsonProperty("resources") List resources, + @JsonProperty("nextCursor") String nextCursor, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public ListResourcesResult(List resources, String nextCursor) { + this(resources, nextCursor, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ReadResourceRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ReadResourceRequest.java new file mode 100644 index 000000000..6ba92eb7d --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ReadResourceRequest.java @@ -0,0 +1,27 @@ +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Request; + +/** + * Sent from the client to the server, to read a specific resource URI. + * + * @param uri The URI of the resource to read. The URI can use any protocol; it is up to + * the server how to interpret it + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ReadResourceRequest( // @formatter:off + @JsonProperty("uri") String uri, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + public ReadResourceRequest(String uri) { + this(uri, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ReadResourceResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ReadResourceResult.java new file mode 100644 index 000000000..d9fabd4e3 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ReadResourceResult.java @@ -0,0 +1,27 @@ +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The server's response to a resources/read request from the client. + * + * @param contents The contents of the resource + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ReadResourceResult( // @formatter:off + @JsonProperty("contents") List contents, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public ReadResourceResult(List contents) { + this(contents, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Resource.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Resource.java new file mode 100644 index 000000000..8c3021a1c --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/Resource.java @@ -0,0 +1,115 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Annotations; +import io.modelcontextprotocol.util.Assert; + +/** + * A known resource that the server is capable of reading. + * + * @param uri the URI of the resource. + * @param name A human-readable name for this resource. This can be used by clients to + * populate UI elements. + * @param title An optional title for this resource. + * @param description A description of what this resource represents. This can be used by + * clients to improve the LLM's understanding of available resources. It can be thought of + * like a "hint" to the model. + * @param mimeType The MIME type of this resource, if known. + * @param size The size of the raw resource content, in bytes (i.e., before base64 + * encoding or any tokenization), if known. This can be used by Hosts to display file + * sizes and estimate context window usage. + * @param annotations Optional annotations for the client. The client can use annotations + * to inform how objects are used or displayed. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record Resource( // @formatter:off + @JsonProperty("uri") String uri, + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("size") Long size, + @JsonProperty("annotations") Annotations annotations, + @JsonProperty("_meta") Map meta) implements ResourceContent { // @formatter:on + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String uri; + + private String name; + + private String title; + + private String description; + + private String mimeType; + + private Long size; + + private Annotations annotations; + + private Map meta; + + public Builder uri(String uri) { + this.uri = uri; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder mimeType(String mimeType) { + this.mimeType = mimeType; + return this; + } + + public Builder size(Long size) { + this.size = size; + return this; + } + + public Builder annotations(Annotations annotations) { + this.annotations = annotations; + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public Resource build() { + Assert.hasText(uri, "uri must not be empty"); + Assert.hasText(name, "name must not be empty"); + + return new Resource(uri, name, title, description, mimeType, size, annotations, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceContent.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceContent.java new file mode 100644 index 000000000..777311613 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceContent.java @@ -0,0 +1,31 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import io.modelcontextprotocol.spec.McpSchema.Meta; +import io.modelcontextprotocol.spec.McpSchema.ResourceLink; + +/** + * A common interface for resource content, which includes metadata about the resource + * such as its URI, name, description, MIME type, size, and annotations. This interface is + * implemented by both {@link Resource} and {@link ResourceLink} to provide a consistent + * way to access resource metadata. + */ +public interface ResourceContent extends Identifier, Annotated, Meta { + + // name & title from Identifier + + String uri(); + + String description(); + + String mimeType(); + + Long size(); + + // annotations from Annotated + // meta from Meta + +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceContents.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceContents.java new file mode 100644 index 000000000..ea0af8a8e --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceContents.java @@ -0,0 +1,31 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.modelcontextprotocol.spec.McpSchema.Meta; + +/** + * The contents of a specific resource or sub-resource. + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) +@JsonSubTypes({ @JsonSubTypes.Type(value = TextResourceContents.class), + @JsonSubTypes.Type(value = BlobResourceContents.class) }) +public interface ResourceContents extends Meta { + + /** + * The URI of this resource. + * @return the URI of this resource. + */ + String uri(); + + /** + * The MIME type of this resource. + * @return the MIME type of this resource. + */ + String mimeType(); + +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceTemplate.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceTemplate.java new file mode 100644 index 000000000..97699f5a7 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/ResourceTemplate.java @@ -0,0 +1,117 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Annotations; +import io.modelcontextprotocol.spec.McpSchema.Meta; +import io.modelcontextprotocol.util.Assert; + +/** + * Resource templates allow servers to expose parameterized resources using URI + * + * @param uriTemplate A URI template that can be used to generate URIs for this resource. + * @param name A human-readable name for this resource. This can be used by clients to + * populate UI elements. + * @param title An optional title for this resource. + * @param description A description of what this resource represents. This can be used by + * clients to improve the LLM's understanding of available resources. It can be thought of + * like a "hint" to the model. + * @param mimeType The MIME type of this resource, if known. + * @param annotations Optional annotations for the client. The client can use annotations + * to inform how objects are used or displayed. + * @see RFC 6570 + * @param meta See specification for notes on _meta usage + * + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ResourceTemplate( // @formatter:off + @JsonProperty("uriTemplate") String uriTemplate, + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("annotations") Annotations annotations, + @JsonProperty("_meta") Map meta) implements Annotated, Identifier, Meta { // @formatter:on + + public ResourceTemplate(String uriTemplate, String name, String title, String description, String mimeType, + Annotations annotations) { + this(uriTemplate, name, title, description, mimeType, annotations, null); + } + + public ResourceTemplate(String uriTemplate, String name, String description, String mimeType, + Annotations annotations) { + this(uriTemplate, name, null, description, mimeType, annotations); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String uriTemplate; + + private String name; + + private String title; + + private String description; + + private String mimeType; + + private Annotations annotations; + + private Map meta; + + public Builder uriTemplate(String uri) { + this.uriTemplate = uri; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder mimeType(String mimeType) { + this.mimeType = mimeType; + return this; + } + + public Builder annotations(Annotations annotations) { + this.annotations = annotations; + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public ResourceTemplate build() { + Assert.hasText(uriTemplate, "uri must not be empty"); + Assert.hasText(name, "name must not be empty"); + + return new ResourceTemplate(uriTemplate, name, title, description, mimeType, annotations, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/SubscribeRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/SubscribeRequest.java new file mode 100644 index 000000000..567e37da6 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/SubscribeRequest.java @@ -0,0 +1,31 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Request; + +/** + * Sent from the client to request resources/updated notifications from the server + * whenever a particular resource changes. + * + * @param uri the URI of the resource to subscribe to. The URI can use any protocol; it is + * up to the server how to interpret it. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SubscribeRequest( // @formatter:off + @JsonProperty("uri") String uri, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + public SubscribeRequest(String uri) { + this(uri, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/TextResourceContents.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/TextResourceContents.java new file mode 100644 index 000000000..93fe9b70f --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/TextResourceContents.java @@ -0,0 +1,33 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Text contents of a resource. + * + * @param uri the URI of this resource. + * @param mimeType the MIME type of this resource. + * @param text the text of the resource. This must only be set if the resource can + * actually be represented as text (not binary data). + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record TextResourceContents( // @formatter:off + @JsonProperty("uri") String uri, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("text") String text, + @JsonProperty("_meta") Map meta) implements ResourceContents { // @formatter:on + + public TextResourceContents(String uri, String mimeType, String text) { + this(uri, mimeType, text, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/UnsubscribeRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/UnsubscribeRequest.java new file mode 100644 index 000000000..dabf9e5bd --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/resource/UnsubscribeRequest.java @@ -0,0 +1,30 @@ +/* +* Copyright 2024 - 2024 the original author or authors. +*/ + +package io.modelcontextprotocol.spec.schema.resource; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Request; + +/** + * Sent from the client to request cancellation of resources/updated notifications from + * the server. This should follow a previous resources/subscribe request. + * + * @param uri The URI of the resource to unsubscribe from + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UnsubscribeRequest( // @formatter:off + @JsonProperty("uri") String uri, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + public UnsubscribeRequest(String uri) { + this(uri, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/CreateMessageRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/CreateMessageRequest.java new file mode 100644 index 000000000..1224e0a7a --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/CreateMessageRequest.java @@ -0,0 +1,147 @@ +package io.modelcontextprotocol.spec.schema.sample; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Request; + +/** + * A request from the server to sample an LLM via the client. The client has full + * discretion over which model to select. The client should also inform the user before + * beginning sampling, to allow them to inspect the request (human in the loop) and decide + * whether to approve it. + * + * @param messages The conversation messages to send to the LLM + * @param modelPreferences The server's preferences for which model to select. The client + * MAY ignore these preferences + * @param systemPrompt An optional system prompt the server wants to use for sampling. The + * client MAY modify or omit this prompt + * @param includeContext A request to include context from one or more MCP servers + * (including the caller), to be attached to the prompt. The client MAY ignore this + * request + * @param temperature Optional temperature parameter for sampling + * @param maxTokens The maximum number of tokens to sample, as requested by the server. + * The client MAY choose to sample fewer tokens than requested + * @param stopSequences Optional stop sequences for sampling + * @param metadata Optional metadata to pass through to the LLM provider. The format of + * this metadata is provider-specific + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CreateMessageRequest( // @formatter:off + @JsonProperty("messages") List messages, + @JsonProperty("modelPreferences") ModelPreferences modelPreferences, + @JsonProperty("systemPrompt") String systemPrompt, + @JsonProperty("includeContext") ContextInclusionStrategy includeContext, + @JsonProperty("temperature") Double temperature, + @JsonProperty("maxTokens") Integer maxTokens, + @JsonProperty("stopSequences") List stopSequences, + @JsonProperty("metadata") Map metadata, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + // backwards compatibility constructor + public CreateMessageRequest(List messages, ModelPreferences modelPreferences, String systemPrompt, + ContextInclusionStrategy includeContext, Double temperature, Integer maxTokens, List stopSequences, + Map metadata) { + this(messages, modelPreferences, systemPrompt, includeContext, temperature, maxTokens, stopSequences, metadata, + null); + } + + public enum ContextInclusionStrategy { + + // @formatter:off + @JsonProperty("none") NONE, + @JsonProperty("thisServer") THIS_SERVER, + @JsonProperty("allServers")ALL_SERVERS + } // @formatter:on + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private List messages; + + private ModelPreferences modelPreferences; + + private String systemPrompt; + + private ContextInclusionStrategy includeContext; + + private Double temperature; + + private Integer maxTokens; + + private List stopSequences; + + private Map metadata; + + private Map meta; + + public Builder messages(List messages) { + this.messages = messages; + return this; + } + + public Builder modelPreferences(ModelPreferences modelPreferences) { + this.modelPreferences = modelPreferences; + return this; + } + + public Builder systemPrompt(String systemPrompt) { + this.systemPrompt = systemPrompt; + return this; + } + + public Builder includeContext(ContextInclusionStrategy includeContext) { + this.includeContext = includeContext; + return this; + } + + public Builder temperature(Double temperature) { + this.temperature = temperature; + return this; + } + + public Builder maxTokens(int maxTokens) { + this.maxTokens = maxTokens; + return this; + } + + public Builder stopSequences(List stopSequences) { + this.stopSequences = stopSequences; + return this; + } + + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public Builder progressToken(Object progressToken) { + if (this.meta == null) { + this.meta = new HashMap<>(); + } + this.meta.put("progressToken", progressToken); + return this; + } + + public CreateMessageRequest build() { + return new CreateMessageRequest(messages, modelPreferences, systemPrompt, includeContext, temperature, + maxTokens, stopSequences, metadata, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/CreateMessageResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/CreateMessageResult.java new file mode 100644 index 000000000..3be62daa1 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/CreateMessageResult.java @@ -0,0 +1,116 @@ +package io.modelcontextprotocol.spec.schema.sample; + +import java.util.Arrays; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Content; +import io.modelcontextprotocol.spec.McpSchema.Result; +import io.modelcontextprotocol.spec.McpSchema.Role; +import io.modelcontextprotocol.spec.McpSchema.TextContent; + +/** + * The client's response to a sampling/create_message request from the server. The client + * should inform the user before returning the sampled message, to allow them to inspect + * the response (human in the loop) and decide whether to allow the server to see it. + * + * @param role The role of the message sender (typically assistant) + * @param content The content of the sampled message + * @param model The name of the model that generated the message + * @param stopReason The reason why sampling stopped, if known + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CreateMessageResult( // @formatter:off + @JsonProperty("role") Role role, + @JsonProperty("content") Content content, + @JsonProperty("model") String model, + @JsonProperty("stopReason") StopReason stopReason, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public enum StopReason { + + // @formatter:off + @JsonProperty("endTurn") END_TURN("endTurn"), + @JsonProperty("stopSequence") STOP_SEQUENCE("stopSequence"), + @JsonProperty("maxTokens") MAX_TOKENS("maxTokens"), + @JsonProperty("unknown") UNKNOWN("unknown"); + // @formatter:on + + private final String value; + + StopReason(String value) { + this.value = value; + } + + @JsonCreator + private static StopReason of(String value) { + return Arrays.stream(StopReason.values()) + .filter(stopReason -> stopReason.value.equals(value)) + .findFirst() + .orElse(StopReason.UNKNOWN); + } + + } + + public CreateMessageResult(Role role, Content content, String model, StopReason stopReason) { + this(role, content, model, stopReason, null); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Role role = Role.ASSISTANT; + + private Content content; + + private String model; + + private StopReason stopReason = StopReason.END_TURN; + + private Map meta; + + public Builder role(Role role) { + this.role = role; + return this; + } + + public Builder content(Content content) { + this.content = content; + return this; + } + + public Builder model(String model) { + this.model = model; + return this; + } + + public Builder stopReason(StopReason stopReason) { + this.stopReason = stopReason; + return this; + } + + public Builder message(String message) { + this.content = new TextContent(message); + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public CreateMessageResult build() { + return new CreateMessageResult(role, content, model, stopReason, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/ModelHint.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/ModelHint.java new file mode 100644 index 000000000..7364889e4 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/ModelHint.java @@ -0,0 +1,23 @@ +package io.modelcontextprotocol.spec.schema.sample; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Hints to use for model selection. + * + * @param name A hint for a model name. The client SHOULD treat this as a substring of a + * model name; for example: `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022`, + * `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc., + * `claude` should match any Claude model. The client MAY also map the string to a + * different provider's model name or a different model family, as long as it fills a + * similar niche + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelHint(@JsonProperty("name") String name) { + public static ModelHint of(String name) { + return new ModelHint(name); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/ModelPreferences.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/ModelPreferences.java new file mode 100644 index 000000000..8fb98d6a4 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/ModelPreferences.java @@ -0,0 +1,81 @@ +package io.modelcontextprotocol.spec.schema.sample; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The server's preferences for model selection, requested of the client during sampling. + * + * @param hints Optional hints to use for model selection. If multiple hints are + * specified, the client MUST evaluate them in order (such that the first match is taken). + * The client SHOULD prioritize these hints over the numeric priorities, but MAY still use + * the priorities to select from ambiguous matches + * @param costPriority How much to prioritize cost when selecting a model. A value of 0 + * means cost is not important, while a value of 1 means cost is the most important factor + * @param speedPriority How much to prioritize sampling speed (latency) when selecting a + * model. A value of 0 means speed is not important, while a value of 1 means speed is the + * most important factor + * @param intelligencePriority How much to prioritize intelligence and capabilities when + * selecting a model. A value of 0 means intelligence is not important, while a value of 1 + * means intelligence is the most important factor + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelPreferences( // @formatter:off + @JsonProperty("hints") List hints, + @JsonProperty("costPriority") Double costPriority, + @JsonProperty("speedPriority") Double speedPriority, + @JsonProperty("intelligencePriority") Double intelligencePriority) { // @formatter:on + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private List hints; + + private Double costPriority; + + private Double speedPriority; + + private Double intelligencePriority; + + public Builder hints(List hints) { + this.hints = hints; + return this; + } + + public Builder addHint(String name) { + if (this.hints == null) { + this.hints = new ArrayList<>(); + } + this.hints.add(new ModelHint(name)); + return this; + } + + public Builder costPriority(Double costPriority) { + this.costPriority = costPriority; + return this; + } + + public Builder speedPriority(Double speedPriority) { + this.speedPriority = speedPriority; + return this; + } + + public Builder intelligencePriority(Double intelligencePriority) { + this.intelligencePriority = intelligencePriority; + return this; + } + + public ModelPreferences build() { + return new ModelPreferences(hints, costPriority, speedPriority, intelligencePriority); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/SamplingMessage.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/SamplingMessage.java new file mode 100644 index 000000000..742b0798a --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/sample/SamplingMessage.java @@ -0,0 +1,21 @@ +package io.modelcontextprotocol.spec.schema.sample; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Content; +import io.modelcontextprotocol.spec.McpSchema.Role; + +/** + * Describes a message issued to or received from an LLM API. + * + * @param role The sender or recipient of messages and data in a conversation + * @param content The content of the message + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SamplingMessage( // @formatter:off + @JsonProperty("role") Role role, + @JsonProperty("content") Content content) { // @formatter:on +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/CallToolRequest.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/CallToolRequest.java new file mode 100644 index 000000000..7203aac00 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/CallToolRequest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec.schema.tool; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Request; +import io.modelcontextprotocol.util.Assert; + +/** + * Used by the client to call a tool provided by the server. + * + * @param name The name of the tool to call. This must match a tool name from tools/list. + * @param arguments Arguments to pass to the tool. These must conform to the tool's input + * schema. + * @param meta Optional metadata about the request. This can include additional + * information like `progressToken` + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CallToolRequest( // @formatter:off + @JsonProperty("name") String name, + @JsonProperty("arguments") Map arguments, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + + public CallToolRequest(McpJsonMapper jsonMapper, String name, String jsonArguments) { + this(name, parseJsonArguments(jsonMapper, jsonArguments), null); + } + + public CallToolRequest(String name, Map arguments) { + this(name, arguments, null); + } + + private static Map parseJsonArguments(McpJsonMapper jsonMapper, String jsonArguments) { + try { + return jsonMapper.readValue(jsonArguments, McpSchema.MAP_TYPE_REF); + } + catch (IOException e) { + throw new IllegalArgumentException("Invalid arguments: " + jsonArguments, e); + } + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + private Map arguments; + + private Map meta; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder arguments(Map arguments) { + this.arguments = arguments; + return this; + } + + public Builder arguments(McpJsonMapper jsonMapper, String jsonArguments) { + this.arguments = parseJsonArguments(jsonMapper, jsonArguments); + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public Builder progressToken(Object progressToken) { + if (this.meta == null) { + this.meta = new HashMap<>(); + } + this.meta.put("progressToken", progressToken); + return this; + } + + public CallToolRequest build() { + Assert.hasText(name, "name must not be empty"); + return new CallToolRequest(name, arguments, meta); + } + + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/CallToolResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/CallToolResult.java new file mode 100644 index 000000000..a71c888e6 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/CallToolResult.java @@ -0,0 +1,156 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec.schema.tool; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Content; +import io.modelcontextprotocol.spec.McpSchema.Result; +import io.modelcontextprotocol.spec.McpSchema.TextContent; +import io.modelcontextprotocol.util.Assert; + +/** + * The server's response to a tools/call request from the client. + * + * @param content A list of content items representing the tool's output. Each item can be + * text, an image, or an embedded resource. + * @param isError If true, indicates that the tool execution failed and the content + * contains error information. If false or absent, indicates successful execution. + * @param structuredContent An optional JSON object that represents the structured result + * of the tool call. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CallToolResult( // @formatter:off + @JsonProperty("content") List content, + @JsonProperty("isError") Boolean isError, + @JsonProperty("structuredContent") Object structuredContent, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + /** + * Creates a builder for {@link CallToolResult}. + * @return a new builder instance + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link CallToolResult}. + */ + public static class Builder { + + private List content = new ArrayList<>(); + + private Boolean isError = false; + + private Object structuredContent; + + private Map meta; + + /** + * Sets the content list for the tool result. + * @param content the content list + * @return this builder + */ + public Builder content(List content) { + Assert.notNull(content, "content must not be null"); + this.content = content; + return this; + } + + public Builder structuredContent(Object structuredContent) { + Assert.notNull(structuredContent, "structuredContent must not be null"); + this.structuredContent = structuredContent; + return this; + } + + public Builder structuredContent(McpJsonMapper jsonMapper, String structuredContent) { + Assert.hasText(structuredContent, "structuredContent must not be empty"); + try { + this.structuredContent = jsonMapper.readValue(structuredContent, McpSchema.MAP_TYPE_REF); + } + catch (IOException e) { + throw new IllegalArgumentException("Invalid structured content: " + structuredContent, e); + } + return this; + } + + /** + * Sets the text content for the tool result. + * @param textContent the text content + * @return this builder + */ + public Builder textContent(List textContent) { + Assert.notNull(textContent, "textContent must not be null"); + textContent.stream().map(TextContent::new).forEach(this.content::add); + return this; + } + + /** + * Adds a content item to the tool result. + * @param contentItem the content item to add + * @return this builder + */ + public Builder addContent(Content contentItem) { + Assert.notNull(contentItem, "contentItem must not be null"); + if (this.content == null) { + this.content = new ArrayList<>(); + } + this.content.add(contentItem); + return this; + } + + /** + * Adds a text content item to the tool result. + * @param text the text content + * @return this builder + */ + public Builder addTextContent(String text) { + Assert.notNull(text, "text must not be null"); + return addContent(new TextContent(text)); + } + + /** + * Sets whether the tool execution resulted in an error. + * @param isError true if the tool execution failed, false otherwise + * @return this builder + */ + public Builder isError(Boolean isError) { + Assert.notNull(isError, "isError must not be null"); + this.isError = isError; + return this; + } + + /** + * Sets the metadata for the tool result. + * @param meta metadata + * @return this builder + */ + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + /** + * Builds a new {@link CallToolResult} instance. + * @return a new CallToolResult instance + */ + public CallToolResult build() { + return new CallToolResult(content, isError, structuredContent, meta); + } + + } + +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/JsonSchema.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/JsonSchema.java new file mode 100644 index 000000000..1a96c61fd --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/JsonSchema.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec.schema.tool; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A JSON Schema object that describes the expected structure of arguments or output. + * + * @param type The type of the schema (e.g., "object") + * @param properties The properties of the schema object + * @param required List of required property names + * @param additionalProperties Whether additional properties are allowed + * @param defs Schema definitions using the newer $defs keyword + * @param definitions Schema definitions using the legacy definitions keyword + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record JsonSchema( // @formatter:off + @JsonProperty("type") String type, + @JsonProperty("properties") Map properties, + @JsonProperty("required") List required, + @JsonProperty("additionalProperties") Boolean additionalProperties, + @JsonProperty("$defs") Map defs, + @JsonProperty("definitions") Map definitions) { // @formatter:on +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/ListToolsResult.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/ListToolsResult.java new file mode 100644 index 000000000..52a80ecc1 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/ListToolsResult.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec.schema.tool; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.spec.McpSchema.Result; + +/** + * The server's response to a tools/list request from the client. + * + * @param tools A list of tools that the server provides. + * @param nextCursor An optional cursor for pagination. If present, indicates there are + * more tools available. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListToolsResult( // @formatter:off + @JsonProperty("tools") List tools, + @JsonProperty("nextCursor") String nextCursor, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + + public ListToolsResult(List tools, String nextCursor) { + this(tools, nextCursor, null); + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/Tool.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/Tool.java new file mode 100644 index 000000000..69753d902 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/Tool.java @@ -0,0 +1,133 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec.schema.tool; + +import java.io.IOException; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.util.Assert; + +/** + * Represents a tool that the server provides. Tools enable servers to expose executable + * functionality to the system. Through these tools, you can interact with external + * systems, perform computations, and take actions in the real world. + * + * @param name A unique identifier for the tool. This name is used when calling the tool. + * @param title A human-readable title for the tool. + * @param description A human-readable description of what the tool does. This can be used + * by clients to improve the LLM's understanding of available tools. + * @param inputSchema A JSON Schema object that describes the expected structure of the + * arguments when calling this tool. This allows clients to validate tool + * @param outputSchema An optional JSON Schema object defining the structure of the tool's + * output returned in the structuredContent field of a CallToolResult. + * @param annotations Optional additional tool information. + * @param meta See specification for notes on _meta usage + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record Tool( // @formatter:off + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("inputSchema") JsonSchema inputSchema, + @JsonProperty("outputSchema") Map outputSchema, + @JsonProperty("annotations") ToolAnnotations annotations, + @JsonProperty("_meta") Map meta) { // @formatter:on + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + private String title; + + private String description; + + private JsonSchema inputSchema; + + private Map outputSchema; + + private ToolAnnotations annotations; + + private Map meta; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder inputSchema(JsonSchema inputSchema) { + this.inputSchema = inputSchema; + return this; + } + + public Builder inputSchema(McpJsonMapper jsonMapper, String inputSchema) { + this.inputSchema = Tool.parseSchema(jsonMapper, inputSchema); + return this; + } + + public Builder outputSchema(Map outputSchema) { + this.outputSchema = outputSchema; + return this; + } + + public Builder outputSchema(McpJsonMapper jsonMapper, String outputSchema) { + this.outputSchema = Tool.schemaToMap(jsonMapper, outputSchema); + return this; + } + + public Builder annotations(ToolAnnotations annotations) { + this.annotations = annotations; + return this; + } + + public Builder meta(Map meta) { + this.meta = meta; + return this; + } + + public Tool build() { + Assert.hasText(name, "name must not be empty"); + return new Tool(name, title, description, inputSchema, outputSchema, annotations, meta); + } + + } + + public static JsonSchema parseSchema(McpJsonMapper jsonMapper, String schema) { + try { + return jsonMapper.readValue(schema, JsonSchema.class); + } + catch (IOException e) { + throw new IllegalArgumentException("Invalid schema: " + schema, e); + } + } + + public static Map schemaToMap(McpJsonMapper jsonMapper, String schema) { + try { + return jsonMapper.readValue(schema, McpSchema.MAP_TYPE_REF); + } + catch (IOException e) { + throw new IllegalArgumentException("Invalid schema: " + schema, e); + } + } +} \ No newline at end of file diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/ToolAnnotations.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/ToolAnnotations.java new file mode 100644 index 000000000..71c3b0f10 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/schema/tool/ToolAnnotations.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec.schema.tool; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Additional properties describing a Tool to clients. + * + * NOTE: all properties in ToolAnnotations are **hints**. They are not guaranteed to + * provide a faithful description of tool behavior (including descriptive properties like + * `title`). + * + * Clients should never make tool use decisions based on ToolAnnotations received from + * untrusted servers. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ToolAnnotations( // @formatter:off + @JsonProperty("title") String title, + @JsonProperty("readOnlyHint") Boolean readOnlyHint, + @JsonProperty("destructiveHint") Boolean destructiveHint, + @JsonProperty("idempotentHint") Boolean idempotentHint, + @JsonProperty("openWorldHint") Boolean openWorldHint, + @JsonProperty("returnDirect") Boolean returnDirect) { // @formatter:on +} \ No newline at end of file diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java index 061a95e69..32b9e8c21 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java @@ -11,10 +11,10 @@ import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.McpClientTransport; -import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -23,11 +23,11 @@ */ public class MockMcpClientTransport implements McpClientTransport { - private final Sinks.Many inbound = Sinks.many().unicast().onBackpressureBuffer(); + private final Sinks.Many inbound = Sinks.many().unicast().onBackpressureBuffer(); - private final List sent = new ArrayList<>(); + private final List sent = new ArrayList<>(); - private final BiConsumer interceptor; + private final BiConsumer interceptor; private String protocolVersion = ProtocolVersions.MCP_2025_11_25; @@ -36,7 +36,7 @@ public MockMcpClientTransport() { }); } - public MockMcpClientTransport(BiConsumer interceptor) { + public MockMcpClientTransport(BiConsumer interceptor) { this.interceptor = interceptor; } @@ -49,35 +49,35 @@ public List protocolVersions() { return List.of(protocolVersion); } - public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) { + public void simulateIncomingMessage(JSONRPCMessage message) { if (inbound.tryEmitNext(message).isFailure()) { throw new RuntimeException("Failed to process incoming message " + message); } } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { sent.add(message); interceptor.accept(this, message); return Mono.empty(); } - public McpSchema.JSONRPCRequest getLastSentMessageAsRequest() { + public JSONRPCRequest getLastSentMessageAsRequest() { return (JSONRPCRequest) getLastSentMessage(); } - public McpSchema.JSONRPCNotification getLastSentMessageAsNotification() { + public JSONRPCNotification getLastSentMessageAsNotification() { return (JSONRPCNotification) getLastSentMessage(); } - public McpSchema.JSONRPCMessage getLastSentMessage() { + public JSONRPCMessage getLastSentMessage() { return !sent.isEmpty() ? sent.get(sent.size() - 1) : null; } private volatile boolean connected = false; @Override - public Mono connect(Function, Mono> handler) { + public Mono connect(Function, Mono> handler) { if (connected) { return Mono.error(new IllegalStateException("Already connected")); } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java index 897ae2ccc..b8e43a429 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java @@ -10,12 +10,11 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.Tool; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.ToolNameValidator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -41,11 +40,7 @@ class AsyncToolSpecificationBuilderTest { @Test void builderShouldCreateValidAsyncToolSpecification() { - Tool tool = McpSchema.Tool.builder() - .name("test-tool") - .title("A test tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool tool = Tool.builder().name("test-tool").title("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build(); McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder() .tool(tool) @@ -68,11 +63,7 @@ void builderShouldThrowExceptionWhenToolIsNull() { @Test void builderShouldThrowExceptionWhenCallToolIsNull() { - Tool tool = McpSchema.Tool.builder() - .name("test-tool") - .title("A test tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool tool = Tool.builder().name("test-tool").title("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build(); assertThatThrownBy(() -> McpServerFeatures.AsyncToolSpecification.builder().tool(tool).build()) .isInstanceOf(IllegalArgumentException.class) @@ -81,11 +72,7 @@ void builderShouldThrowExceptionWhenCallToolIsNull() { @Test void builderShouldAllowMethodChaining() { - Tool tool = McpSchema.Tool.builder() - .name("test-tool") - .title("A test tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool tool = Tool.builder().name("test-tool").title("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build(); McpServerFeatures.AsyncToolSpecification.Builder builder = McpServerFeatures.AsyncToolSpecification.builder(); // Then - verify method chaining returns the same builder instance @@ -97,11 +84,7 @@ void builderShouldAllowMethodChaining() { @Test void builtSpecificationShouldExecuteCallToolCorrectly() { - Tool tool = McpSchema.Tool.builder() - .name("calculator") - .title("Simple calculator") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool tool = Tool.builder().name("calculator").title("Simple calculator").inputSchema(EMPTY_JSON_SCHEMA).build(); String expectedResult = "42"; McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder() @@ -124,11 +107,7 @@ void builtSpecificationShouldExecuteCallToolCorrectly() { @Test void fromSyncShouldConvertSyncToolSpecificationCorrectly() { - Tool tool = McpSchema.Tool.builder() - .name("sync-tool") - .title("A sync tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool tool = Tool.builder().name("sync-tool").title("A sync tool").inputSchema(EMPTY_JSON_SCHEMA).build(); String expectedResult = "sync result"; // Create a sync tool specification diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/McpAsyncServerExchangeTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/McpAsyncServerExchangeTests.java index e6161a59f..0844f4d66 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/McpAsyncServerExchangeTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/McpAsyncServerExchangeTests.java @@ -13,6 +13,11 @@ import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.sample.SamplingMessage; import io.modelcontextprotocol.json.TypeRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -304,9 +309,7 @@ void testCreateElicitationWithNullCapabilities() { McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId", mockSession, null, clientInfo, McpTransportContext.EMPTY); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your name") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your name").build(); StepVerifier.create(exchangeWithNullCapabilities.createElicitation(elicitRequest)) .verifyErrorSatisfies(error -> { @@ -328,9 +331,7 @@ void testCreateElicitationWithoutElicitationCapabilities() { McpAsyncServerExchange exchangeWithoutElicitation = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithoutElicitation, clientInfo, McpTransportContext.EMPTY); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your name") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your name").build(); StepVerifier.create(exchangeWithoutElicitation.createElicitation(elicitRequest)).verifyErrorSatisfies(error -> { assertThat(error).isInstanceOf(IllegalStateException.class) @@ -359,7 +360,7 @@ void testCreateElicitationWithComplexRequest() { java.util.Map.of("type", "number"))); requestedSchema.put("required", java.util.List.of("name")); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() + ElicitRequest elicitRequest = ElicitRequest.builder() .message("Please provide your personal information") .requestedSchema(requestedSchema) .build(); @@ -368,8 +369,8 @@ void testCreateElicitationWithComplexRequest() { responseContent.put("name", "John Doe"); responseContent.put("age", 30); - McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.ACCEPT) + ElicitResult expectedResult = ElicitResult.builder() + .message(ElicitResult.Action.ACCEPT) .content(responseContent) .build(); @@ -378,7 +379,7 @@ void testCreateElicitationWithComplexRequest() { StepVerifier.create(exchangeWithElicitation.createElicitation(elicitRequest)).assertNext(result -> { assertThat(result).isEqualTo(expectedResult); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.ACCEPT); + assertThat(result.action()).isEqualTo(ElicitResult.Action.ACCEPT); assertThat(result.content()).isNotNull(); assertThat(result.content().get("name")).isEqualTo("John Doe"); assertThat(result.content().get("age")).isEqualTo(30); @@ -395,20 +396,16 @@ void testCreateElicitationWithDeclineAction() { McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide sensitive information") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide sensitive information").build(); - McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.DECLINE) - .build(); + ElicitResult expectedResult = ElicitResult.builder().message(ElicitResult.Action.DECLINE).build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); StepVerifier.create(exchangeWithElicitation.createElicitation(elicitRequest)).assertNext(result -> { assertThat(result).isEqualTo(expectedResult); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.DECLINE); + assertThat(result.action()).isEqualTo(ElicitResult.Action.DECLINE); }).verifyComplete(); } @@ -422,20 +419,16 @@ void testCreateElicitationWithCancelAction() { McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your information") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your information").build(); - McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.CANCEL) - .build(); + ElicitResult expectedResult = ElicitResult.builder().message(ElicitResult.Action.CANCEL).build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); StepVerifier.create(exchangeWithElicitation.createElicitation(elicitRequest)).assertNext(result -> { assertThat(result).isEqualTo(expectedResult); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.CANCEL); + assertThat(result.action()).isEqualTo(ElicitResult.Action.CANCEL); }).verifyComplete(); } @@ -449,9 +442,7 @@ void testCreateElicitationWithSessionError() { McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your name") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your name").build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.error(new RuntimeException("Session communication error"))); @@ -471,9 +462,9 @@ void testCreateMessageWithNullCapabilities() { McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId", mockSession, null, clientInfo, McpTransportContext.EMPTY); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages( + Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) .build(); StepVerifier.create(exchangeWithNullCapabilities.createMessage(createMessageRequest)) @@ -497,9 +488,9 @@ void testCreateMessageWithoutSamplingCapabilities() { McpAsyncServerExchange exchangeWithoutSampling = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithoutSampling, clientInfo, McpTransportContext.EMPTY); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages( + Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) .build(); StepVerifier.create(exchangeWithoutSampling.createMessage(createMessageRequest)).verifyErrorSatisfies(error -> { @@ -522,16 +513,16 @@ void testCreateMessageWithBasicRequest() { McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages( + Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) .build(); - McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder() + CreateMessageResult expectedResult = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(new McpSchema.TextContent("Hello! How can I help you today?")) .model("gpt-4") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), @@ -544,7 +535,7 @@ void testCreateMessageWithBasicRequest() { assertThat(result.content()).isInstanceOf(McpSchema.TextContent.class); assertThat(((McpSchema.TextContent) result.content()).text()).isEqualTo("Hello! How can I help you today?"); assertThat(result.model()).isEqualTo("gpt-4"); - assertThat(result.stopReason()).isEqualTo(McpSchema.CreateMessageResult.StopReason.END_TURN); + assertThat(result.stopReason()).isEqualTo(CreateMessageResult.StopReason.END_TURN); }).verifyComplete(); } @@ -559,17 +550,17 @@ void testCreateMessageWithImageContent() { capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); // Create request with image content - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages(Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.ImageContent(null, "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...", "image/jpeg")))) .build(); - McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder() + CreateMessageResult expectedResult = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(new McpSchema.TextContent("I can see an image. It appears to be a photograph.")) .model("gpt-4-vision") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), @@ -593,9 +584,8 @@ void testCreateMessageWithSessionError() { McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages(Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello")))) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), @@ -617,17 +607,17 @@ void testCreateMessageWithIncludeContext() { McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession, capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, - new McpSchema.TextContent("What files are available?")))) - .includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.ALL_SERVERS) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages(Arrays.asList( + new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("What files are available?")))) + .includeContext(CreateMessageRequest.ContextInclusionStrategy.ALL_SERVERS) .build(); - McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder() + CreateMessageResult expectedResult = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(new McpSchema.TextContent("Based on the available context, I can see several files...")) .model("gpt-4") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/McpSyncServerExchangeTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/McpSyncServerExchangeTests.java index fba733c9a..c42d335eb 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/McpSyncServerExchangeTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/McpSyncServerExchangeTests.java @@ -13,6 +13,11 @@ import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.sample.SamplingMessage; import io.modelcontextprotocol.json.TypeRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -301,9 +306,7 @@ void testCreateElicitationWithNullCapabilities() { McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange( asyncExchangeWithNullCapabilities); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your name") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your name").build(); assertThatThrownBy(() -> exchangeWithNullCapabilities.createElicitation(elicitRequest)) .isInstanceOf(IllegalStateException.class) @@ -324,9 +327,7 @@ void testCreateElicitationWithoutElicitationCapabilities() { mockSession, capabilitiesWithoutElicitation, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithoutElicitation = new McpSyncServerExchange(asyncExchangeWithoutElicitation); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your name") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your name").build(); assertThatThrownBy(() -> exchangeWithoutElicitation.createElicitation(elicitRequest)) .isInstanceOf(IllegalStateException.class) @@ -355,7 +356,7 @@ void testCreateElicitationWithComplexRequest() { java.util.Map.of("type", "number"))); requestedSchema.put("required", java.util.List.of("name")); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() + ElicitRequest elicitRequest = ElicitRequest.builder() .message("Please provide your personal information") .requestedSchema(requestedSchema) .build(); @@ -364,18 +365,18 @@ void testCreateElicitationWithComplexRequest() { responseContent.put("name", "John Doe"); responseContent.put("age", 30); - McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.ACCEPT) + ElicitResult expectedResult = ElicitResult.builder() + .message(ElicitResult.Action.ACCEPT) .content(responseContent) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); - McpSchema.ElicitResult result = exchangeWithElicitation.createElicitation(elicitRequest); + ElicitResult result = exchangeWithElicitation.createElicitation(elicitRequest); assertThat(result).isEqualTo(expectedResult); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.ACCEPT); + assertThat(result.action()).isEqualTo(ElicitResult.Action.ACCEPT); assertThat(result.content()).isNotNull(); assertThat(result.content().get("name")).isEqualTo("John Doe"); assertThat(result.content().get("age")).isEqualTo(30); @@ -392,21 +393,17 @@ void testCreateElicitationWithDeclineAction() { capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide sensitive information") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide sensitive information").build(); - McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.DECLINE) - .build(); + ElicitResult expectedResult = ElicitResult.builder().message(ElicitResult.Action.DECLINE).build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); - McpSchema.ElicitResult result = exchangeWithElicitation.createElicitation(elicitRequest); + ElicitResult result = exchangeWithElicitation.createElicitation(elicitRequest); assertThat(result).isEqualTo(expectedResult); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.DECLINE); + assertThat(result.action()).isEqualTo(ElicitResult.Action.DECLINE); } @Test @@ -420,21 +417,17 @@ void testCreateElicitationWithCancelAction() { capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your information") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your information").build(); - McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.CANCEL) - .build(); + ElicitResult expectedResult = ElicitResult.builder().message(ElicitResult.Action.CANCEL).build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); - McpSchema.ElicitResult result = exchangeWithElicitation.createElicitation(elicitRequest); + ElicitResult result = exchangeWithElicitation.createElicitation(elicitRequest); assertThat(result).isEqualTo(expectedResult); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.CANCEL); + assertThat(result.action()).isEqualTo(ElicitResult.Action.CANCEL); } @Test @@ -448,9 +441,7 @@ void testCreateElicitationWithSessionError() { capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation); - McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder() - .message("Please provide your name") - .build(); + ElicitRequest elicitRequest = ElicitRequest.builder().message("Please provide your name").build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class))) .thenReturn(Mono.error(new RuntimeException("Session communication error"))); @@ -472,9 +463,9 @@ void testCreateMessageWithNullCapabilities() { McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange( asyncExchangeWithNullCapabilities); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages( + Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) .build(); assertThatThrownBy(() -> exchangeWithNullCapabilities.createMessage(createMessageRequest)) @@ -497,9 +488,9 @@ void testCreateMessageWithoutSamplingCapabilities() { capabilitiesWithoutSampling, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithoutSampling = new McpSyncServerExchange(asyncExchangeWithoutSampling); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages( + Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) .build(); assertThatThrownBy(() -> exchangeWithoutSampling.createMessage(createMessageRequest)) @@ -522,30 +513,30 @@ void testCreateMessageWithBasicRequest() { capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages( + Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!")))) .build(); - McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder() + CreateMessageResult expectedResult = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(new McpSchema.TextContent("Hello! How can I help you today?")) .model("gpt-4") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); - McpSchema.CreateMessageResult result = exchangeWithSampling.createMessage(createMessageRequest); + CreateMessageResult result = exchangeWithSampling.createMessage(createMessageRequest); assertThat(result).isEqualTo(expectedResult); assertThat(result.role()).isEqualTo(McpSchema.Role.ASSISTANT); assertThat(result.content()).isInstanceOf(McpSchema.TextContent.class); assertThat(((McpSchema.TextContent) result.content()).text()).isEqualTo("Hello! How can I help you today?"); assertThat(result.model()).isEqualTo("gpt-4"); - assertThat(result.stopReason()).isEqualTo(McpSchema.CreateMessageResult.StopReason.END_TURN); + assertThat(result.stopReason()).isEqualTo(CreateMessageResult.StopReason.END_TURN); } @Test @@ -560,24 +551,24 @@ void testCreateMessageWithImageContent() { McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling); // Create request with image content - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages(Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.ImageContent(null, "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...", "image/jpeg")))) .build(); - McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder() + CreateMessageResult expectedResult = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(new McpSchema.TextContent("I can see an image. It appears to be a photograph.")) .model("gpt-4-vision") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); - McpSchema.CreateMessageResult result = exchangeWithSampling.createMessage(createMessageRequest); + CreateMessageResult result = exchangeWithSampling.createMessage(createMessageRequest); assertThat(result).isEqualTo(expectedResult); assertThat(result.role()).isEqualTo(McpSchema.Role.ASSISTANT); @@ -595,9 +586,8 @@ void testCreateMessageWithSessionError() { capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays - .asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello")))) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages(Arrays.asList(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello")))) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), @@ -620,24 +610,24 @@ void testCreateMessageWithIncludeContext() { capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY); McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling); - McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, - new McpSchema.TextContent("What files are available?")))) - .includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.ALL_SERVERS) + CreateMessageRequest createMessageRequest = CreateMessageRequest.builder() + .messages(Arrays.asList( + new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("What files are available?")))) + .includeContext(CreateMessageRequest.ContextInclusionStrategy.ALL_SERVERS) .build(); - McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder() + CreateMessageResult expectedResult = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(new McpSchema.TextContent("Based on the available context, I can see several files...")) .model("gpt-4") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest), any(TypeRef.class))) .thenReturn(Mono.just(expectedResult)); - McpSchema.CreateMessageResult result = exchangeWithSampling.createMessage(createMessageRequest); + CreateMessageResult result = exchangeWithSampling.createMessage(createMessageRequest); assertThat(result).isEqualTo(expectedResult); assertThat(((McpSchema.TextContent) result.content()).text()).contains("context"); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/ResourceTemplateListingTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/ResourceTemplateListingTest.java index 993ca717e..2d375aeeb 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/ResourceTemplateListingTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/ResourceTemplateListingTest.java @@ -4,7 +4,8 @@ package io.modelcontextprotocol.server; -import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import org.junit.jupiter.api.Test; import java.util.List; @@ -40,67 +41,59 @@ void testTemplateResourcesFilteredFromRegularListing() { @Test void testResourceListingWithMixedResources() { // Create resource list with both regular and template resources - List allResources = List.of( - McpSchema.Resource.builder() - .uri("file:///test/doc1.txt") - .name("Document 1") - .mimeType("text/plain") - .build(), - McpSchema.Resource.builder() - .uri("file:///test/doc2.txt") - .name("Document 2") - .mimeType("text/plain") - .build(), - McpSchema.Resource.builder() + List allResources = List.of( + Resource.builder().uri("file:///test/doc1.txt").name("Document 1").mimeType("text/plain").build(), + Resource.builder().uri("file:///test/doc2.txt").name("Document 2").mimeType("text/plain").build(), + Resource.builder() .uri("file:///test/{type}/document.txt") .name("Typed Document") .mimeType("text/plain") .build(), - McpSchema.Resource.builder() + Resource.builder() .uri("file:///users/{userId}/files/{fileId}") .name("User File") .mimeType("text/plain") .build()); // Apply the filter logic from McpAsyncServer line 438 - List filteredResources = allResources.stream() + List filteredResources = allResources.stream() .filter(resource -> !resource.uri().contains("{")) .collect(Collectors.toList()); // Verify only regular resources are included assertThat(filteredResources).hasSize(2); - assertThat(filteredResources).extracting(McpSchema.Resource::uri) + assertThat(filteredResources).extracting(Resource::uri) .containsExactlyInAnyOrder("file:///test/doc1.txt", "file:///test/doc2.txt"); } @Test void testResourceTemplatesListedSeparately() { // Create mixed resources - List resources = List.of( - McpSchema.Resource.builder() + List resources = List.of( + Resource.builder() .uri("file:///test/regular.txt") .name("Regular Resource") .mimeType("text/plain") .build(), - McpSchema.Resource.builder() + Resource.builder() .uri("file:///test/user/{userId}/profile.txt") .name("User Profile") .mimeType("text/plain") .build()); // Create explicit resource template - McpSchema.ResourceTemplate explicitTemplate = new McpSchema.ResourceTemplate( - "file:///test/document/{docId}/content.txt", "Document Template", null, "text/plain", null); + ResourceTemplate explicitTemplate = new ResourceTemplate("file:///test/document/{docId}/content.txt", + "Document Template", null, "text/plain", null); // Filter regular resources (those without template parameters) - List regularResources = resources.stream() + List regularResources = resources.stream() .filter(resource -> !resource.uri().contains("{")) .collect(Collectors.toList()); // Extract template resources (those with template parameters) - List templateResources = resources.stream() + List templateResources = resources.stream() .filter(resource -> resource.uri().contains("{")) - .map(resource -> new McpSchema.ResourceTemplate(resource.uri(), resource.name(), resource.description(), + .map(resource -> new ResourceTemplate(resource.uri(), resource.name(), resource.description(), resource.mimeType(), resource.annotations())) .collect(Collectors.toList()); @@ -113,9 +106,9 @@ void testResourceTemplatesListedSeparately() { assertThat(templateResources.get(0).uriTemplate()).isEqualTo("file:///test/user/{userId}/profile.txt"); // In the actual implementation, both would be combined - List allTemplates = List.of(templateResources.get(0), explicitTemplate); + List allTemplates = List.of(templateResources.get(0), explicitTemplate); assertThat(allTemplates).hasSize(2); - assertThat(allTemplates).extracting(McpSchema.ResourceTemplate::uriTemplate) + assertThat(allTemplates).extracting(ResourceTemplate::uriTemplate) .containsExactlyInAnyOrder("file:///test/user/{userId}/profile.txt", "file:///test/document/{docId}/content.txt"); } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java index 54c45e561..ee8f03193 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java @@ -10,11 +10,11 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.Tool; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.ToolNameValidator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/JSONRPCRequestMcpValidationTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/spec/JSONRPCRequestMcpValidationTest.java index fbe17d464..1edfa6d80 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/spec/JSONRPCRequestMcpValidationTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/spec/JSONRPCRequestMcpValidationTest.java @@ -4,6 +4,7 @@ package io.modelcontextprotocol.spec; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -20,7 +21,7 @@ public class JSONRPCRequestMcpValidationTest { @Test public void testValidStringId() { assertDoesNotThrow(() -> { - var request = new McpSchema.JSONRPCRequest("2.0", "test/method", "string-id", null); + var request = new JSONRPCRequest("2.0", "test/method", "string-id", null); assertEquals("string-id", request.id()); }); } @@ -28,7 +29,7 @@ public void testValidStringId() { @Test public void testValidIntegerId() { assertDoesNotThrow(() -> { - var request = new McpSchema.JSONRPCRequest("2.0", "test/method", 123, null); + var request = new JSONRPCRequest("2.0", "test/method", 123, null); assertEquals(123, request.id()); }); } @@ -36,7 +37,7 @@ public void testValidIntegerId() { @Test public void testValidLongId() { assertDoesNotThrow(() -> { - var request = new McpSchema.JSONRPCRequest("2.0", "test/method", 123L, null); + var request = new JSONRPCRequest("2.0", "test/method", 123L, null); assertEquals(123L, request.id()); }); } @@ -44,7 +45,7 @@ public void testValidLongId() { @Test public void testNullIdThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new McpSchema.JSONRPCRequest("2.0", "test/method", null, null); + new JSONRPCRequest("2.0", "test/method", null, null); }); assertTrue(exception.getMessage().contains("MCP requests MUST include an ID")); @@ -54,7 +55,7 @@ public void testNullIdThrowsException() { @Test public void testDoubleIdTypeThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new McpSchema.JSONRPCRequest("2.0", "test/method", 123.45, null); + new JSONRPCRequest("2.0", "test/method", 123.45, null); }); assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer")); @@ -63,7 +64,7 @@ public void testDoubleIdTypeThrowsException() { @Test public void testBooleanIdThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new McpSchema.JSONRPCRequest("2.0", "test/method", true, null); + new JSONRPCRequest("2.0", "test/method", true, null); }); assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer")); @@ -72,7 +73,7 @@ public void testBooleanIdThrowsException() { @Test public void testArrayIdThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new McpSchema.JSONRPCRequest("2.0", "test/method", new String[] { "array" }, null); + new JSONRPCRequest("2.0", "test/method", new String[] { "array" }, null); }); assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer")); @@ -81,7 +82,7 @@ public void testArrayIdThrowsException() { @Test public void testObjectIdThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new McpSchema.JSONRPCRequest("2.0", "test/method", new Object(), null); + new JSONRPCRequest("2.0", "test/method", new Object(), null); }); assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer")); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpClientSessionTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpClientSessionTests.java index 3de06f503..9adae2842 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpClientSessionTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpClientSessionTests.java @@ -10,6 +10,11 @@ import io.modelcontextprotocol.MockMcpClientTransport; import io.modelcontextprotocol.json.TypeRef; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,14 +59,14 @@ void testSendRequest() { Mono responseMono = session.sendRequest(TEST_METHOD, testParam, responseType); // Verify response handling StepVerifier.create(responseMono).then(() -> { - McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); + JSONRPCRequest request = transport.getLastSentMessageAsRequest(); transport.simulateIncomingMessage( - new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), responseData, null)); + new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), responseData, null)); }).consumeNextWith(response -> { // Verify the request was sent - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessageAsRequest(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCRequest.class); - McpSchema.JSONRPCRequest request = (McpSchema.JSONRPCRequest) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessageAsRequest(); + assertThat(sentMessage).isInstanceOf(JSONRPCRequest.class); + JSONRPCRequest request = (JSONRPCRequest) sentMessage; assertThat(request.method()).isEqualTo(TEST_METHOD); assertThat(request.params()).isEqualTo(testParam); assertThat(response).isEqualTo(responseData); @@ -81,12 +86,11 @@ void testSendRequestWithError() { // Verify error handling StepVerifier.create(responseMono).then(() -> { - McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); + JSONRPCRequest request = transport.getLastSentMessageAsRequest(); // Simulate error response - McpSchema.JSONRPCResponse.JSONRPCError error = new McpSchema.JSONRPCResponse.JSONRPCError( - McpSchema.ErrorCodes.METHOD_NOT_FOUND, "Method not found", null); - transport.simulateIncomingMessage( - new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, error)); + JSONRPCResponse.JSONRPCError error = new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND, + "Method not found", null); + transport.simulateIncomingMessage(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), null, error)); }).expectError(McpError.class).verify(); session.close(); @@ -121,9 +125,9 @@ void testSendNotification() { // Verify notification was sent StepVerifier.create(notificationMono).consumeSubscriptionWith(response -> { - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCNotification.class); - McpSchema.JSONRPCNotification notification = (McpSchema.JSONRPCNotification) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCNotification.class); + JSONRPCNotification notification = (JSONRPCNotification) sentMessage; assertThat(notification.method()).isEqualTo(TEST_NOTIFICATION); assertThat(notification.params()).isEqualTo(params); }).verifyComplete(); @@ -140,14 +144,13 @@ void testRequestHandling() { var session = new McpClientSession(TIMEOUT, transport, requestHandlers, Map.of(), Function.identity()); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, ECHO_METHOD, - "test-id", echoMessage); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, ECHO_METHOD, "test-id", echoMessage); transport.simulateIncomingMessage(request); // Verify response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.result()).isEqualTo(echoMessage); assertThat(response.error()).isNull(); @@ -166,8 +169,8 @@ void testNotificationHandling() { // Simulate incoming notification from the server Map notificationParams = Map.of("status", "ready"); - McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, - TEST_NOTIFICATION, notificationParams); + JSONRPCNotification notification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, TEST_NOTIFICATION, + notificationParams); transport.simulateIncomingMessage(notification); @@ -186,14 +189,13 @@ void testUnknownMethodHandling() { Function.identity()); // Simulate incoming request for unknown method - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, "unknown.method", - "test-id", null); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "unknown.method", "test-id", null); transport.simulateIncomingMessage(request); // Verify error response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.error()).isNotNull(); assertThat(response.error().code()).isEqualTo(McpSchema.ErrorCodes.METHOD_NOT_FOUND); @@ -214,14 +216,13 @@ void testRequestHandlerThrowsMcpErrorWithJsonRpcError() { Function.identity()); // Simulate incoming request that will trigger the error - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, testMethod, - "test-id", null); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, testMethod, "test-id", null); transport.simulateIncomingMessage(request); // Verify: The response should contain the custom error from McpError - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.error()).isNotNull(); assertThat(response.error().code()).isEqualTo(123); assertThat(response.error().message()).isEqualTo("Custom error message"); @@ -242,15 +243,14 @@ void testRequestHandlerThrowsGenericException() { Function.identity()); // Simulate incoming request that will trigger the error - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, testMethod, - "test-id", null); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, testMethod, "test-id", null); transport.simulateIncomingMessage(request); // Verify: The response should contain INTERNAL_ERROR with aggregated exception // messages in data field - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.error()).isNotNull(); assertThat(response.error().code()).isEqualTo(McpSchema.ErrorCodes.INTERNAL_ERROR); assertThat(response.error().message()).isEqualTo("Something went wrong"); @@ -276,15 +276,14 @@ void testRequestHandlerThrowsExceptionWithCause() { Function.identity()); // Simulate incoming request that will trigger the error - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, testMethod, - "test-id", null); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, testMethod, "test-id", null); transport.simulateIncomingMessage(request); // Verify: The response should contain INTERNAL_ERROR with full exception chain // in data field - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.error()).isNotNull(); assertThat(response.error().code()).isEqualTo(McpSchema.ErrorCodes.INTERNAL_ERROR); assertThat(response.error().message()).isEqualTo("Top level message"); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/json/gson/GsonMcpJsonMapperTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/spec/json/gson/GsonMcpJsonMapperTests.java index 498194d17..3b0fe834e 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/spec/json/gson/GsonMcpJsonMapperTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/spec/json/gson/GsonMcpJsonMapperTests.java @@ -1,6 +1,9 @@ package io.modelcontextprotocol.spec.json.gson; -import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import io.modelcontextprotocol.json.TypeRef; import org.junit.jupiter.api.Test; @@ -83,10 +86,10 @@ void deserializeJsonRpcMessageRequestUsingCustomMapper() throws IOException { } """; - var msg = McpSchema.deserializeJsonRpcMessage(mapper, json); - assertTrue(msg instanceof McpSchema.JSONRPCRequest); + var msg = JSONRPC.deserializeJsonRpcMessage(mapper, json); + assertTrue(msg instanceof JSONRPCRequest); - var req = (McpSchema.JSONRPCRequest) msg; + var req = (JSONRPCRequest) msg; assertEquals("2.0", req.jsonrpc()); assertEquals("ping", req.method()); assertNotNull(req.id()); @@ -105,7 +108,7 @@ void integrateWithMcpSchemaStaticMapperForStringParsing() { var gsonMapper = new GsonMcpJsonMapper(); // Tool builder parsing of input/output schema strings - var tool = McpSchema.Tool.builder().name("echo").description("Echo tool").inputSchema(gsonMapper, """ + var tool = Tool.builder().name("echo").description("Echo tool").inputSchema(gsonMapper, """ { "type": "object", "properties": { "x": { "type": "integer" } }, @@ -123,7 +126,7 @@ void integrateWithMcpSchemaStaticMapperForStringParsing() { assertTrue(tool.outputSchema().containsKey("properties")); // CallToolRequest builder parsing of JSON arguments string - var call = McpSchema.CallToolRequest.builder().name("echo").arguments(gsonMapper, "{\"x\": 123}").build(); + var call = CallToolRequest.builder().name("echo").arguments(gsonMapper, "{\"x\": 123}").build(); assertEquals("echo", call.name()); assertNotNull(call.arguments()); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/util/ToolsUtils.java b/mcp-core/src/test/java/io/modelcontextprotocol/util/ToolsUtils.java index ce8755223..27eac41da 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/util/ToolsUtils.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/util/ToolsUtils.java @@ -1,15 +1,15 @@ package io.modelcontextprotocol.util; -import io.modelcontextprotocol.spec.McpSchema; - import java.util.Collections; +import io.modelcontextprotocol.spec.schema.tool.JsonSchema; + public final class ToolsUtils { private ToolsUtils() { } - public static final McpSchema.JsonSchema EMPTY_JSON_SCHEMA = new McpSchema.JsonSchema("object", - Collections.emptyMap(), null, null, null, null); + public static final JsonSchema EMPTY_JSON_SCHEMA = new JsonSchema("object", Collections.emptyMap(), null, null, + null, null); } diff --git a/mcp-migrate/README.md b/mcp-migrate/README.md new file mode 100644 index 000000000..ff99262c4 --- /dev/null +++ b/mcp-migrate/README.md @@ -0,0 +1,114 @@ +# mcp-migrate + +OpenRewrite recipe module for automating the migration of user code from the **MCP Java SDK pre-1.1** API to the **1.1** release. + +In 1.1 the large `McpSchema` class was decomposed: every domain type that was previously a nested class or record inside `McpSchema` is now a standalone top-level class in a feature-specific sub-package of `io.modelcontextprotocol.spec`. This module provides [OpenRewrite](https://docs.openrewrite.org/) recipes that rewrite all affected import statements and type references automatically. + +For a full description of every breaking change see [MIGRATION-1.1.md](../MIGRATION-1.1.md) in the repository root. + +--- + +## Recipes + +| Recipe name | What it migrates | +|---|---| +| `io.modelcontextprotocol.sdk.migrations.McpSchemaMigration` | **Aggregate** – run this one to apply all migrations below | +| `io.modelcontextprotocol.sdk.migrations.MigrateJsonRpcTypes` | `JSONRPCMessage`, `JSONRPCRequest`, `JSONRPCNotification`, `JSONRPCResponse`, `JSONRPCError` → `spec.jsonrpc.*` | +| `io.modelcontextprotocol.sdk.migrations.MigrateResourceTypes` | `Resource`, `ResourceTemplate`, `ResourceContent`, `ResourceContents`, `TextResourceContents`, `BlobResourceContents`, `ListResourcesResult`, `ListResourceTemplatesResult`, `ReadResourceRequest`, `ReadResourceResult`, `SubscribeRequest`, `UnsubscribeRequest`, `Annotated`, `Identifier` → `spec.schema.resource.*` | +| `io.modelcontextprotocol.sdk.migrations.MigratePromptTypes` | `Prompt`, `PromptArgument`, `PromptMessage`, `ListPromptsResult`, `GetPromptRequest`, `GetPromptResult` → `spec.schema.prompt.*` | +| `io.modelcontextprotocol.sdk.migrations.MigrateToolTypes` | `Tool`, `JsonSchema`, `ToolAnnotations`, `ListToolsResult`, `CallToolRequest`, `CallToolResult` → `spec.schema.tool.*` | +| `io.modelcontextprotocol.sdk.migrations.MigrateSamplingTypes` | `SamplingMessage`, `ModelPreferences`, `ModelHint`, `CreateMessageRequest`, `CreateMessageRequest.ContextInclusionStrategy`, `CreateMessageResult` → `spec.schema.sample.*` | +| `io.modelcontextprotocol.sdk.migrations.MigrateElicitTypes` | `ElicitRequest`, `ElicitResult` → `spec.schema.elicit.*` | +| `io.modelcontextprotocol.sdk.migrations.MigrateStaticMembers` | `McpSchema.JSONRPC_VERSION` → `JSONRPC.JSONRPC_VERSION`; `McpSchema.deserializeJsonRpcMessage(…)` → `JSONRPC.deserializeJsonRpcMessage(…)` | + +--- + +## Usage + +### Prerequisites + +- Java 17+ +- Maven 3.6+ or Gradle 7+ + +### Maven + +Add the OpenRewrite Maven plugin to your build (you do **not** need to add `mcp-migrate` as a regular dependency): + +```xml + + + + org.openrewrite.maven + rewrite-maven-plugin + 5.47.0 + + + +``` + +Then run the migration on the command line: + +```bash +mvn org.openrewrite.maven:rewrite-maven-plugin:run \ + -Drewrite.recipeArtifactCoordinates=io.modelcontextprotocol.sdk:mcp-migrate:1.1.0 \ + -Drewrite.activeRecipes=io.modelcontextprotocol.sdk.migrations.McpSchemaMigration +``` + +To preview what will change without writing files, use the `dryRun` goal instead: + +```bash +mvn org.openrewrite.maven:rewrite-maven-plugin:dryRun \ + -Drewrite.recipeArtifactCoordinates=io.modelcontextprotocol.sdk:mcp-migrate:1.1.0 \ + -Drewrite.activeRecipes=io.modelcontextprotocol.sdk.migrations.McpSchemaMigration +``` + +### Gradle (Kotlin DSL) + +```kotlin +plugins { + id("org.openrewrite.rewrite") version "6.26.0" +} + +rewrite { + activeRecipe("io.modelcontextprotocol.sdk.migrations.McpSchemaMigration") + setRecipeArtifactCoordinates("io.modelcontextprotocol.sdk:mcp-migrate:1.1.0") +} +``` + +Run the migration: + +```bash +./gradlew rewriteRun +``` + +### Running a single sub-recipe + +If you only need to migrate one domain area (for example, only tool types), pass the specific sub-recipe name: + +```bash +mvn org.openrewrite.maven:rewrite-maven-plugin:run \ + -Drewrite.recipeArtifactCoordinates=io.modelcontextprotocol.sdk:mcp-migrate:1.1.0 \ + -Drewrite.activeRecipes=io.modelcontextprotocol.sdk.migrations.MigrateToolTypes +``` + +--- + +## What the recipes do and do not change + +**The recipes rewrite:** +- `import` statements — both single-type (`import io.modelcontextprotocol.spec.McpSchema.Tool`) and wildcard (`import io.modelcontextprotocol.spec.McpSchema.*`) imports +- Qualified type references in source code (e.g. `McpSchema.Tool.builder()`) +- Method parameter and return-type declarations +- Generic type arguments +- `instanceof` pattern expressions + +**The recipes do not change:** +- Runtime behaviour — the migrated code is semantically identical +- Types that remain in `McpSchema` (see the full list in [MIGRATION-1.1.md](../MIGRATION-1.1.md)) +- The removal of `sealed` from `Request`, `Result`, and `Notification` — this is a compile-time constraint change, not a source reference change; it does not require code edits in user code + +--- + +## After running the recipes + +Review the diff produced by OpenRewrite (`git diff`), compile, and run your tests. If any type references could not be resolved (typically because they appear inside generated sources or annotation processors), fix those manually using the mapping table in [MIGRATION-1.1.md](../MIGRATION-1.1.md). diff --git a/mcp-migrate/pom.xml b/mcp-migrate/pom.xml new file mode 100644 index 000000000..adbdc165a --- /dev/null +++ b/mcp-migrate/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + io.modelcontextprotocol.sdk + mcp-parent + 1.1.0-SNAPSHOT + + + mcp-migrate + jar + + Java MCP SDK Migration Recipes + + OpenRewrite recipes to migrate user code from the McpSchema nested-type API + (pre-1.1) to the new top-level package structure introduced in 1.1. + + + + + 8.48.0 + + + + + + org.openrewrite + rewrite-java + ${rewrite.version} + provided + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + + + none + + + + + + + diff --git a/mcp-migrate/src/main/resources/META-INF/rewrite/mcp-schema-migration.yml b/mcp-migrate/src/main/resources/META-INF/rewrite/mcp-schema-migration.yml new file mode 100644 index 000000000..663b9051d --- /dev/null +++ b/mcp-migrate/src/main/resources/META-INF/rewrite/mcp-schema-migration.yml @@ -0,0 +1,304 @@ +--- +# OpenRewrite migration recipes for the MCP Java SDK 1.1 release. +# +# These recipes automate migrating user code that depended on the nested types +# inside McpSchema (pre-1.1) to the new top-level classes introduced in 1.1. +# +# Usage (Maven): +# +# mvn org.openrewrite.maven:rewrite-maven-plugin:run \ +# -Drewrite.recipeArtifactCoordinates=io.modelcontextprotocol.sdk:mcp-migrate:LATEST \ +# -Drewrite.activeRecipes=io.modelcontextprotocol.sdk.migrations.McpSchemaMigration +# +# Usage (Gradle): +# +# rewrite { +# activeRecipe("io.modelcontextprotocol.sdk.migrations.McpSchemaMigration") +# setRecipeArtifactCoordinates("io.modelcontextprotocol.sdk:mcp-migrate:LATEST") +# } + +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.McpSchemaMigration +displayName: Migrate McpSchema nested types to top-level package classes (MCP SDK 1.1) +description: > + In MCP SDK 1.1 the large McpSchema class was decomposed. All domain types that + were previously defined as nested classes/records inside McpSchema were extracted + into dedicated top-level classes in feature-specific sub-packages under + io.modelcontextprotocol.spec. This recipe updates all import statements and + type references so that user code compiles against the new structure. +tags: + - mcp + - migration + - breaking-change +recipeList: + - io.modelcontextprotocol.sdk.migrations.MigrateJsonRpcTypes + - io.modelcontextprotocol.sdk.migrations.MigrateResourceTypes + - io.modelcontextprotocol.sdk.migrations.MigratePromptTypes + - io.modelcontextprotocol.sdk.migrations.MigrateToolTypes + - io.modelcontextprotocol.sdk.migrations.MigrateSamplingTypes + - io.modelcontextprotocol.sdk.migrations.MigrateElicitTypes + - io.modelcontextprotocol.sdk.migrations.MigrateStaticMembers + +--- +# --------------------------------------------------------------------------- +# JSON-RPC types +# Previously: McpSchema.JSONRPCMessage / JSONRPCRequest / JSONRPCNotification +# / JSONRPCResponse / JSONRPCResponse.JSONRPCError +# Now: io.modelcontextprotocol.spec.jsonrpc.* +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigrateJsonRpcTypes +displayName: Migrate McpSchema JSON-RPC nested types to io.modelcontextprotocol.spec.jsonrpc +description: > + Moves JSONRPCMessage, JSONRPCRequest, JSONRPCNotification, JSONRPCResponse, + and the nested JSONRPCError from McpSchema to the + io.modelcontextprotocol.spec.jsonrpc package. +tags: + - mcp + - migration +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse + # JSONRPCError is still nested inside JSONRPCResponse – only the outer class moved. + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse.JSONRPCError + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse.JSONRPCError + +--- +# --------------------------------------------------------------------------- +# Resource types +# Previously: McpSchema.Resource / ResourceTemplate / ResourceContent / +# ResourceContents / TextResourceContents / BlobResourceContents / +# ListResourcesResult / ListResourceTemplatesResult / +# ReadResourceRequest / ReadResourceResult / +# SubscribeRequest / UnsubscribeRequest / +# Annotated / Identifier +# Now: io.modelcontextprotocol.spec.schema.resource.* +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigrateResourceTypes +displayName: Migrate McpSchema resource nested types to io.modelcontextprotocol.spec.schema.resource +description: > + Moves all resource-related types from McpSchema into the + io.modelcontextprotocol.spec.schema.resource package. +tags: + - mcp + - migration +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.Annotated + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.Annotated + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.Identifier + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.Identifier + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ResourceContent + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ResourceContent + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ResourceContents + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ResourceContents + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.TextResourceContents + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.TextResourceContents + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.BlobResourceContents + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.BlobResourceContents + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.Resource + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.Resource + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ResourceTemplate + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ResourceTemplate + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ListResourcesResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ListResourcesResult + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ListResourceTemplatesResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ReadResourceRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ReadResourceResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.ReadResourceResult + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.SubscribeRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.SubscribeRequest + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.UnsubscribeRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest + +--- +# --------------------------------------------------------------------------- +# Prompt types +# Previously: McpSchema.Prompt / PromptArgument / PromptMessage / +# ListPromptsResult / GetPromptRequest / GetPromptResult +# Now: io.modelcontextprotocol.spec.schema.prompt.* +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigratePromptTypes +displayName: Migrate McpSchema prompt nested types to io.modelcontextprotocol.spec.schema.prompt +description: > + Moves all prompt-related types from McpSchema into the + io.modelcontextprotocol.spec.schema.prompt package. +tags: + - mcp + - migration +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.Prompt + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.prompt.Prompt + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.PromptArgument + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.prompt.PromptArgument + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.PromptMessage + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.prompt.PromptMessage + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ListPromptsResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.GetPromptRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.GetPromptResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.prompt.GetPromptResult + +--- +# --------------------------------------------------------------------------- +# Tool types +# Previously: McpSchema.Tool / JsonSchema / ToolAnnotations / +# ListToolsResult / CallToolRequest / CallToolResult +# Now: io.modelcontextprotocol.spec.schema.tool.* +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigrateToolTypes +displayName: Migrate McpSchema tool nested types to io.modelcontextprotocol.spec.schema.tool +description: > + Moves all tool-related types from McpSchema into the + io.modelcontextprotocol.spec.schema.tool package. +tags: + - mcp + - migration +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.JsonSchema + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.tool.JsonSchema + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ToolAnnotations + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.tool.ToolAnnotations + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.Tool + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.tool.Tool + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ListToolsResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.tool.ListToolsResult + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.CallToolRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.tool.CallToolRequest + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.CallToolResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.tool.CallToolResult + +--- +# --------------------------------------------------------------------------- +# Sampling types +# Previously: McpSchema.SamplingMessage / ModelPreferences / ModelHint / +# CreateMessageRequest / CreateMessageRequest.ContextInclusionStrategy / +# CreateMessageResult +# Now: io.modelcontextprotocol.spec.schema.sample.* +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigrateSamplingTypes +displayName: Migrate McpSchema sampling nested types to io.modelcontextprotocol.spec.schema.sample +description: > + Moves all sampling / LLM-interaction types from McpSchema into the + io.modelcontextprotocol.spec.schema.sample package. +tags: + - mcp + - migration +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.SamplingMessage + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.sample.SamplingMessage + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ModelPreferences + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.sample.ModelPreferences + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ModelHint + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.sample.ModelHint + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest + # ContextInclusionStrategy is still nested inside CreateMessageRequest – outer class moved. + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest.ContextInclusionStrategy + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest.ContextInclusionStrategy + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.CreateMessageResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.sample.CreateMessageResult + +--- +# --------------------------------------------------------------------------- +# Elicitation types +# Previously: McpSchema.ElicitRequest / ElicitResult +# Now: io.modelcontextprotocol.spec.schema.elicit.* +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigrateElicitTypes +displayName: Migrate McpSchema elicitation nested types to io.modelcontextprotocol.spec.schema.elicit +description: > + Moves ElicitRequest and ElicitResult from McpSchema into the + io.modelcontextprotocol.spec.schema.elicit package. +tags: + - mcp + - migration +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ElicitRequest + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.elicit.ElicitRequest + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.modelcontextprotocol.spec.McpSchema.ElicitResult + newFullyQualifiedTypeName: io.modelcontextprotocol.spec.schema.elicit.ElicitResult + +--- +# --------------------------------------------------------------------------- +# Static member migrations +# +# McpSchema.JSONRPC_VERSION -> JSONRPC.JSONRPC_VERSION +# McpSchema.deserializeJsonRpcMessage -> JSONRPC.deserializeJsonRpcMessage +# --------------------------------------------------------------------------- +type: specs.openrewrite.org/v1beta/recipe +name: io.modelcontextprotocol.sdk.migrations.MigrateStaticMembers +displayName: Migrate McpSchema static members to JSONRPC utility class +description: > + The JSONRPC_VERSION string constant and the deserializeJsonRpcMessage utility + method were moved from McpSchema to the new JSONRPC utility class in the + io.modelcontextprotocol.spec.jsonrpc package. +tags: + - mcp + - migration +recipeList: + # Constant: McpSchema.JSONRPC_VERSION -> JSONRPC.JSONRPC_VERSION + - org.openrewrite.java.ReplaceConstantWithAnotherConstant: + owningType: io.modelcontextprotocol.spec.McpSchema + constantName: JSONRPC_VERSION + newOwningType: io.modelcontextprotocol.spec.jsonrpc.JSONRPC + newConstantName: JSONRPC_VERSION + + # Static method: McpSchema.deserializeJsonRpcMessage(...) -> JSONRPC.deserializeJsonRpcMessage(...) + - org.openrewrite.java.ChangeMethodTargetToStatic: + methodPattern: >- + io.modelcontextprotocol.spec.McpSchema deserializeJsonRpcMessage( + io.modelcontextprotocol.json.McpJsonMapper, String) + fullyQualifiedTargetTypeName: io.modelcontextprotocol.spec.jsonrpc.JSONRPC diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java index e5d55c39d..880d0dc72 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java @@ -27,26 +27,34 @@ import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.server.McpSyncServerExchange; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; import io.modelcontextprotocol.spec.McpSchema.CompleteRequest; import io.modelcontextprotocol.spec.McpSchema.CompleteResult; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; import io.modelcontextprotocol.spec.McpSchema.InitializeResult; -import io.modelcontextprotocol.spec.McpSchema.ModelPreferences; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.PromptArgument; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.Role; import io.modelcontextprotocol.spec.McpSchema.Root; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptArgument; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.TextResourceContents; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.sample.ModelPreferences; +import io.modelcontextprotocol.spec.schema.sample.SamplingMessage; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.util.Utils; import net.javacrumbs.jsonunit.core.Option; import org.junit.jupiter.params.ParameterizedTest; @@ -108,7 +116,7 @@ void testCreateMessageWithoutSamplingCapabilities(String clientType) { McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder() .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) .callHandler((exchange, request) -> { - return exchange.createMessage(mock(McpSchema.CreateMessageRequest.class)) + return exchange.createMessage(mock(CreateMessageRequest.class)) .then(Mono.just(mock(CallToolResult.class))); }) .build(); @@ -123,7 +131,7 @@ void testCreateMessageWithoutSamplingCapabilities(String clientType) { assertThat(client.initialize()).isNotNull(); try { - client.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + client.callTool(new CallToolRequest("tool1", Map.of())); } catch (McpError e) { assertThat(e).isInstanceOf(McpError.class) @@ -149,7 +157,7 @@ void testCreateMessageSuccess(String clientType) { CreateMessageResult.StopReason.STOP_SEQUENCE); }; - CallToolResult callResponse = McpSchema.CallToolResult.builder() + CallToolResult callResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE")) .build(); @@ -159,9 +167,9 @@ void testCreateMessageSuccess(String clientType) { .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) .callHandler((exchange, request) -> { - var createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, - new McpSchema.TextContent("Test message")))) + var createMessageRequest = CreateMessageRequest.builder() + .messages(List + .of(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message")))) .modelPreferences(ModelPreferences.builder() .hints(List.of()) .costPriority(1.0) @@ -186,7 +194,7 @@ void testCreateMessageSuccess(String clientType) { InitializeResult initResult = mcpClient.initialize(); assertThat(initResult).isNotNull(); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(response).isNotNull(); assertThat(response).isEqualTo(callResponse); @@ -228,7 +236,7 @@ void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws Interr // Server - CallToolResult callResponse = McpSchema.CallToolResult.builder() + CallToolResult callResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE")) .build(); @@ -238,9 +246,9 @@ void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws Interr .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) .callHandler((exchange, request) -> { - var createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, - new McpSchema.TextContent("Test message")))) + var createMessageRequest = CreateMessageRequest.builder() + .messages(List + .of(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message")))) .modelPreferences(ModelPreferences.builder() .hints(List.of()) .costPriority(1.0) @@ -267,7 +275,7 @@ void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws Interr InitializeResult initResult = mcpClient.initialize(); assertThat(initResult).isNotNull(); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(response).isNotNull(); assertThat(response).isEqualTo(callResponse); @@ -305,7 +313,7 @@ void testCreateMessageWithRequestTimeoutFail(String clientType) throws Interrupt CreateMessageResult.StopReason.STOP_SEQUENCE); }; - CallToolResult callResponse = McpSchema.CallToolResult.builder() + CallToolResult callResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE")) .build(); @@ -313,9 +321,9 @@ void testCreateMessageWithRequestTimeoutFail(String clientType) throws Interrupt .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) .callHandler((exchange, request) -> { - var createMessageRequest = McpSchema.CreateMessageRequest.builder() - .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, - new McpSchema.TextContent("Test message")))) + var createMessageRequest = CreateMessageRequest.builder() + .messages(List + .of(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message")))) .modelPreferences(ModelPreferences.builder() .hints(List.of()) .costPriority(1.0) @@ -342,7 +350,7 @@ void testCreateMessageWithRequestTimeoutFail(String clientType) throws Interrupt assertThat(initResult).isNotNull(); assertThatExceptionOfType(McpError.class).isThrownBy(() -> { - mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + mcpClient.callTool(new CallToolRequest("tool1", Map.of())); }).withMessageContaining("1000ms"); } finally { @@ -373,7 +381,7 @@ void testCreateElicitationWithoutElicitationCapabilities(String clientType) { assertThat(client.initialize()).isNotNull(); try { - client.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + client.callTool(new CallToolRequest("tool1", Map.of())); } catch (McpError e) { assertThat(e).isInstanceOf(McpError.class) @@ -391,25 +399,24 @@ void testCreateElicitationSuccess(String clientType) { var clientBuilder = clientBuilders.get(clientType); - Function elicitationHandler = request -> { + Function elicitationHandler = request -> { assertThat(request.message()).isNotEmpty(); assertThat(request.requestedSchema()).isNotNull(); - return new McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, - Map.of("message", request.message())); + return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message())); }; - CallToolResult callResponse = McpSchema.CallToolResult.builder() + CallToolResult callResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE")) .build(); - AtomicReference elicitResultRef = new AtomicReference<>(); + AtomicReference elicitResultRef = new AtomicReference<>(); McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder() .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) .callHandler((exchange, request) -> { - var elicitationRequest = McpSchema.ElicitRequest.builder() + var elicitationRequest = ElicitRequest.builder() .message("Test message") .requestedSchema( Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string")))) @@ -431,13 +438,13 @@ void testCreateElicitationSuccess(String clientType) { InitializeResult initResult = mcpClient.initialize(); assertThat(initResult).isNotNull(); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(response).isNotNull(); assertThat(response).isEqualTo(callResponse); assertWith(elicitResultRef.get(), result -> { assertThat(result).isNotNull(); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.ACCEPT); + assertThat(result.action()).isEqualTo(ElicitResult.Action.ACCEPT); assertThat(result.content().get("message")).isEqualTo("Test message"); }); } @@ -458,7 +465,7 @@ void testCreateElicitationWithRequestTimeoutSuccess(String clientType) { return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message())); }; - CallToolResult callResponse = McpSchema.CallToolResult.builder() + CallToolResult callResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE")) .build(); @@ -468,7 +475,7 @@ void testCreateElicitationWithRequestTimeoutSuccess(String clientType) { .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) .callHandler((exchange, request) -> { - var elicitationRequest = McpSchema.ElicitRequest.builder() + var elicitationRequest = ElicitRequest.builder() .message("Test message") .requestedSchema( Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string")))) @@ -493,13 +500,13 @@ void testCreateElicitationWithRequestTimeoutSuccess(String clientType) { InitializeResult initResult = mcpClient.initialize(); assertThat(initResult).isNotNull(); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(response).isNotNull(); assertThat(response).isEqualTo(callResponse); assertWith(resultRef.get(), result -> { assertThat(result).isNotNull(); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.ACCEPT); + assertThat(result.action()).isEqualTo(ElicitResult.Action.ACCEPT); assertThat(result.content().get("message")).isEqualTo("Test message"); }); } @@ -565,7 +572,7 @@ void testCreateElicitationWithRequestTimeoutFail(String clientType) { assertThat(initResult).isNotNull(); assertThatExceptionOfType(McpError.class).isThrownBy(() -> { - mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + mcpClient.callTool(new CallToolRequest("tool1", Map.of())); }).withMessageContaining("within 1000ms"); ElicitResult elicitResult = resultRef.get(); @@ -655,7 +662,7 @@ void testRootsWithoutCapability(String clientType) { // Attempt to list roots should fail try { - mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + mcpClient.callTool(new CallToolRequest("tool1", Map.of())); } catch (McpError e) { assertThat(e).isInstanceOf(McpError.class).hasMessage("Roots not supported"); @@ -772,7 +779,7 @@ void testToolCallSuccess(String clientType) { var clientBuilder = clientBuilders.get(clientType); var responseBodyIsNullOrBlank = new AtomicBoolean(false); - var callResponse = McpSchema.CallToolResult.builder() + var callResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=importantValue")) .build(); McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder() @@ -808,7 +815,7 @@ void testToolCallSuccess(String clientType) { assertThat(mcpClient.listTools().tools()).contains(tool1.tool()); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(responseBodyIsNullOrBlank.get()).isFalse(); assertThat(response).isNotNull().isEqualTo(callResponse); @@ -847,7 +854,7 @@ void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) { // We expect the tool call to fail immediately with the exception raised by // the offending tool instead of getting back a timeout. assertThatExceptionOfType(McpError.class) - .isThrownBy(() -> mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()))) + .isThrownBy(() -> mcpClient.callTool(new CallToolRequest("tool1", Map.of()))) .withMessageContaining("Timeout on blocking read"); } finally { @@ -865,7 +872,7 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) { var transportContextIsEmpty = new AtomicBoolean(false); var responseBodyIsNullOrBlank = new AtomicBoolean(false); - var expectedCallResponse = McpSchema.CallToolResult.builder() + var expectedCallResponse = CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=value")) .build(); McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder() @@ -885,7 +892,7 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) { e.printStackTrace(); } - return McpSchema.CallToolResult.builder() + return CallToolResult.builder() .addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=" + ctxValue)) .build(); }) @@ -902,7 +909,7 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) { assertThat(mcpClient.listTools().tools()).contains(tool1.tool()); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(transportContextIsNull.get()).isFalse(); assertThat(transportContextIsEmpty.get()).isFalse(); @@ -920,9 +927,7 @@ void testToolListChangeHandlingSuccess(String clientType) { var clientBuilder = clientBuilders.get(clientType); - var callResponse = McpSchema.CallToolResult.builder() - .addContent(new McpSchema.TextContent("CALL RESPONSE")) - .build(); + var callResponse = CallToolResult.builder().addContent(new McpSchema.TextContent("CALL RESPONSE")).build(); McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder() .tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build()) @@ -1111,7 +1116,7 @@ void testLoggingNotification(String clientType) throws InterruptedException { mcpClient.setLoggingLevel(McpSchema.LoggingLevel.NOTICE); // Call the tool that sends logging notifications - CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of())); + CallToolResult result = mcpClient.callTool(new CallToolRequest("logging-test", Map.of())); assertThat(result).isNotNull(); assertThat(result.content().get(0)).isInstanceOf(McpSchema.TextContent.class); assertThat(((McpSchema.TextContent) result.content().get(0)).text()).isEqualTo("Logging test completed"); @@ -1160,7 +1165,7 @@ void testProgressNotification(String clientType) throws InterruptedException { // Create server with a tool that sends logging notifications McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder() - .tool(McpSchema.Tool.builder() + .tool(Tool.builder() .name("progress-test") .description("Test progress notifications") .inputSchema(EMPTY_JSON_SCHEMA) @@ -1205,7 +1210,7 @@ void testProgressNotification(String clientType) throws InterruptedException { assertThat(initResult).isNotNull(); // Call the tool that sends progress notifications - McpSchema.CallToolRequest callToolRequest = McpSchema.CallToolRequest.builder() + CallToolRequest callToolRequest = CallToolRequest.builder() .name("progress-test") .meta(Map.of("progressToken", "test-progress-token")) .build(); @@ -1358,7 +1363,7 @@ void testPingSuccess(String clientType) { assertThat(initResult).isNotNull(); // Call the tool that tests ping async behavior - CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("ping-async-test", Map.of())); + CallToolResult result = mcpClient.callTool(new CallToolRequest("ping-async-test", Map.of())); assertThat(result).isNotNull(); assertThat(result.content().get(0)).isInstanceOf(McpSchema.TextContent.class); assertThat(((McpSchema.TextContent) result.content().get(0)).text()).isEqualTo("Async ping test completed"); @@ -1420,7 +1425,7 @@ void testStructuredOutputValidationSuccess(String clientType) { // Call tool with valid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -1488,7 +1493,7 @@ void testStructuredOutputOfObjectArrayValidationSuccess(String clientType) { assertThat(mcpClient.initialize()).isNotNull(); // Call tool with valid structured output of type array - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("getMembers", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("getMembers", Map.of())); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -1550,7 +1555,7 @@ void testStructuredOutputWithInHandlerError(String clientType) { // Call tool with valid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -1604,7 +1609,7 @@ void testStructuredOutputValidationFailure(String clientType) { // Call tool with invalid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -1654,7 +1659,7 @@ void testStructuredOutputMissingStructuredContent(String clientType) { // Call tool that should return structured content but doesn't CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -1725,8 +1730,7 @@ void testStructuredOutputRuntimeToolAddition(String clientType) { // Note: outputSchema might be null in sync server, but validation still works // Call dynamically added tool - CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("dynamic-tool", Map.of("count", 3))); + CallToolResult response = mcpClient.callTool(new CallToolRequest("dynamic-tool", Map.of("count", 3))); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -1759,17 +1763,13 @@ void testResourceSubscription(String clientType) throws InterruptedException { var clientBuilder = clientBuilders.get(clientType); String resourceUri = "test://subscribable-resource"; - var receivedContents = new AtomicReference>(); + var receivedContents = new AtomicReference>(); var latch = new CountDownLatch(1); McpServerFeatures.SyncResourceSpecification resourceSpec = new McpServerFeatures.SyncResourceSpecification( - McpSchema.Resource.builder() - .uri(resourceUri) - .name("Subscribable Resource") - .mimeType("text/plain") - .build(), - (exchange, req) -> new McpSchema.ReadResourceResult( - List.of(new McpSchema.TextResourceContents(resourceUri, "text/plain", "initial content")))); + Resource.builder().uri(resourceUri).name("Subscribable Resource").mimeType("text/plain").build(), + (exchange, req) -> new ReadResourceResult( + List.of(new TextResourceContents(resourceUri, "text/plain", "initial content")))); McpSyncServer mcpServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder().resources(true, false).build()) @@ -1783,7 +1783,7 @@ void testResourceSubscription(String clientType) throws InterruptedException { mcpClient.initialize(); - mcpClient.subscribeResource(new McpSchema.SubscribeRequest(resourceUri)); + mcpClient.subscribeResource(new SubscribeRequest(resourceUri)); mcpServer.notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(resourceUri)); @@ -1807,13 +1807,9 @@ void testResourceSubscription_afterUnsubscribe_noNotification(String clientType) var notificationCount = new java.util.concurrent.atomic.AtomicInteger(0); McpServerFeatures.SyncResourceSpecification resourceSpec = new McpServerFeatures.SyncResourceSpecification( - McpSchema.Resource.builder() - .uri(resourceUri) - .name("Subscribable Resource") - .mimeType("text/plain") - .build(), - (exchange, req) -> new McpSchema.ReadResourceResult( - List.of(new McpSchema.TextResourceContents(resourceUri, "text/plain", "content")))); + Resource.builder().uri(resourceUri).name("Subscribable Resource").mimeType("text/plain").build(), + (exchange, req) -> new ReadResourceResult( + List.of(new TextResourceContents(resourceUri, "text/plain", "content")))); McpSyncServer mcpServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder().resources(true, false).build()) @@ -1825,8 +1821,8 @@ void testResourceSubscription_afterUnsubscribe_noNotification(String clientType) mcpClient.initialize(); - mcpClient.subscribeResource(new McpSchema.SubscribeRequest(resourceUri)); - mcpClient.unsubscribeResource(new McpSchema.UnsubscribeRequest(resourceUri)); + mcpClient.subscribeResource(new SubscribeRequest(resourceUri)); + mcpClient.unsubscribeResource(new UnsubscribeRequest(resourceUri)); mcpServer.notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(resourceUri)); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/AbstractStatelessIntegrationTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/AbstractStatelessIntegrationTests.java index 7755ce456..36e275918 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/AbstractStatelessIntegrationTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/AbstractStatelessIntegrationTests.java @@ -21,11 +21,12 @@ import io.modelcontextprotocol.server.McpStatelessSyncServer; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.InitializeResult; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import net.javacrumbs.jsonunit.core.Option; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -82,7 +83,7 @@ void testToolCallSuccess(String clientType) { var clientBuilder = clientBuilders.get(clientType); - var callResponse = McpSchema.CallToolResult.builder() + var callResponse = CallToolResult.builder() .content(List.of(new McpSchema.TextContent("CALL RESPONSE"))) .isError(false) .build(); @@ -120,7 +121,7 @@ void testToolCallSuccess(String clientType) { assertThat(mcpClient.listTools().tools()).contains(tool1.tool()); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(response).isNotNull().isEqualTo(callResponse); } @@ -159,7 +160,7 @@ void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) { // the offending tool // instead of getting back a timeout. assertThatExceptionOfType(McpError.class) - .isThrownBy(() -> mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()))) + .isThrownBy(() -> mcpClient.callTool(new CallToolRequest("tool1", Map.of()))) .withMessageContaining("Timeout on blocking read"); } finally { @@ -173,7 +174,7 @@ void testToolListChangeHandlingSuccess(String clientType) { var clientBuilder = clientBuilders.get(clientType); - var callResponse = McpSchema.CallToolResult.builder() + var callResponse = CallToolResult.builder() .content(List.of(new McpSchema.TextContent("CALL RESPONSE"))) .isError(false) .build(); @@ -320,7 +321,7 @@ void testStructuredOutputValidationSuccess(String clientType) { // Call tool with valid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -389,7 +390,7 @@ void testStructuredOutputOfObjectArrayValidationSuccess(String clientType) { assertThat(mcpClient.initialize()).isNotNull(); // Call tool with valid structured output of type array - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("getMembers", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("getMembers", Map.of())); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -452,7 +453,7 @@ void testStructuredOutputWithInHandlerError(String clientType) { // Call tool with valid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -507,7 +508,7 @@ void testStructuredOutputValidationFailure(String clientType) { // Call tool with invalid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -557,7 +558,7 @@ void testStructuredOutputMissingStructuredContent(String clientType) { // Call tool that should return structured content but doesn't CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -628,8 +629,7 @@ void testStructuredOutputRuntimeToolAddition(String clientType) { // Note: outputSchema might be null in sync server, but validation still works // Call dynamically added tool - CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("dynamic-tool", Map.of("count", 3))); + CallToolResult response = mcpClient.callTool(new CallToolRequest("dynamic-tool", Map.of("count", 3))); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientResiliencyTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientResiliencyTests.java index 8fb8093ac..93f0961dd 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientResiliencyTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientResiliencyTests.java @@ -11,6 +11,8 @@ import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpTransport; import io.modelcontextprotocol.spec.McpTransportSessionClosedException; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.Tool; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -195,7 +197,7 @@ void testSessionInvalidation() { @Test void testCallTool() { withClient(createMcpTransport(), mcpAsyncClient -> { - AtomicReference> tools = new AtomicReference<>(); + AtomicReference> tools = new AtomicReference<>(); StepVerifier.create(mcpAsyncClient.initialize()).expectNextCount(1).verifyComplete(); StepVerifier.create(mcpAsyncClient.listTools()) .consumeNextWith(list -> tools.set(list.tools())) @@ -205,7 +207,7 @@ void testCallTool() { String name = tools.get().get(0).name(); // Assuming this is the echo tool - McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(name, Map.of("message", "hello")); + CallToolRequest request = new CallToolRequest(name, Map.of("message", "hello")); StepVerifier.create(mcpAsyncClient.callTool(request)).expectError().verify(); reconnect(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java index 2ef45a1e0..fd90dbb07 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java @@ -26,28 +26,28 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; - +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.BlobResourceContents; -import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; -import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; -import io.modelcontextprotocol.spec.McpSchema.ResourceContents; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; import io.modelcontextprotocol.spec.McpSchema.Root; -import io.modelcontextprotocol.spec.McpSchema.SubscribeRequest; -import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; -import io.modelcontextprotocol.spec.McpSchema.Tool; -import io.modelcontextprotocol.spec.McpSchema.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.resource.BlobResourceContents; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.TextResourceContents; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; import io.modelcontextprotocol.spec.McpTransport; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.Tool; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -243,7 +243,7 @@ void testCallToolWithMessageAnnotations(String messageType) { withClient(transport, mcpAsyncClient -> { StepVerifier.create(mcpAsyncClient.initialize() - .then(mcpAsyncClient.callTool(new McpSchema.CallToolRequest("annotatedMessage", + .then(mcpAsyncClient.callTool(new CallToolRequest("annotatedMessage", Map.of("messageType", messageType, "includeImage", true))))) .consumeNextWith(result -> { assertThat(result).isNotNull(); @@ -504,14 +504,11 @@ void testRemoveNonExistentRoot() { void testReadResource() { AtomicInteger resourceCount = new AtomicInteger(); withClient(createMcpTransport(), client -> { - Flux resources = client.initialize() - .then(client.listResources(null)) - .flatMapMany(r -> { - List l = r.resources(); - resourceCount.set(l.size()); - return Flux.fromIterable(l); - }) - .flatMap(r -> client.readResource(r)); + Flux resources = client.initialize().then(client.listResources(null)).flatMapMany(r -> { + List l = r.resources(); + resourceCount.set(l.size()); + return Flux.fromIterable(l); + }).flatMap(r -> client.readResource(r)); StepVerifier.create(resources) .recordWith(ArrayList::new) @@ -603,7 +600,7 @@ void testListAllResourceTemplatesReturnsImmutableList() { assertThat(result.resourceTemplates()).isNotNull(); // Verify that the returned list is immutable assertThatThrownBy(() -> result.resourceTemplates() - .add(new McpSchema.ResourceTemplate("test://template", "test", "test", null, null, null))) + .add(new ResourceTemplate("test://template", "test", "test", null, null, null))) .isInstanceOf(UnsupportedOperationException.class); }) .verifyComplete(); @@ -757,14 +754,14 @@ void testSampling() { receivedMessage.set(messageText.text()); receivedMaxTokens.set(request.maxTokens()); - return Mono - .just(new McpSchema.CreateMessageResult(McpSchema.Role.USER, new McpSchema.TextContent(response), - "modelId", McpSchema.CreateMessageResult.StopReason.END_TURN)); + return Mono.just(new CreateMessageResult(McpSchema.Role.USER, new McpSchema.TextContent(response), + "modelId", CreateMessageResult.StopReason.END_TURN)); }), client -> { StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete(); - StepVerifier.create(client.callTool( - new McpSchema.CallToolRequest("sampleLLM", Map.of("prompt", message, "maxTokens", maxTokens)))) + StepVerifier + .create(client + .callTool(new CallToolRequest("sampleLLM", Map.of("prompt", message, "maxTokens", maxTokens)))) .consumeNextWith(result -> { // Verify tool response to ensure our sampling response was passed // through diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java index 26d60568a..371ef245b 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java @@ -27,25 +27,25 @@ import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.BlobResourceContents; -import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; -import io.modelcontextprotocol.spec.McpSchema.ListResourceTemplatesResult; -import io.modelcontextprotocol.spec.McpSchema.ListResourcesResult; -import io.modelcontextprotocol.spec.McpSchema.ListToolsResult; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; -import io.modelcontextprotocol.spec.McpSchema.ResourceContents; import io.modelcontextprotocol.spec.McpSchema.Root; -import io.modelcontextprotocol.spec.McpSchema.SubscribeRequest; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; -import io.modelcontextprotocol.spec.McpSchema.Tool; -import io.modelcontextprotocol.spec.McpSchema.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.resource.BlobResourceContents; +import io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceContents; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.TextResourceContents; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -246,8 +246,8 @@ void testCallToolWithMessageAnnotations(String messageType) { withClient(transport, client -> { client.initialize(); - McpSchema.CallToolResult result = client.callTool(new McpSchema.CallToolRequest("annotatedMessage", - Map.of("messageType", messageType, "includeImage", true))); + CallToolResult result = client.callTool( + new CallToolRequest("annotatedMessage", Map.of("messageType", messageType, "includeImage", true))); assertThat(result).isNotNull(); assertThat(result.isError()).isNotEqualTo(true); @@ -610,13 +610,13 @@ void testSampling() { receivedMessage.set(messageText.text()); receivedMaxTokens.set(request.maxTokens()); - return new McpSchema.CreateMessageResult(McpSchema.Role.USER, new McpSchema.TextContent(response), - "modelId", McpSchema.CreateMessageResult.StopReason.END_TURN); + return new CreateMessageResult(McpSchema.Role.USER, new McpSchema.TextContent(response), "modelId", + CreateMessageResult.StopReason.END_TURN); }), client -> { client.initialize(); - McpSchema.CallToolResult result = client.callTool( - new McpSchema.CallToolRequest("sampleLLM", Map.of("prompt", message, "maxTokens", maxTokens))); + CallToolResult result = client + .callTool(new CallToolRequest("sampleLLM", Map.of("prompt", message, "maxTokens", maxTokens))); // Verify tool response to ensure our sampling response was passed through assertThat(result.content()).hasAtLeastOneElementOfType(McpSchema.TextContent.class); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java index 9cd1191d1..03a71067b 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java @@ -8,15 +8,16 @@ import java.util.List; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; -import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.PromptMessage; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; -import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptMessage; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -97,11 +98,7 @@ void testImmediateClose() { // --------------------------------------- @Test void testAddToolCall() { - Tool newTool = McpSchema.Tool.builder() - .name("new-tool") - .title("New test tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool newTool = Tool.builder().name("new-tool").title("New test tool").inputSchema(EMPTY_JSON_SCHEMA).build(); var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) @@ -118,7 +115,7 @@ void testAddToolCall() { @Test void testAddDuplicateToolCall() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name(TEST_TOOL_NAME) .title("Duplicate tool") .inputSchema(EMPTY_JSON_SCHEMA) @@ -142,7 +139,7 @@ void testAddDuplicateToolCall() { @Test void testDuplicateToolCallDuringBuilding() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name("duplicate-build-toolcall") .title("Duplicate toolcall during building") .inputSchema(EMPTY_JSON_SCHEMA) @@ -162,7 +159,7 @@ void testDuplicateToolCallDuringBuilding() { @Test void testDuplicateToolsInBatchListRegistration() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name("batch-list-tool") .title("Duplicate tool in batch list") .inputSchema(EMPTY_JSON_SCHEMA) @@ -190,7 +187,7 @@ void testDuplicateToolsInBatchListRegistration() { @Test void testDuplicateToolsInBatchVarargsRegistration() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name("batch-varargs-tool") .title("Duplicate tool in batch varargs") .inputSchema(EMPTY_JSON_SCHEMA) @@ -215,11 +212,7 @@ void testDuplicateToolsInBatchVarargsRegistration() { @Test void testRemoveTool() { - Tool too = McpSchema.Tool.builder() - .name(TEST_TOOL_NAME) - .title("Duplicate tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool too = Tool.builder().name(TEST_TOOL_NAME).title("Duplicate tool").inputSchema(EMPTY_JSON_SCHEMA).build(); var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) @@ -246,11 +239,7 @@ void testRemoveNonexistentTool() { @Test void testNotifyToolsListChanged() { - Tool too = McpSchema.Tool.builder() - .name(TEST_TOOL_NAME) - .title("Duplicate tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool too = Tool.builder().name(TEST_TOOL_NAME).title("Duplicate tool").inputSchema(EMPTY_JSON_SCHEMA).build(); var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) @@ -425,7 +414,7 @@ void testAddResourceTemplate() { .capabilities(ServerCapabilities.builder().resources(true, false).build()) .build(); - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -445,7 +434,7 @@ void testAddResourceTemplateWithoutCapability() { // Create a server without resource capabilities McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -463,7 +452,7 @@ void testAddResourceTemplateWithoutCapability() { @Test void testRemoveResourceTemplate() { - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -508,7 +497,7 @@ void testRemoveNonexistentResourceTemplate() { @Test void testListResourceTemplates() { - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java index eee5f1a4d..f6467d52d 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java @@ -7,15 +7,16 @@ import java.util.List; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; -import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.PromptMessage; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; -import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptMessage; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -105,11 +106,7 @@ void testAddToolCall() { .capabilities(ServerCapabilities.builder().tools(true).build()) .build(); - Tool newTool = McpSchema.Tool.builder() - .name("new-tool") - .title("New test tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool newTool = Tool.builder().name("new-tool").title("New test tool").inputSchema(EMPTY_JSON_SCHEMA).build(); assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder() .tool(newTool) @@ -121,7 +118,7 @@ void testAddToolCall() { @Test void testAddDuplicateToolCall() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name(TEST_TOOL_NAME) .title("Duplicate tool") .inputSchema(EMPTY_JSON_SCHEMA) @@ -143,7 +140,7 @@ void testAddDuplicateToolCall() { @Test void testDuplicateToolCallDuringBuilding() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name("duplicate-build-toolcall") .title("Duplicate toolcall during building") .inputSchema(EMPTY_JSON_SCHEMA) @@ -161,7 +158,7 @@ void testDuplicateToolCallDuringBuilding() { @Test void testDuplicateToolsInBatchListRegistration() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name("batch-list-tool") .title("Duplicate tool in batch list") .inputSchema(EMPTY_JSON_SCHEMA) @@ -188,7 +185,7 @@ void testDuplicateToolsInBatchListRegistration() { @Test void testDuplicateToolsInBatchVarargsRegistration() { - Tool duplicateTool = McpSchema.Tool.builder() + Tool duplicateTool = Tool.builder() .name("batch-varargs-tool") .title("Duplicate tool in batch varargs") .inputSchema(EMPTY_JSON_SCHEMA) @@ -212,11 +209,7 @@ void testDuplicateToolsInBatchVarargsRegistration() { @Test void testRemoveTool() { - Tool tool = McpSchema.Tool.builder() - .name(TEST_TOOL_NAME) - .title("Test tool") - .inputSchema(EMPTY_JSON_SCHEMA) - .build(); + Tool tool = Tool.builder().name(TEST_TOOL_NAME).title("Test tool").inputSchema(EMPTY_JSON_SCHEMA).build(); var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) @@ -351,7 +344,7 @@ void testListResources() { resource, (exchange, req) -> new ReadResourceResult(List.of())); mcpSyncServer.addResource(specification); - List resources = mcpSyncServer.listResources(); + List resources = mcpSyncServer.listResources(); assertThat(resources).hasSize(1); assertThat(resources.get(0).uri()).isEqualTo(TEST_RESOURCE_URI); @@ -404,7 +397,7 @@ void testAddResourceTemplate() { .capabilities(ServerCapabilities.builder().resources(true, false).build()) .build(); - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -424,7 +417,7 @@ void testAddResourceTemplateWithoutCapability() { // Create a server without resource capabilities var serverWithoutResources = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -441,7 +434,7 @@ void testAddResourceTemplateWithoutCapability() { @Test void testRemoveResourceTemplate() { - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -485,7 +478,7 @@ void testRemoveNonexistentResourceTemplate() { @Test void testListResourceTemplates() { - McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder() + ResourceTemplate template = ResourceTemplate.builder() .uriTemplate("test://template/{id}") .name("test-template") .description("Test resource template") @@ -500,7 +493,7 @@ void testListResourceTemplates() { .resourceTemplates(specification) .build(); - List templates = mcpSyncServer.listResourceTemplates(); + List templates = mcpSyncServer.listResourceTemplates(); assertThat(templates).isNotNull(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/util/ToolsUtils.java b/mcp-test/src/main/java/io/modelcontextprotocol/util/ToolsUtils.java index ce8755223..27eac41da 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/util/ToolsUtils.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/util/ToolsUtils.java @@ -1,15 +1,15 @@ package io.modelcontextprotocol.util; -import io.modelcontextprotocol.spec.McpSchema; - import java.util.Collections; +import io.modelcontextprotocol.spec.schema.tool.JsonSchema; + public final class ToolsUtils { private ToolsUtils() { } - public static final McpSchema.JsonSchema EMPTY_JSON_SCHEMA = new McpSchema.JsonSchema("object", - Collections.emptyMap(), null, null, null, null); + public static final JsonSchema EMPTY_JSON_SCHEMA = new JsonSchema("object", Collections.emptyMap(), null, null, + null, null); } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java index 4e74dac3e..254b3cbd6 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java @@ -12,10 +12,10 @@ import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.McpClientTransport; -import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -24,11 +24,11 @@ */ public class MockMcpClientTransport implements McpClientTransport { - private final Sinks.Many inbound = Sinks.many().unicast().onBackpressureBuffer(); + private final Sinks.Many inbound = Sinks.many().unicast().onBackpressureBuffer(); - private final List sent = new ArrayList<>(); + private final List sent = new ArrayList<>(); - private final BiConsumer interceptor; + private final BiConsumer interceptor; private String protocolVersion = ProtocolVersions.MCP_2025_11_25; @@ -37,7 +37,7 @@ public MockMcpClientTransport() { }); } - public MockMcpClientTransport(BiConsumer interceptor) { + public MockMcpClientTransport(BiConsumer interceptor) { this.interceptor = interceptor; } @@ -50,35 +50,35 @@ public List protocolVersions() { return List.of(protocolVersion); } - public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) { + public void simulateIncomingMessage(JSONRPCMessage message) { if (inbound.tryEmitNext(message).isFailure()) { throw new RuntimeException("Failed to process incoming message " + message); } } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { sent.add(message); interceptor.accept(this, message); return Mono.empty(); } - public McpSchema.JSONRPCRequest getLastSentMessageAsRequest() { + public JSONRPCRequest getLastSentMessageAsRequest() { return (JSONRPCRequest) getLastSentMessage(); } - public McpSchema.JSONRPCNotification getLastSentMessageAsNotification() { + public JSONRPCNotification getLastSentMessageAsNotification() { return (JSONRPCNotification) getLastSentMessage(); } - public McpSchema.JSONRPCMessage getLastSentMessage() { + public JSONRPCMessage getLastSentMessage() { return !sent.isEmpty() ? sent.get(sent.size() - 1) : null; } private volatile boolean connected = false; @Override - public Mono connect(Function, Mono> handler) { + public Mono connect(Function, Mono> handler) { if (connected) { return Mono.error(new IllegalStateException("Already connected")); } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java index 9d43968e5..9647ee952 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java @@ -11,9 +11,9 @@ import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.TypeRef; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import io.modelcontextprotocol.spec.McpServerTransport; import reactor.core.publisher.Mono; @@ -22,35 +22,35 @@ */ public class MockMcpServerTransport implements McpServerTransport { - private final List sent = new ArrayList<>(); + private final List sent = new ArrayList<>(); - private final BiConsumer interceptor; + private final BiConsumer interceptor; public MockMcpServerTransport() { this((t, msg) -> { }); } - public MockMcpServerTransport(BiConsumer interceptor) { + public MockMcpServerTransport(BiConsumer interceptor) { this.interceptor = interceptor; } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { sent.add(message); interceptor.accept(this, message); return Mono.empty(); } - public McpSchema.JSONRPCRequest getLastSentMessageAsRequest() { + public JSONRPCRequest getLastSentMessageAsRequest() { return (JSONRPCRequest) getLastSentMessage(); } - public McpSchema.JSONRPCNotification getLastSentMessageAsNotification() { + public JSONRPCNotification getLastSentMessageAsNotification() { return (JSONRPCNotification) getLastSentMessage(); } - public McpSchema.JSONRPCMessage getLastSentMessage() { + public JSONRPCMessage getLastSentMessage() { return !sent.isEmpty() ? sent.get(sent.size() - 1) : null; } @@ -58,7 +58,7 @@ public void clearSentMessages() { sent.clear(); } - public List getAllSentMessages() { + public List getAllSentMessages() { return new ArrayList<>(sent); } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java index 9488870e5..8e312eaf7 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java @@ -4,9 +4,9 @@ package io.modelcontextprotocol; -import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; import io.modelcontextprotocol.spec.McpServerSession.Factory; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; import io.modelcontextprotocol.spec.McpServerTransportProvider; import reactor.core.publisher.Mono; @@ -51,7 +51,7 @@ public Mono closeGracefully() { return session.closeGracefully(); } - public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) { + public void simulateIncomingMessage(JSONRPCMessage message) { session.handle(message).subscribe(); } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java index 47a229afd..eb20b3d80 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java @@ -12,12 +12,29 @@ import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.MockMcpClientTransport; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.schema.sample.SamplingMessage; +import io.modelcontextprotocol.spec.schema.tool.Tool; import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities; import io.modelcontextprotocol.spec.McpSchema.InitializeResult; import io.modelcontextprotocol.spec.McpSchema.PaginatedRequest; import io.modelcontextprotocol.spec.McpSchema.Root; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptArgument; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -47,9 +64,9 @@ private static MockMcpClientTransport initializationEnabledTransport( mockServerCapabilities, mockServerInfo, "Test instructions"); return new MockMcpClientTransport((t, message) -> { - if (message instanceof McpSchema.JSONRPCRequest r && METHOD_INITIALIZE.equals(r.method())) { - McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, - r.id(), mockInitResult, null); + if (message instanceof JSONRPCRequest r && METHOD_INITIALIZE.equals(r.method())) { + JSONRPCResponse initResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, r.id(), mockInitResult, + null); t.simulateIncomingMessage(initResponse); } }).withProtocolVersion(ProtocolVersions.MCP_2025_11_25); @@ -72,9 +89,9 @@ void testSuccessfulInitialization() { InitializeResult result = asyncMcpClient.initialize().block(); // Verify initialized notification was sent - McpSchema.JSONRPCMessage notificationMessage = transport.getLastSentMessage(); - assertThat(notificationMessage).isInstanceOf(McpSchema.JSONRPCNotification.class); - McpSchema.JSONRPCNotification notification = (McpSchema.JSONRPCNotification) notificationMessage; + JSONRPCMessage notificationMessage = transport.getLastSentMessage(); + assertThat(notificationMessage).isInstanceOf(JSONRPCNotification.class); + JSONRPCNotification notification = (JSONRPCNotification) notificationMessage; assertThat(notification.method()).isEqualTo(McpSchema.METHOD_NOTIFICATION_INITIALIZED); // Verify initialization result @@ -98,10 +115,10 @@ void testToolsChangeNotificationHandling() throws IOException { MockMcpClientTransport transport = initializationEnabledTransport(); // Create a list to store received tools for verification - List receivedTools = new ArrayList<>(); + List receivedTools = new ArrayList<>(); // Create a consumer that will be called when tools change - Function, Mono> toolsChangeConsumer = tools -> Mono + Function, Mono> toolsChangeConsumer = tools -> Mono .fromRunnable(() -> receivedTools.addAll(tools)); // Create client with tools change consumer @@ -111,7 +128,7 @@ void testToolsChangeNotificationHandling() throws IOException { // Create a mock tools list that the server will return Map inputSchema = Map.of("type", "object", "properties", Map.of(), "required", List.of()); - McpSchema.Tool mockTool = McpSchema.Tool.builder() + Tool mockTool = Tool.builder() .name("test-tool-1") .description("Test Tool 1 Description") .inputSchema(JSON_MAPPER, JSON_MAPPER.writeValueAsString(inputSchema)) @@ -119,32 +136,32 @@ void testToolsChangeNotificationHandling() throws IOException { // Create page 1 response with nextPageToken String nextPageToken = "page2Token"; - McpSchema.ListToolsResult mockToolsResult1 = new McpSchema.ListToolsResult(List.of(mockTool), nextPageToken); + ListToolsResult mockToolsResult1 = new ListToolsResult(List.of(mockTool), nextPageToken); // Simulate server sending tools/list_changed notification - McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, + JSONRPCNotification notification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED, null); transport.simulateIncomingMessage(notification); // Simulate server response to first tools/list request - McpSchema.JSONRPCRequest toolsListRequest1 = transport.getLastSentMessageAsRequest(); + JSONRPCRequest toolsListRequest1 = transport.getLastSentMessageAsRequest(); assertThat(toolsListRequest1.method()).isEqualTo(McpSchema.METHOD_TOOLS_LIST); - McpSchema.JSONRPCResponse toolsListResponse1 = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, - toolsListRequest1.id(), mockToolsResult1, null); + JSONRPCResponse toolsListResponse1 = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, toolsListRequest1.id(), + mockToolsResult1, null); transport.simulateIncomingMessage(toolsListResponse1); // Create mock tools for page 2 - McpSchema.Tool mockTool2 = McpSchema.Tool.builder() + Tool mockTool2 = Tool.builder() .name("test-tool-2") .description("Test Tool 2 Description") .inputSchema(JSON_MAPPER, JSON_MAPPER.writeValueAsString(inputSchema)) .build(); // Create page 2 response with no nextPageToken (last page) - McpSchema.ListToolsResult mockToolsResult2 = new McpSchema.ListToolsResult(List.of(mockTool2), null); + ListToolsResult mockToolsResult2 = new ListToolsResult(List.of(mockTool2), null); // Simulate server response to second tools/list request with page token - McpSchema.JSONRPCRequest toolsListRequest2 = transport.getLastSentMessageAsRequest(); + JSONRPCRequest toolsListRequest2 = transport.getLastSentMessageAsRequest(); assertThat(toolsListRequest2.method()).isEqualTo(McpSchema.METHOD_TOOLS_LIST); // Verify the page token was included in the request @@ -152,8 +169,8 @@ void testToolsChangeNotificationHandling() throws IOException { assertThat(params).isNotNull(); assertThat(params.cursor()).isEqualTo(nextPageToken); - McpSchema.JSONRPCResponse toolsListResponse2 = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, - toolsListRequest2.id(), mockToolsResult2, null); + JSONRPCResponse toolsListResponse2 = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, toolsListRequest2.id(), + mockToolsResult2, null); transport.simulateIncomingMessage(toolsListResponse2); // Verify the consumer received all expected tools from both pages @@ -177,15 +194,15 @@ void testRootsListRequestHandling() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_ROOTS_LIST, "test-id", null); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_ROOTS_LIST, "test-id", + null); transport.simulateIncomingMessage(request); // Verify response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("test-id"); assertThat(response.result()) .isEqualTo(new McpSchema.ListRootsResult(List.of(new Root("file:///test/path", "test-root")))); @@ -199,10 +216,10 @@ void testResourcesChangeNotificationHandling() { MockMcpClientTransport transport = initializationEnabledTransport(); // Create a list to store received resources for verification - List receivedResources = new ArrayList<>(); + List receivedResources = new ArrayList<>(); // Create a consumer that will be called when resources change - Function, Mono> resourcesChangeConsumer = resources -> Mono + Function, Mono> resourcesChangeConsumer = resources -> Mono .fromRunnable(() -> receivedResources.addAll(resources)); // Create client with resources change consumer @@ -213,26 +230,25 @@ void testResourcesChangeNotificationHandling() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock resources list that the server will return - McpSchema.Resource mockResource = McpSchema.Resource.builder() + Resource mockResource = Resource.builder() .uri("test://resource") .name("Test Resource") .description("A test resource") .mimeType("text/plain") .build(); - McpSchema.ListResourcesResult mockResourcesResult = new McpSchema.ListResourcesResult(List.of(mockResource), - null); + ListResourcesResult mockResourcesResult = new ListResourcesResult(List.of(mockResource), null); // Simulate server sending resources/list_changed notification - McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, + JSONRPCNotification notification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED, null); transport.simulateIncomingMessage(notification); // Simulate server response to resources/list request - McpSchema.JSONRPCRequest resourcesListRequest = transport.getLastSentMessageAsRequest(); + JSONRPCRequest resourcesListRequest = transport.getLastSentMessageAsRequest(); assertThat(resourcesListRequest.method()).isEqualTo(McpSchema.METHOD_RESOURCES_LIST); - McpSchema.JSONRPCResponse resourcesListResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, - resourcesListRequest.id(), mockResourcesResult, null); + JSONRPCResponse resourcesListResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, resourcesListRequest.id(), + mockResourcesResult, null); transport.simulateIncomingMessage(resourcesListResponse); // Verify the consumer received the expected resources @@ -249,10 +265,10 @@ void testPromptsChangeNotificationHandling() { MockMcpClientTransport transport = initializationEnabledTransport(); // Create a list to store received prompts for verification - List receivedPrompts = new ArrayList<>(); + List receivedPrompts = new ArrayList<>(); // Create a consumer that will be called when prompts change - Function, Mono> promptsChangeConsumer = prompts -> Mono + Function, Mono> promptsChangeConsumer = prompts -> Mono .fromRunnable(() -> receivedPrompts.addAll(prompts)); // Create client with prompts change consumer @@ -261,21 +277,21 @@ void testPromptsChangeNotificationHandling() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock prompts list that the server will return - McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "Test Prompt", "Test Prompt Description", - List.of(new McpSchema.PromptArgument("arg1", "Test argument", "Test argument", true))); - McpSchema.ListPromptsResult mockPromptsResult = new McpSchema.ListPromptsResult(List.of(mockPrompt), null); + Prompt mockPrompt = new Prompt("test-prompt", "Test Prompt", "Test Prompt Description", + List.of(new PromptArgument("arg1", "Test argument", "Test argument", true))); + ListPromptsResult mockPromptsResult = new ListPromptsResult(List.of(mockPrompt), null); // Simulate server sending prompts/list_changed notification - McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, + JSONRPCNotification notification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED, null); transport.simulateIncomingMessage(notification); // Simulate server response to prompts/list request - McpSchema.JSONRPCRequest promptsListRequest = transport.getLastSentMessageAsRequest(); + JSONRPCRequest promptsListRequest = transport.getLastSentMessageAsRequest(); assertThat(promptsListRequest.method()).isEqualTo(McpSchema.METHOD_PROMPT_LIST); - McpSchema.JSONRPCResponse promptsListResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, - promptsListRequest.id(), mockPromptsResult, null); + JSONRPCResponse promptsListResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, promptsListRequest.id(), + mockPromptsResult, null); transport.simulateIncomingMessage(promptsListResponse); // Verify the consumer received the expected prompts @@ -293,10 +309,10 @@ void testSamplingCreateMessageRequestHandling() { MockMcpClientTransport transport = initializationEnabledTransport(); // Create a test sampling handler that echoes back the input - Function> samplingHandler = request -> { + Function> samplingHandler = request -> { var content = request.messages().get(0).content(); - return Mono.just(new McpSchema.CreateMessageResult(McpSchema.Role.ASSISTANT, content, "test-model", - McpSchema.CreateMessageResult.StopReason.END_TURN)); + return Mono.just(new CreateMessageResult(McpSchema.Role.ASSISTANT, content, "test-model", + CreateMessageResult.StopReason.END_TURN)); }; // Create client with sampling capability and handler @@ -308,36 +324,34 @@ void testSamplingCreateMessageRequestHandling() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock create message request - var messageRequest = new McpSchema.CreateMessageRequest( - List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message"))), - null, // modelPreferences - "Test system prompt", McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, 0.7, // temperature + var messageRequest = new CreateMessageRequest( + List.of(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message"))), null, // modelPreferences + "Test system prompt", CreateMessageRequest.ContextInclusionStrategy.NONE, 0.7, // temperature 100, // maxTokens null, // stopSequences null // metadata ); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, "test-id", messageRequest); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, + "test-id", messageRequest); transport.simulateIncomingMessage(request); // Verify response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("test-id"); assertThat(response.error()).isNull(); - McpSchema.CreateMessageResult result = transport.unmarshalFrom(response.result(), - new TypeRef() { - }); + CreateMessageResult result = transport.unmarshalFrom(response.result(), new TypeRef() { + }); assertThat(result).isNotNull(); assertThat(result.role()).isEqualTo(McpSchema.Role.ASSISTANT); assertThat(result.content()).isNotNull(); assertThat(result.model()).isEqualTo("test-model"); - assertThat(result.stopReason()).isEqualTo(McpSchema.CreateMessageResult.StopReason.END_TURN); + assertThat(result.stopReason()).isEqualTo(CreateMessageResult.StopReason.END_TURN); asyncMcpClient.closeGracefully(); } @@ -354,20 +368,20 @@ void testSamplingCreateMessageRequestHandlingWithoutCapability() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock create message request - var messageRequest = new McpSchema.CreateMessageRequest( - List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message"))), - null, null, null, null, 0, null, null); + var messageRequest = new CreateMessageRequest( + List.of(new SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message"))), null, + null, null, null, 0, null, null); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, "test-id", messageRequest); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, + "test-id", messageRequest); transport.simulateIncomingMessage(request); // Verify error response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("test-id"); assertThat(response.result()).isNull(); assertThat(response.error()).isNotNull(); @@ -393,7 +407,7 @@ void testElicitationCreateRequestHandling() { MockMcpClientTransport transport = initializationEnabledTransport(); // Create a test elicitation handler that echoes back the input - Function> elicitationHandler = request -> { + Function> elicitationHandler = request -> { assertThat(request.message()).isNotEmpty(); assertThat(request.requestedSchema()).isInstanceOf(Map.class); assertThat(request.requestedSchema().get("type")).isEqualTo("object"); @@ -402,8 +416,8 @@ void testElicitationCreateRequestHandling() { assertThat(properties).isNotNull(); assertThat(((Map) properties).get("message")).isInstanceOf(Map.class); - return Mono.just(McpSchema.ElicitResult.builder() - .message(McpSchema.ElicitResult.Action.ACCEPT) + return Mono.just(ElicitResult.builder() + .message(ElicitResult.Action.ACCEPT) .content(Map.of("message", request.message())) .build()); }; @@ -417,41 +431,41 @@ void testElicitationCreateRequestHandling() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock elicitation - var elicitRequest = McpSchema.ElicitRequest.builder() + var elicitRequest = ElicitRequest.builder() .message("Test message") .requestedSchema(Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string")))) .build(); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_ELICITATION_CREATE, "test-id", elicitRequest); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_ELICITATION_CREATE, + "test-id", elicitRequest); transport.simulateIncomingMessage(request); // Verify response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("test-id"); assertThat(response.error()).isNull(); - McpSchema.ElicitResult result = transport.unmarshalFrom(response.result(), new TypeRef<>() { + ElicitResult result = transport.unmarshalFrom(response.result(), new TypeRef<>() { }); assertThat(result).isNotNull(); - assertThat(result.action()).isEqualTo(McpSchema.ElicitResult.Action.ACCEPT); + assertThat(result.action()).isEqualTo(ElicitResult.Action.ACCEPT); assertThat(result.content()).isEqualTo(Map.of("message", "Test message")); asyncMcpClient.closeGracefully(); } @ParameterizedTest - @EnumSource(value = McpSchema.ElicitResult.Action.class, names = { "DECLINE", "CANCEL" }) - void testElicitationFailRequestHandling(McpSchema.ElicitResult.Action action) { + @EnumSource(value = ElicitResult.Action.class, names = { "DECLINE", "CANCEL" }) + void testElicitationFailRequestHandling(ElicitResult.Action action) { MockMcpClientTransport transport = initializationEnabledTransport(); // Create a test elicitation handler to decline the request - Function> elicitationHandler = request -> Mono - .just(McpSchema.ElicitResult.builder().message(action).build()); + Function> elicitationHandler = request -> Mono + .just(ElicitResult.builder().message(action).build()); // Create client with elicitation capability and handler McpAsyncClient asyncMcpClient = McpClient.async(transport) @@ -462,25 +476,25 @@ void testElicitationFailRequestHandling(McpSchema.ElicitResult.Action action) { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock elicitation - var elicitRequest = McpSchema.ElicitRequest.builder() + var elicitRequest = ElicitRequest.builder() .message("Test message") .requestedSchema(Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string")))) .build(); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_ELICITATION_CREATE, "test-id", elicitRequest); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_ELICITATION_CREATE, + "test-id", elicitRequest); transport.simulateIncomingMessage(request); // Verify response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("test-id"); assertThat(response.error()).isNull(); - McpSchema.ElicitResult result = transport.unmarshalFrom(response.result(), new TypeRef<>() { + ElicitResult result = transport.unmarshalFrom(response.result(), new TypeRef<>() { }); assertThat(result).isNotNull(); assertThat(result.action()).isEqualTo(action); @@ -502,20 +516,24 @@ void testElicitationCreateRequestHandlingWithoutCapability() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock elicitation - var elicitRequest = new McpSchema.ElicitRequest("test", - Map.of("type", "object", "properties", Map.of("test", Map.of("type", "boolean", "defaultValue", true, - "description", "test-description", "title", "test-title")))); + var elicitRequest = ElicitRequest.builder() + .message("test") + .requestedSchema(Map.of("type", "object", "properties", + Map.of("test", + Map.of("type", "boolean", "defaultValue", true, "description", "test-description", "title", + "test-title")))) + .build(); // Simulate incoming request - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_ELICITATION_CREATE, "test-id", elicitRequest); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_ELICITATION_CREATE, + "test-id", elicitRequest); transport.simulateIncomingMessage(request); // Verify error response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("test-id"); assertThat(response.result()).isNull(); assertThat(response.error()).isNotNull(); @@ -544,15 +562,15 @@ void testPingMessageRequestHandling() { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Simulate incoming ping request from server - McpSchema.JSONRPCRequest pingRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_PING, "ping-id", null); + JSONRPCRequest pingRequest = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_PING, "ping-id", + null); transport.simulateIncomingMessage(pingRequest); // Verify response - McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage(); - assertThat(sentMessage).isInstanceOf(McpSchema.JSONRPCResponse.class); + JSONRPCMessage sentMessage = transport.getLastSentMessage(); + assertThat(sentMessage).isInstanceOf(JSONRPCResponse.class); - McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage; + JSONRPCResponse response = (JSONRPCResponse) sentMessage; assertThat(response.id()).isEqualTo("ping-id"); assertThat(response.error()).isNull(); assertThat(response.result()).isInstanceOf(Map.class); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java index 48bf1da5b..827322fdf 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java @@ -16,6 +16,15 @@ import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.JsonSchema; +import io.modelcontextprotocol.spec.schema.tool.ListToolsResult; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -44,8 +53,8 @@ private McpClientTransport createMockTransportForToolValidation(boolean hasOutpu Map inputSchemaMap = Map.of("type", "object", "properties", Map.of("expression", Map.of("type", "string")), "required", List.of("expression")); - McpSchema.JsonSchema inputSchema = new McpSchema.JsonSchema("object", inputSchemaMap, null, null, null, null); - McpSchema.Tool.Builder toolBuilder = McpSchema.Tool.builder() + JsonSchema inputSchema = new JsonSchema("object", inputSchemaMap, null, null, null, null); + Tool.Builder toolBuilder = Tool.builder() .name("calculator") .description("Performs mathematical calculations") .inputSchema(inputSchema); @@ -57,24 +66,23 @@ private McpClientTransport createMockTransportForToolValidation(boolean hasOutpu toolBuilder.outputSchema(outputSchema); } - McpSchema.Tool calculatorTool = toolBuilder.build(); - McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(calculatorTool), null); + Tool calculatorTool = toolBuilder.build(); + ListToolsResult mockToolsResult = new ListToolsResult(List.of(calculatorTool), null); // Create call tool result - valid or invalid based on parameter Map structuredContent = invalidOutput ? Map.of("result", "5", "operation", "add") : Map.of("result", 5, "operation", "add"); - McpSchema.CallToolResult mockCallToolResult = McpSchema.CallToolResult.builder() + CallToolResult mockCallToolResult = CallToolResult.builder() .addTextContent("Calculation result") .structuredContent(structuredContent) .build(); return new McpClientTransport() { - Function, Mono> handler; + Function, Mono> handler; @Override - public Mono connect( - Function, Mono> handler) { + public Mono connect(Function, Mono> handler) { this.handler = handler; return Mono.empty(); } @@ -85,23 +93,20 @@ public Mono closeGracefully() { } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { + public Mono sendMessage(JSONRPCMessage message) { + if (!(message instanceof JSONRPCRequest request)) { return Mono.empty(); } - McpSchema.JSONRPCResponse response; + JSONRPCResponse response; if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, - null); + response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, null); } else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult, - null); + response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), mockToolsResult, null); } else if (McpSchema.METHOD_TOOLS_CALL.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), - mockCallToolResult, null); + response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), mockCallToolResult, null); } else { return Mono.empty(); @@ -125,13 +130,12 @@ public java.lang.reflect.Type getType() { @Test void validateContextPassedToTransportConnect() { McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; + Function, Mono> handler; final AtomicReference contextValue = new AtomicReference<>(); @Override - public Mono connect( - Function, Mono> handler) { + public Mono connect(Function, Mono> handler) { return Mono.deferContextual(ctx -> { this.handler = handler; if (ctx.hasKey(CONTEXT_KEY)) { @@ -147,17 +151,17 @@ public Mono closeGracefully() { } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { + public Mono sendMessage(JSONRPCMessage message) { if (!"hello".equals(this.contextValue.get())) { return Mono.error(new RuntimeException("Context value not propagated via #connect method")); } // We're only interested in handling the init request to provide an init // response - if (!(message instanceof McpSchema.JSONRPCRequest)) { + if (!(message instanceof JSONRPCRequest)) { return Mono.empty(); } - McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, - ((McpSchema.JSONRPCRequest) message).id(), MOCK_INIT_RESULT, null); + JSONRPCResponse initResponse = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, + ((JSONRPCRequest) message).id(), MOCK_INIT_RESULT, null); return handler.apply(Mono.just(initResponse)).then(); } @@ -186,7 +190,7 @@ void testCallToolWithOutputSchemaValidationSuccess() { StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete(); - StepVerifier.create(client.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")))) + StepVerifier.create(client.callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3")))) .expectNextMatches(response -> { assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -208,7 +212,7 @@ void testCallToolWithNoOutputSchemaSuccess() { StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete(); - StepVerifier.create(client.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")))) + StepVerifier.create(client.callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3")))) .expectNextMatches(response -> { assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -230,7 +234,7 @@ void testCallToolWithOutputSchemaValidationFailure() { StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete(); - StepVerifier.create(client.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")))) + StepVerifier.create(client.callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3")))) .expectErrorMatches(ex -> ex instanceof IllegalArgumentException && ex.getMessage().contains("Tool call result validation failed")) .verify(); @@ -240,19 +244,15 @@ void testCallToolWithOutputSchemaValidationFailure() { @Test void testListToolsWithEmptyCursor() { - McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build(); - McpSchema.Tool subtractTool = McpSchema.Tool.builder() - .name("subtract") - .description("calculate subtract") - .build(); - McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool, subtractTool), ""); + Tool addTool = Tool.builder().name("add").description("calculate add").build(); + Tool subtractTool = Tool.builder().name("subtract").description("calculate subtract").build(); + ListToolsResult mockToolsResult = new ListToolsResult(List.of(addTool, subtractTool), ""); McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; + Function, Mono> handler; @Override - public Mono connect( - Function, Mono> handler) { + public Mono connect(Function, Mono> handler) { return Mono.deferContextual(ctx -> { this.handler = handler; return Mono.empty(); @@ -265,19 +265,17 @@ public Mono closeGracefully() { } @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { + public Mono sendMessage(JSONRPCMessage message) { + if (!(message instanceof JSONRPCRequest request)) { return Mono.empty(); } - McpSchema.JSONRPCResponse response; + JSONRPCResponse response; if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, - null); + response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, null); } else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult, - null); + response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), mockToolsResult, null); } else { return Mono.empty(); @@ -299,11 +297,11 @@ public java.lang.reflect.Type getType() { McpAsyncClient client = McpClient.async(transport).enableCallToolSchemaCaching(true).build(); - Mono mono = client.listTools(); - McpSchema.ListToolsResult toolsResult = mono.block(); + Mono mono = client.listTools(); + ListToolsResult toolsResult = mono.block(); assertThat(toolsResult).isNotNull(); - Set names = toolsResult.tools().stream().map(McpSchema.Tool::name).collect(Collectors.toSet()); + Set names = toolsResult.tools().stream().map(Tool::name).collect(Collectors.toSet()); assertThat(names).containsExactlyInAnyOrder("subtract", "add"); } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java index 03f64aa64..1c8ba3b94 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java @@ -11,6 +11,9 @@ import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.InitializeResult; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import io.modelcontextprotocol.spec.ProtocolVersions; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; @@ -41,12 +44,12 @@ void shouldUseLatestVersionByDefault() { String protocolVersion = transport.protocolVersions().get(transport.protocolVersions().size() - 1); StepVerifier.create(initializeResultMono).then(() -> { - McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); + JSONRPCRequest request = transport.getLastSentMessageAsRequest(); assertThat(request.params()).isInstanceOf(McpSchema.InitializeRequest.class); McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params(); assertThat(initRequest.protocolVersion()).isEqualTo(transport.protocolVersions().get(0)); - transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), + transport.simulateIncomingMessage(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), new McpSchema.InitializeResult(protocolVersion, ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"), null), null)); @@ -75,12 +78,12 @@ void shouldNegotiateSpecificVersion() { Mono initializeResultMono = client.initialize(); StepVerifier.create(initializeResultMono).then(() -> { - McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); + JSONRPCRequest request = transport.getLastSentMessageAsRequest(); assertThat(request.params()).isInstanceOf(McpSchema.InitializeRequest.class); McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params(); assertThat(initRequest.protocolVersion()).isIn(List.of(oldVersion, ProtocolVersions.MCP_2025_11_25)); - transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), + transport.simulateIncomingMessage(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), new McpSchema.InitializeResult(oldVersion, ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"), null), null)); @@ -106,10 +109,10 @@ void shouldFailForUnsupportedVersion() { Mono initializeResultMono = client.initialize(); StepVerifier.create(initializeResultMono).then(() -> { - McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); + JSONRPCRequest request = transport.getLastSentMessageAsRequest(); assertThat(request.params()).isInstanceOf(McpSchema.InitializeRequest.class); - transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), + transport.simulateIncomingMessage(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), new McpSchema.InitializeResult(unsupportedVersion, ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"), null), null)); @@ -138,11 +141,11 @@ void shouldUseHighestVersionWhenMultipleSupported() { Mono initializeResultMono = client.initialize(); StepVerifier.create(initializeResultMono).then(() -> { - McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); + JSONRPCRequest request = transport.getLastSentMessageAsRequest(); McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params(); assertThat(initRequest.protocolVersion()).isEqualTo(latestVersion); - transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), + transport.simulateIncomingMessage(new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, request.id(), new McpSchema.InitializeResult(latestVersion, ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"), null), null)); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java index a24805a30..3059ca494 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java @@ -17,9 +17,8 @@ import io.modelcontextprotocol.client.transport.customizer.McpAsyncHttpClientRequestCustomizer; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import io.modelcontextprotocol.common.McpTransportContext; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest; - +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -136,7 +135,7 @@ void testErrorOnBogusMessage() { @Test void testMessageProcessing() { // Create a test message - JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id", + JSONRPCRequest testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "test-method", "test-id", Map.of("key", "value")); // Simulate receiving the message @@ -167,7 +166,7 @@ void testResponseMessageProcessing() { """); // Create and send a request message - JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id", + JSONRPCRequest testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "test-method", "test-id", Map.of("key", "value")); // Verify message handling @@ -191,7 +190,7 @@ void testErrorMessageProcessing() { """); // Create and send a request message - JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id", + JSONRPCRequest testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "test-method", "test-id", Map.of("key", "value")); // Verify message handling @@ -221,7 +220,7 @@ void testGracefulShutdown() { StepVerifier.create(transport.closeGracefully()).verifyComplete(); // Create a test message - JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id", + JSONRPCRequest testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "test-method", "test-id", Map.of("key", "value")); // Verify message is not processed after shutdown @@ -266,10 +265,10 @@ void testMultipleMessageProcessing() { """); // Create and send corresponding messages - JSONRPCRequest message1 = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "method1", "id1", + JSONRPCRequest message1 = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "method1", "id1", Map.of("key", "value1")); - JSONRPCRequest message2 = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "method2", "id2", + JSONRPCRequest message2 = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "method2", "id2", Map.of("key", "value2")); // Verify both messages are processed @@ -414,8 +413,7 @@ void testRequestCustomizer() { clearInvocations(mockCustomizer); // Send test message - var testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id", - Map.of("key", "value")); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "test-method", "test-id", Map.of("key", "value")); // Subscribe to messages and verify StepVerifier @@ -457,8 +455,7 @@ void testAsyncRequestCustomizer() { clearInvocations(mockCustomizer); // Send test message - var testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id", - Map.of("key", "value")); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "test-method", "test-id", Map.of("key", "value")); // Subscribe to messages and verify StepVerifier diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java index 81e642681..1df4136ba 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java @@ -26,6 +26,8 @@ import io.modelcontextprotocol.server.transport.TomcatTestUtil; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import reactor.test.StepVerifier; /** @@ -79,8 +81,8 @@ void testNotificationInitialized() throws URISyntaxException { var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_03_26, McpSchema.ClientCapabilities.builder().roots(true).build(), new McpSchema.Implementation("MCP Client", "0.3.1")); - var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, - "test-id", initializeRequest); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", + initializeRequest); StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete(); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java index b82d6eb2c..eaaf59eb2 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java @@ -28,6 +28,8 @@ import io.modelcontextprotocol.spec.McpTransportException; import io.modelcontextprotocol.spec.McpTransportSessionNotFoundException; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import reactor.test.StepVerifier; /** @@ -334,12 +336,11 @@ else if (status == 404) { StepVerifier.create(transport.closeGracefully()).verifyComplete(); } - private McpSchema.JSONRPCRequest createTestRequestMessage() { + private JSONRPCRequest createTestRequestMessage() { var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_03_26, McpSchema.ClientCapabilities.builder().roots(true).build(), new McpSchema.Implementation("Test Client", "1.0.0")); - return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", - initializeRequest); + return new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest); } } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java index f88736a5d..5855babfd 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java @@ -9,6 +9,9 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; + import java.net.URI; import java.net.URISyntaxException; import java.util.Map; @@ -82,8 +85,8 @@ void testRequestCustomizer() throws URISyntaxException { var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(), new McpSchema.Implementation("MCP Client", "0.3.1")); - var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, - "test-id", initializeRequest); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", + initializeRequest); StepVerifier .create(t.sendMessage(testMessage).contextWrite(ctx -> ctx.put(McpTransportContext.KEY, context))) @@ -112,8 +115,8 @@ void testAsyncRequestCustomizer() throws URISyntaxException { var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(), new McpSchema.Implementation("MCP Client", "0.3.1")); - var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, - "test-id", initializeRequest); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", + initializeRequest); StepVerifier .create(t.sendMessage(testMessage).contextWrite(ctx -> ctx.put(McpTransportContext.KEY, context))) @@ -135,8 +138,8 @@ void testCloseUninitialized() { var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(), new McpSchema.Implementation("MCP Client", "0.3.1")); - var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, - "test-id", initializeRequest); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", + initializeRequest); StepVerifier.create(transport.sendMessage(testMessage)) .expectErrorMessage("MCP session has been closed") @@ -150,8 +153,8 @@ void testCloseInitialized() { var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(), new McpSchema.Implementation("MCP Client", "0.3.1")); - var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, - "test-id", initializeRequest); + var testMessage = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id", + initializeRequest); StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete(); StepVerifier.create(transport.closeGracefully()).verifyComplete(); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java index ce381436d..7c35a168d 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java @@ -23,6 +23,9 @@ import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; import io.modelcontextprotocol.server.transport.TomcatTestUtil; import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import jakarta.servlet.Servlet; import jakarta.servlet.http.HttpServletRequest; import org.apache.catalina.LifecycleException; @@ -125,20 +128,20 @@ public class AsyncServerMcpTransportContextIntegrationTests { .build()) .build(); - private final McpSchema.Tool tool = McpSchema.Tool.builder() + private final Tool tool = Tool.builder() .name("test-tool") .description("return the value of the x-test header from call tool request") .build(); - private final BiFunction> asyncStatelessHandler = ( + private final BiFunction> asyncStatelessHandler = ( transportContext, request) -> { - return Mono.just(McpSchema.CallToolResult.builder() + return Mono.just(CallToolResult.builder() .addTextContent(transportContext.get("server-side-header-value").toString()) .isError(false) .build()); }; - private final BiFunction> asyncStatefulHandler = ( + private final BiFunction> asyncStatefulHandler = ( exchange, request) -> { return asyncStatelessHandler.apply(exchange.transportContext(), request); }; @@ -178,7 +181,7 @@ void asyncClinetStatelessServer() { // Test tool call with context StepVerifier - .create(asyncStreamableClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of())) + .create(asyncStreamableClient.callTool(new CallToolRequest("test-tool", Map.of())) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, McpTransportContext.create(Map.of("client-side-header-value", "some important value"))))) .assertNext(response -> { @@ -212,7 +215,7 @@ void asyncClientStreamableServer() { // Test tool call with context StepVerifier - .create(asyncStreamableClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of())) + .create(asyncStreamableClient.callTool(new CallToolRequest("test-tool", Map.of())) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, McpTransportContext.create(Map.of("client-side-header-value", "some important value"))))) .assertNext(response -> { @@ -246,7 +249,7 @@ void asyncClientSseServer() { // Test tool call with context StepVerifier - .create(asyncSseClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of())) + .create(asyncSseClient.callTool(new CallToolRequest("test-tool", Map.of())) .contextWrite(ctx -> ctx.put(McpTransportContext.KEY, McpTransportContext.create(Map.of("client-side-header-value", "some important value"))))) .assertNext(response -> { diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java index 29eef1410..b3d45dfc1 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java @@ -19,6 +19,9 @@ import io.modelcontextprotocol.server.transport.TomcatTestUtil; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; import org.apache.catalina.startup.Tomcat; @@ -41,13 +44,13 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests { req -> McpTransportContext.create(Map.of("protocol-version", req.getHeader("MCP-protocol-version")))) .build(); - private final McpSchema.Tool toolSpec = McpSchema.Tool.builder() + private final Tool toolSpec = Tool.builder() .name("test-tool") .description("return the protocol version used") .build(); - private final BiFunction toolHandler = ( - exchange, request) -> McpSchema.CallToolResult.builder() + private final BiFunction toolHandler = (exchange, + request) -> CallToolResult.builder() .addTextContent(exchange.transportContext().get("protocol-version").toString()) .isError(false) .build(); @@ -70,7 +73,7 @@ void usesLatestVersion() { .build(); client.initialize(); - McpSchema.CallToolResult response = client.callTool(new McpSchema.CallToolRequest("test-tool", Map.of())); + CallToolResult response = client.callTool(new CallToolRequest("test-tool", Map.of())); var calls = requestRecordingFilter.getCalls(); @@ -100,7 +103,7 @@ void usesServerSupportedVersion() { var client = McpClient.sync(transport).build(); client.initialize(); - McpSchema.CallToolResult response = client.callTool(new McpSchema.CallToolRequest("test-tool", Map.of())); + CallToolResult response = client.callTool(new CallToolRequest("test-tool", Map.of())); var calls = requestRecordingFilter.getCalls(); // Initialize tells the server the Client's latest supported version diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java index 563e2167d..26b410e91 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java @@ -20,6 +20,9 @@ import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; import io.modelcontextprotocol.server.transport.TomcatTestUtil; import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import jakarta.servlet.Servlet; import jakarta.servlet.http.HttpServletRequest; import java.util.Map; @@ -77,14 +80,14 @@ public class SyncServerMcpTransportContextIntegrationTests { : McpTransportContext.EMPTY; }; - private final BiFunction statelessHandler = ( - transportContext, request) -> McpSchema.CallToolResult.builder() + private final BiFunction statelessHandler = (transportContext, + request) -> CallToolResult.builder() .addTextContent(transportContext.get("server-side-header-value").toString()) .isError(false) .build(); - private final BiFunction statefulHandler = ( - exchange, request) -> statelessHandler.apply(exchange.transportContext(), request); + private final BiFunction statefulHandler = (exchange, + request) -> statelessHandler.apply(exchange.transportContext(), request); private final HttpServletStatelessServerTransport statelessServerTransport = HttpServletStatelessServerTransport .builder() @@ -116,7 +119,7 @@ public class SyncServerMcpTransportContextIntegrationTests { .transportContextProvider(clientContextProvider) .build(); - private final McpSchema.Tool tool = McpSchema.Tool.builder() + private final Tool tool = Tool.builder() .name("test-tool") .description("return the value of the x-test header from call tool request") .build(); @@ -155,8 +158,7 @@ void statelessServer() { assertThat(initResult).isNotNull(); CLIENT_SIDE_HEADER_VALUE_HOLDER.set("some important value"); - McpSchema.CallToolResult response = streamableClient - .callTool(new McpSchema.CallToolRequest("test-tool", Map.of())); + CallToolResult response = streamableClient.callTool(new CallToolRequest("test-tool", Map.of())); assertThat(response).isNotNull(); assertThat(response.content()).hasSize(1) @@ -181,8 +183,7 @@ void streamableServer() { assertThat(initResult).isNotNull(); CLIENT_SIDE_HEADER_VALUE_HOLDER.set("some important value"); - McpSchema.CallToolResult response = streamableClient - .callTool(new McpSchema.CallToolRequest("test-tool", Map.of())); + CallToolResult response = streamableClient.callTool(new CallToolRequest("test-tool", Map.of())); assertThat(response).isNotNull(); assertThat(response.content()).hasSize(1) @@ -207,7 +208,7 @@ void sseServer() { assertThat(initResult).isNotNull(); CLIENT_SIDE_HEADER_VALUE_HOLDER.set("some important value"); - McpSchema.CallToolResult response = sseClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of())); + CallToolResult response = sseClient.callTool(new CallToolRequest("test-tool", Map.of())); assertThat(response).isNotNull(); assertThat(response.content()).hasSize(1) diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java index 491c2d4ed..31df4abb1 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java @@ -18,18 +18,22 @@ import io.modelcontextprotocol.server.transport.TomcatTestUtil; import io.modelcontextprotocol.spec.HttpHeaders; import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteRequest; import io.modelcontextprotocol.spec.McpSchema.CompleteResult; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; import io.modelcontextprotocol.spec.McpSchema.InitializeResult; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.PromptArgument; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptArgument; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.Tool; import net.javacrumbs.jsonunit.core.Option; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; @@ -143,7 +147,7 @@ void testToolCallSuccess(String clientType) { assertThat(mcpClient.listTools().tools()).contains(tool1.tool()); - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("tool1", Map.of())); assertThat(response).isNotNull(); assertThat(response).isEqualTo(callResponse); @@ -270,7 +274,7 @@ void testStructuredOutputValidationSuccess(String clientType) { // Call tool with valid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -337,7 +341,7 @@ void testStructuredOutputOfObjectArrayValidationSuccess(String clientType) { assertThat(mcpClient.initialize()).isNotNull(); // Call tool with valid structured output of type array - CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("getMembers", Map.of())); + CallToolResult response = mcpClient.callTool(new CallToolRequest("getMembers", Map.of())); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -401,7 +405,7 @@ void testStructuredOutputWithInHandlerError(String clientType) { // Call tool with valid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -453,7 +457,7 @@ void testStructuredOutputValidationFailure(String clientType) { // Call tool with invalid structured output CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -502,7 +506,7 @@ void testStructuredOutputMissingStructuredContent(String clientType) { // Call tool that should return structured content but doesn't CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))); + .callTool(new CallToolRequest("calculator", Map.of("expression", "2 + 3"))); assertThat(response).isNotNull(); assertThat(response.isError()).isTrue(); @@ -571,8 +575,7 @@ void testStructuredOutputRuntimeToolAddition(String clientType) { // Note: outputSchema might be null in sync server, but validation still works // Call dynamically added tool - CallToolResult response = mcpClient - .callTool(new McpSchema.CallToolRequest("dynamic-tool", Map.of("count", 3))); + CallToolResult response = mcpClient.callTool(new CallToolRequest("dynamic-tool", Map.of("count", 3))); assertThat(response).isNotNull(); assertThat(response.isError()).isFalse(); @@ -609,9 +612,9 @@ void testThrownMcpErrorAndJsonRpcError() throws Exception { mcpServer.addTool(toolSpec); - McpSchema.CallToolRequest callToolRequest = new McpSchema.CallToolRequest("test", Map.of()); - McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_TOOLS_CALL, "test", callToolRequest); + CallToolRequest callToolRequest = new CallToolRequest("test", Map.of()); + JSONRPCRequest jsonrpcRequest = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_TOOLS_CALL, "test", + callToolRequest); MockHttpServletRequest request = new MockHttpServletRequest("POST", CUSTOM_MESSAGE_ENDPOINT); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -628,8 +631,8 @@ void testThrownMcpErrorAndJsonRpcError() throws Exception { mcpStatelessServerTransport.service(request, response); - McpSchema.JSONRPCResponse jsonrpcResponse = JSON_MAPPER.readValue(response.getContentAsByteArray(), - McpSchema.JSONRPCResponse.class); + JSONRPCResponse jsonrpcResponse = JSON_MAPPER.readValue(response.getContentAsByteArray(), + JSONRPCResponse.class); assertThat(jsonrpcResponse).isNotNull(); assertThat(jsonrpcResponse.error()).isNotNull(); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java index 54fb80a78..b4c1949da 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java @@ -28,13 +28,13 @@ import io.modelcontextprotocol.spec.McpSchema.CompleteResult; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; import io.modelcontextprotocol.spec.McpSchema.InitializeResult; -import io.modelcontextprotocol.spec.McpSchema.Prompt; -import io.modelcontextprotocol.spec.McpSchema.PromptArgument; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; import io.modelcontextprotocol.spec.McpSchema.ResourceReference; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptArgument; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; import io.modelcontextprotocol.spec.McpError; /** @@ -144,7 +144,7 @@ void testCompletionBackwardCompatibility() { new CompleteResult.CompleteCompletion(List.of("no-context-completion"), 1, false)); }; - McpSchema.Prompt prompt = new Prompt("test-prompt", "this is a test prompt", + Prompt prompt = new Prompt("test-prompt", "this is a test prompt", List.of(new PromptArgument("arg", "string", false))); var mcpServer = McpServer.sync(mcpServerTransportProvider) @@ -204,7 +204,7 @@ else if ("products_db".equals(db)) { return new CompleteResult(new CompleteResult.CompleteCompletion(List.of(), 0, false)); }; - McpSchema.Resource resource = Resource.builder() + Resource resource = Resource.builder() .uri("db://{database}/{table}") .name("Database Table") .description("Resource representing a table in a database") @@ -281,7 +281,7 @@ void testCompletionErrorOnMissingContext() { return new CompleteResult(new CompleteResult.CompleteCompletion(List.of(), 0, false)); }; - McpSchema.Resource resource = Resource.builder() + Resource resource = Resource.builder() .uri("db://{database}/{table}") .name("Database Table") .description("Resource representing a table in a database") diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java index d9f899020..c36c8c69e 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java @@ -11,6 +11,10 @@ import io.modelcontextprotocol.MockMcpServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -24,8 +28,8 @@ class McpServerProtocolVersionTests { private static final McpSchema.Implementation CLIENT_INFO = new McpSchema.Implementation("test-client", "1.0.0"); - private McpSchema.JSONRPCRequest jsonRpcInitializeRequest(String requestId, String protocolVersion) { - return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, requestId, + private JSONRPCRequest jsonRpcInitializeRequest(String requestId, String protocolVersion) { + return new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, requestId, new McpSchema.InitializeRequest(protocolVersion, null, CLIENT_INFO)); } @@ -39,9 +43,9 @@ void shouldUseLatestVersionByDefault() { transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, ProtocolVersions.MCP_2025_11_25)); - McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage(); - assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response; + JSONRPCMessage response = serverTransport.getLastSentMessage(); + assertThat(response).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse jsonResponse = (JSONRPCResponse) response; assertThat(jsonResponse.id()).isEqualTo(requestId); assertThat(jsonResponse.result()).isInstanceOf(McpSchema.InitializeResult.class); McpSchema.InitializeResult result = (McpSchema.InitializeResult) jsonResponse.result(); @@ -66,9 +70,9 @@ void shouldNegotiateSpecificVersion() { transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, oldVersion)); - McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage(); - assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response; + JSONRPCMessage response = serverTransport.getLastSentMessage(); + assertThat(response).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse jsonResponse = (JSONRPCResponse) response; assertThat(jsonResponse.id()).isEqualTo(requestId); assertThat(jsonResponse.result()).isInstanceOf(McpSchema.InitializeResult.class); McpSchema.InitializeResult result = (McpSchema.InitializeResult) jsonResponse.result(); @@ -89,9 +93,9 @@ void shouldSuggestLatestVersionForUnsupportedVersion() { transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, unsupportedVersion)); - McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage(); - assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response; + JSONRPCMessage response = serverTransport.getLastSentMessage(); + assertThat(response).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse jsonResponse = (JSONRPCResponse) response; assertThat(jsonResponse.id()).isEqualTo(requestId); assertThat(jsonResponse.result()).isInstanceOf(McpSchema.InitializeResult.class); McpSchema.InitializeResult result = (McpSchema.InitializeResult) jsonResponse.result(); @@ -117,9 +121,9 @@ void shouldUseHighestVersionWhenMultipleSupported() { String requestId = UUID.randomUUID().toString(); transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, latestVersion)); - McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage(); - assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class); - McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response; + JSONRPCMessage response = serverTransport.getLastSentMessage(); + assertThat(response).isInstanceOf(JSONRPCResponse.class); + JSONRPCResponse jsonResponse = (JSONRPCResponse) response; assertThat(jsonResponse.id()).isEqualTo(requestId); assertThat(jsonResponse.result()).isInstanceOf(McpSchema.InitializeResult.class); McpSchema.InitializeResult result = (McpSchema.InitializeResult) jsonResponse.result(); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceSubscriptionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceSubscriptionTests.java index 016e25e9f..63aa1a763 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceSubscriptionTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceSubscriptionTests.java @@ -10,6 +10,12 @@ import io.modelcontextprotocol.MockMcpServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.ProtocolVersions; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.schema.resource.SubscribeRequest; +import io.modelcontextprotocol.spec.schema.resource.UnsubscribeRequest; import org.junit.jupiter.api.Test; import reactor.test.StepVerifier; @@ -35,25 +41,23 @@ private static McpAsyncServer buildServer(MockMcpServerTransportProvider transpo .build(); } - private static McpSchema.JSONRPCRequest initRequest() { - return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, - UUID.randomUUID().toString(), + private static JSONRPCRequest initRequest() { + return new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, UUID.randomUUID().toString(), new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25, null, CLIENT_INFO)); } - private static McpSchema.JSONRPCNotification initializedNotification() { - return new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_INITIALIZED, - null); + private static JSONRPCNotification initializedNotification() { + return new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_INITIALIZED, null); } - private static McpSchema.JSONRPCRequest subscribeRequest(String uri) { - return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_RESOURCES_SUBSCRIBE, - UUID.randomUUID().toString(), new McpSchema.SubscribeRequest(uri)); + private static JSONRPCRequest subscribeRequest(String uri) { + return new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_RESOURCES_SUBSCRIBE, + UUID.randomUUID().toString(), new SubscribeRequest(uri)); } - private static McpSchema.JSONRPCRequest unsubscribeRequest(String uri) { - return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_RESOURCES_UNSUBSCRIBE, - UUID.randomUUID().toString(), new McpSchema.UnsubscribeRequest(uri)); + private static JSONRPCRequest unsubscribeRequest(String uri) { + return new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, McpSchema.METHOD_RESOURCES_UNSUBSCRIBE, + UUID.randomUUID().toString(), new UnsubscribeRequest(uri)); } @Test @@ -89,9 +93,9 @@ void notifyResourcesUpdated_afterSubscribe_notifiesSession() { StepVerifier.create(server.notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(RESOURCE_URI))) .verifyComplete(); - McpSchema.JSONRPCMessage sent = transport.getLastSentMessage(); - assertThat(sent).isInstanceOf(McpSchema.JSONRPCNotification.class); - McpSchema.JSONRPCNotification notification = (McpSchema.JSONRPCNotification) sent; + JSONRPCMessage sent = transport.getLastSentMessage(); + assertThat(sent).isInstanceOf(JSONRPCNotification.class); + JSONRPCNotification notification = (JSONRPCNotification) sent; assertThat(notification.method()).isEqualTo(McpSchema.METHOD_NOTIFICATION_RESOURCES_UPDATED); server.closeGracefully().block(); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java index b7d46a967..e34f47e2f 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java @@ -9,9 +9,9 @@ import io.modelcontextprotocol.MockMcpServerTransport; import io.modelcontextprotocol.MockMcpServerTransportProvider; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java index 6c2cc2bf4..d1ebce17f 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java @@ -18,9 +18,10 @@ import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.spec.McpError; -import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; import io.modelcontextprotocol.spec.McpServerTransport; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -105,7 +106,7 @@ void shouldHandleIncomingMessages() throws Exception { transportProvider = new StdioServerTransportProvider(McpJsonDefaults.getMapper(), stream, System.out); // Set up a real session to capture the message - AtomicReference capturedMessage = new AtomicReference<>(); + AtomicReference capturedMessage = new AtomicReference<>(); CountDownLatch messageLatch = new CountDownLatch(1); McpServerSession.Factory realSessionFactory = transport -> { @@ -130,8 +131,8 @@ void shouldHandleIncomingMessages() throws Exception { return Mono.just(capturedMessage.get()); })).assertNext(message -> { assertThat(message).isNotNull(); - assertThat(message).isInstanceOf(McpSchema.JSONRPCRequest.class); - McpSchema.JSONRPCRequest request = (McpSchema.JSONRPCRequest) message; + assertThat(message).isInstanceOf(JSONRPCRequest.class); + JSONRPCRequest request = (JSONRPCRequest) message; assertThat(request.method()).isEqualTo("test"); assertThat(request.id()).isEqualTo(1); }).verifyComplete(); diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioUtf8TestServer.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioUtf8TestServer.java index 3fc3a716d..aa18aa063 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioUtf8TestServer.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioUtf8TestServer.java @@ -12,8 +12,9 @@ import java.util.concurrent.TimeUnit; import io.modelcontextprotocol.json.McpJsonDefaults; -import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCMessage; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; import reactor.core.publisher.Mono; import static org.mockito.ArgumentMatchers.any; @@ -53,8 +54,8 @@ public static void main(String[] args) throws Exception { McpServerSession.Factory sessionFactory = transport -> { McpServerSession session = mock(McpServerSession.class); when(session.handle(any())).thenAnswer(invocation -> { - McpSchema.JSONRPCMessage msg = invocation.getArgument(0); - if (msg instanceof McpSchema.JSONRPCRequest request) { + JSONRPCMessage msg = invocation.getArgument(0); + if (msg instanceof JSONRPCRequest request) { Map params = (Map) request.params(); receivedMessage.append(params.get("message")); } diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index 942e0a6e2..c12440bfd 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -4,12 +4,6 @@ package io.modelcontextprotocol.spec; -import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER; -import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; -import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -17,11 +11,45 @@ import java.util.List; import java.util.Map; -import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPC; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCNotification; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCRequest; +import io.modelcontextprotocol.spec.jsonrpc.JSONRPCResponse; +import io.modelcontextprotocol.spec.schema.elicit.ElicitRequest; +import io.modelcontextprotocol.spec.schema.elicit.ElicitResult; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptRequest; +import io.modelcontextprotocol.spec.schema.prompt.GetPromptResult; +import io.modelcontextprotocol.spec.schema.prompt.ListPromptsResult; +import io.modelcontextprotocol.spec.schema.prompt.Prompt; +import io.modelcontextprotocol.spec.schema.prompt.PromptArgument; +import io.modelcontextprotocol.spec.schema.prompt.PromptMessage; +import io.modelcontextprotocol.spec.schema.resource.BlobResourceContents; +import io.modelcontextprotocol.spec.schema.resource.ListResourceTemplatesResult; +import io.modelcontextprotocol.spec.schema.resource.ListResourcesResult; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceRequest; +import io.modelcontextprotocol.spec.schema.resource.ReadResourceResult; +import io.modelcontextprotocol.spec.schema.resource.Resource; +import io.modelcontextprotocol.spec.schema.resource.ResourceTemplate; +import io.modelcontextprotocol.spec.schema.resource.TextResourceContents; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageRequest; +import io.modelcontextprotocol.spec.schema.sample.CreateMessageResult; +import io.modelcontextprotocol.spec.schema.sample.ModelHint; +import io.modelcontextprotocol.spec.schema.sample.ModelPreferences; +import io.modelcontextprotocol.spec.schema.sample.SamplingMessage; +import io.modelcontextprotocol.spec.schema.tool.CallToolRequest; +import io.modelcontextprotocol.spec.schema.tool.CallToolResult; +import io.modelcontextprotocol.spec.schema.tool.JsonSchema; +import io.modelcontextprotocol.spec.schema.tool.Tool; +import io.modelcontextprotocol.spec.schema.tool.ToolAnnotations; +import net.javacrumbs.jsonunit.core.Option; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; -import net.javacrumbs.jsonunit.core.Option; +import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @author Christian Tzolov @@ -121,10 +149,9 @@ void testAudioContentDeserialization() throws Exception { @Test void testCreateMessageRequestWithMeta() throws Exception { McpSchema.TextContent content = new McpSchema.TextContent("User message"); - McpSchema.SamplingMessage message = new McpSchema.SamplingMessage(McpSchema.Role.USER, content); - McpSchema.ModelHint hint = new McpSchema.ModelHint("gpt-4"); - McpSchema.ModelPreferences preferences = new McpSchema.ModelPreferences(Collections.singletonList(hint), 0.3, - 0.7, 0.9); + SamplingMessage message = new SamplingMessage(McpSchema.Role.USER, content); + ModelHint hint = new ModelHint("gpt-4"); + ModelPreferences preferences = new ModelPreferences(Collections.singletonList(hint), 0.3, 0.7, 0.9); Map metadata = new HashMap<>(); metadata.put("session", "test-session"); @@ -132,11 +159,11 @@ void testCreateMessageRequestWithMeta() throws Exception { Map meta = new HashMap<>(); meta.put("progressToken", "create-message-token-456"); - McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() + CreateMessageRequest request = CreateMessageRequest.builder() .messages(Collections.singletonList(message)) .modelPreferences(preferences) .systemPrompt("You are a helpful assistant") - .includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER) + .includeContext(CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER) .temperature(0.7) .maxTokens(1000) .stopSequences(Arrays.asList("STOP", "END")) @@ -157,8 +184,8 @@ void testCreateMessageRequestWithMeta() throws Exception { @Test void testEmbeddedResource() throws Exception { - McpSchema.TextResourceContents resourceContents = new McpSchema.TextResourceContents("resource://test", - "text/plain", "Sample resource content"); + TextResourceContents resourceContents = new TextResourceContents("resource://test", "text/plain", + "Sample resource content"); McpSchema.EmbeddedResource test = new McpSchema.EmbeddedResource(null, resourceContents); @@ -188,8 +215,8 @@ void testEmbeddedResourceDeserialization() throws Exception { @Test void testEmbeddedResourceWithBlobContents() throws Exception { - McpSchema.BlobResourceContents resourceContents = new McpSchema.BlobResourceContents("resource://test", - "application/octet-stream", "base64encodedblob"); + BlobResourceContents resourceContents = new BlobResourceContents("resource://test", "application/octet-stream", + "base64encodedblob"); McpSchema.EmbeddedResource test = new McpSchema.EmbeddedResource(null, resourceContents); @@ -213,9 +240,8 @@ void testEmbeddedResourceWithBlobContentsDeserialization() throws Exception { assertThat(embeddedResource.resource()).isNotNull(); assertThat(embeddedResource.resource().uri()).isEqualTo("resource://test"); assertThat(embeddedResource.resource().mimeType()).isEqualTo("application/octet-stream"); - assertThat(((McpSchema.BlobResourceContents) embeddedResource.resource()).blob()) - .isEqualTo("base64encodedblob"); - assertThat(((McpSchema.BlobResourceContents) embeddedResource.resource()).meta()).containsKey("metaKey"); + assertThat(((BlobResourceContents) embeddedResource.resource()).blob()).isEqualTo("base64encodedblob"); + assertThat(((BlobResourceContents) embeddedResource.resource()).meta()).containsKey("metaKey"); } @Test @@ -255,8 +281,7 @@ void testJSONRPCRequest() throws Exception { Map params = new HashMap<>(); params.put("key", "value"); - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, "method_name", 1, - params); + JSONRPCRequest request = new JSONRPCRequest(JSONRPC.JSONRPC_VERSION, "method_name", 1, params); String value = JSON_MAPPER.writeValueAsString(request); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -271,8 +296,8 @@ void testJSONRPCNotification() throws Exception { Map params = new HashMap<>(); params.put("key", "value"); - McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, - "notification_method", params); + JSONRPCNotification notification = new JSONRPCNotification(JSONRPC.JSONRPC_VERSION, "notification_method", + params); String value = JSON_MAPPER.writeValueAsString(notification); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -287,7 +312,7 @@ void testJSONRPCResponse() throws Exception { Map result = new HashMap<>(); result.put("result_key", "result_value"); - McpSchema.JSONRPCResponse response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, 1, result, null); + JSONRPCResponse response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, 1, result, null); String value = JSON_MAPPER.writeValueAsString(response); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -299,10 +324,10 @@ void testJSONRPCResponse() throws Exception { @Test void testJSONRPCResponseWithError() throws Exception { - McpSchema.JSONRPCResponse.JSONRPCError error = new McpSchema.JSONRPCResponse.JSONRPCError( - McpSchema.ErrorCodes.INVALID_REQUEST, "Invalid request", null); + JSONRPCResponse.JSONRPCError error = new JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INVALID_REQUEST, + "Invalid request", null); - McpSchema.JSONRPCResponse response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, 1, null, error); + JSONRPCResponse response = new JSONRPCResponse(JSONRPC.JSONRPC_VERSION, 1, null, error); String value = JSON_MAPPER.writeValueAsString(response); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -366,7 +391,7 @@ void testResource() throws Exception { McpSchema.Annotations annotations = new McpSchema.Annotations( Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); - McpSchema.Resource resource = McpSchema.Resource.builder() + Resource resource = Resource.builder() .uri("resource://test") .name("Test Resource") .description("A test resource") @@ -388,7 +413,7 @@ void testResourceBuilder() throws Exception { McpSchema.Annotations annotations = new McpSchema.Annotations( Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); - McpSchema.Resource resource = McpSchema.Resource.builder() + Resource resource = Resource.builder() .uri("resource://test") .name("Test Resource") .description("A test resource") @@ -412,7 +437,7 @@ void testResourceBuilderUriRequired() { McpSchema.Annotations annotations = new McpSchema.Annotations( Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); - McpSchema.Resource.Builder resourceBuilder = McpSchema.Resource.builder() + Resource.Builder resourceBuilder = Resource.builder() .name("Test Resource") .description("A test resource") .mimeType("text/plain") @@ -427,7 +452,7 @@ void testResourceBuilderNameRequired() { McpSchema.Annotations annotations = new McpSchema.Annotations( Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); - McpSchema.Resource.Builder resourceBuilder = McpSchema.Resource.builder() + Resource.Builder resourceBuilder = Resource.builder() .uri("resource://test") .description("A test resource") .mimeType("text/plain") @@ -442,8 +467,8 @@ void testResourceTemplate() throws Exception { McpSchema.Annotations annotations = new McpSchema.Annotations(Arrays.asList(McpSchema.Role.USER), 0.5); Map meta = Map.of("metaKey", "metaValue"); - McpSchema.ResourceTemplate template = new McpSchema.ResourceTemplate("resource://{param}/test", "Test Template", - "Test Template", "A test resource template", "text/plain", annotations, meta); + ResourceTemplate template = new ResourceTemplate("resource://{param}/test", "Test Template", "Test Template", + "A test resource template", "text/plain", annotations, meta); String value = JSON_MAPPER.writeValueAsString(template); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -456,14 +481,14 @@ void testResourceTemplate() throws Exception { @Test void testListResourcesResult() throws Exception { - McpSchema.Resource resource1 = McpSchema.Resource.builder() + Resource resource1 = Resource.builder() .uri("resource://test1") .name("Test Resource 1") .description("First test resource") .mimeType("text/plain") .build(); - McpSchema.Resource resource2 = McpSchema.Resource.builder() + Resource resource2 = Resource.builder() .uri("resource://test2") .name("Test Resource 2") .description("Second test resource") @@ -472,8 +497,7 @@ void testListResourcesResult() throws Exception { Map meta = Map.of("metaKey", "metaValue"); - McpSchema.ListResourcesResult result = new McpSchema.ListResourcesResult(Arrays.asList(resource1, resource2), - "next-cursor", meta); + ListResourcesResult result = new ListResourcesResult(Arrays.asList(resource1, resource2), "next-cursor", meta); String value = JSON_MAPPER.writeValueAsString(result); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -486,14 +510,14 @@ void testListResourcesResult() throws Exception { @Test void testListResourceTemplatesResult() throws Exception { - McpSchema.ResourceTemplate template1 = new McpSchema.ResourceTemplate("resource://{param}/test1", - "Test Template 1", "Test Template 1", "First test template", "text/plain", null); + ResourceTemplate template1 = new ResourceTemplate("resource://{param}/test1", "Test Template 1", + "Test Template 1", "First test template", "text/plain", null); - McpSchema.ResourceTemplate template2 = new McpSchema.ResourceTemplate("resource://{param}/test2", - "Test Template 2", "Test Template 2", "Second test template", "application/json", null); + ResourceTemplate template2 = new ResourceTemplate("resource://{param}/test2", "Test Template 2", + "Test Template 2", "Second test template", "application/json", null); - McpSchema.ListResourceTemplatesResult result = new McpSchema.ListResourceTemplatesResult( - Arrays.asList(template1, template2), "next-cursor"); + ListResourceTemplatesResult result = new ListResourceTemplatesResult(Arrays.asList(template1, template2), + "next-cursor"); String value = JSON_MAPPER.writeValueAsString(result); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -506,8 +530,7 @@ void testListResourceTemplatesResult() throws Exception { @Test void testReadResourceRequest() throws Exception { - McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("resource://test", - Map.of("metaKey", "metaValue")); + ReadResourceRequest request = new ReadResourceRequest("resource://test", Map.of("metaKey", "metaValue")); String value = JSON_MAPPER.writeValueAsString(request); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -522,7 +545,7 @@ void testReadResourceRequestWithMeta() throws Exception { Map meta = new HashMap<>(); meta.put("progressToken", "read-resource-token-123"); - McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("resource://test", meta); + ReadResourceRequest request = new ReadResourceRequest("resource://test", meta); String value = JSON_MAPPER.writeValueAsString(request); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -538,9 +561,8 @@ void testReadResourceRequestWithMeta() throws Exception { @Test void testReadResourceRequestDeserialization() throws Exception { - McpSchema.ReadResourceRequest request = JSON_MAPPER.readValue(""" - {"uri":"resource://test","_meta":{"progressToken":"test-token"}}""", - McpSchema.ReadResourceRequest.class); + ReadResourceRequest request = JSON_MAPPER.readValue(""" + {"uri":"resource://test","_meta":{"progressToken":"test-token"}}""", ReadResourceRequest.class); assertThat(request.uri()).isEqualTo("resource://test"); assertThat(request.meta()).containsEntry("progressToken", "test-token"); @@ -549,13 +571,13 @@ void testReadResourceRequestDeserialization() throws Exception { @Test void testReadResourceResult() throws Exception { - McpSchema.TextResourceContents contents1 = new McpSchema.TextResourceContents("resource://test1", "text/plain", + TextResourceContents contents1 = new TextResourceContents("resource://test1", "text/plain", "Sample text content"); - McpSchema.BlobResourceContents contents2 = new McpSchema.BlobResourceContents("resource://test2", - "application/octet-stream", "base64encodedblob"); + BlobResourceContents contents2 = new BlobResourceContents("resource://test2", "application/octet-stream", + "base64encodedblob"); - McpSchema.ReadResourceResult result = new McpSchema.ReadResourceResult(Arrays.asList(contents1, contents2), + ReadResourceResult result = new ReadResourceResult(Arrays.asList(contents1, contents2), Map.of("metaKey", "metaValue")); String value = JSON_MAPPER.writeValueAsString(result); @@ -571,13 +593,12 @@ void testReadResourceResult() throws Exception { @Test void testPrompt() throws Exception { - McpSchema.PromptArgument arg1 = new McpSchema.PromptArgument("arg1", "First argument", "First argument", true); + PromptArgument arg1 = new PromptArgument("arg1", "First argument", "First argument", true); - McpSchema.PromptArgument arg2 = new McpSchema.PromptArgument("arg2", "Second argument", "Second argument", - false); + PromptArgument arg2 = new PromptArgument("arg2", "Second argument", "Second argument", false); - McpSchema.Prompt prompt = new McpSchema.Prompt("test-prompt", "Test Prompt", "A test prompt", - Arrays.asList(arg1, arg2), Map.of("metaKey", "metaValue")); + Prompt prompt = new Prompt("test-prompt", "Test Prompt", "A test prompt", Arrays.asList(arg1, arg2), + Map.of("metaKey", "metaValue")); String value = JSON_MAPPER.writeValueAsString(prompt); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -592,7 +613,7 @@ void testPrompt() throws Exception { void testPromptMessage() throws Exception { McpSchema.TextContent content = new McpSchema.TextContent("Hello, world!"); - McpSchema.PromptMessage message = new McpSchema.PromptMessage(McpSchema.Role.USER, content); + PromptMessage message = new PromptMessage(McpSchema.Role.USER, content); String value = JSON_MAPPER.writeValueAsString(message); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -604,16 +625,13 @@ void testPromptMessage() throws Exception { @Test void testListPromptsResult() throws Exception { - McpSchema.PromptArgument arg = new McpSchema.PromptArgument("arg", "Argument", "An argument", true); + PromptArgument arg = new PromptArgument("arg", "Argument", "An argument", true); - McpSchema.Prompt prompt1 = new McpSchema.Prompt("prompt1", "First prompt", "First prompt", - Collections.singletonList(arg)); + Prompt prompt1 = new Prompt("prompt1", "First prompt", "First prompt", Collections.singletonList(arg)); - McpSchema.Prompt prompt2 = new McpSchema.Prompt("prompt2", "Second prompt", "Second prompt", - Collections.emptyList()); + Prompt prompt2 = new Prompt("prompt2", "Second prompt", "Second prompt", Collections.emptyList()); - McpSchema.ListPromptsResult result = new McpSchema.ListPromptsResult(Arrays.asList(prompt1, prompt2), - "next-cursor"); + ListPromptsResult result = new ListPromptsResult(Arrays.asList(prompt1, prompt2), "next-cursor"); String value = JSON_MAPPER.writeValueAsString(result); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -630,10 +648,10 @@ void testGetPromptRequest() throws Exception { arguments.put("arg1", "value1"); arguments.put("arg2", 42); - McpSchema.GetPromptRequest request = new McpSchema.GetPromptRequest("test-prompt", arguments); + GetPromptRequest request = new GetPromptRequest("test-prompt", arguments); assertThat(JSON_MAPPER.readValue(""" - {"name":"test-prompt","arguments":{"arg1":"value1","arg2":42}}""", McpSchema.GetPromptRequest.class)) + {"name":"test-prompt","arguments":{"arg1":"value1","arg2":42}}""", GetPromptRequest.class)) .isEqualTo(request); } @@ -646,7 +664,7 @@ void testGetPromptRequestWithMeta() throws Exception { Map meta = new HashMap<>(); meta.put("progressToken", "token123"); - McpSchema.GetPromptRequest request = new McpSchema.GetPromptRequest("test-prompt", arguments, meta); + GetPromptRequest request = new GetPromptRequest("test-prompt", arguments, meta); String value = JSON_MAPPER.writeValueAsString(request); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -666,12 +684,11 @@ void testGetPromptResult() throws Exception { McpSchema.TextContent content1 = new McpSchema.TextContent("System message"); McpSchema.TextContent content2 = new McpSchema.TextContent("User message"); - McpSchema.PromptMessage message1 = new McpSchema.PromptMessage(McpSchema.Role.ASSISTANT, content1); + PromptMessage message1 = new PromptMessage(McpSchema.Role.ASSISTANT, content1); - McpSchema.PromptMessage message2 = new McpSchema.PromptMessage(McpSchema.Role.USER, content2); + PromptMessage message2 = new PromptMessage(McpSchema.Role.USER, content2); - McpSchema.GetPromptResult result = new McpSchema.GetPromptResult("A test prompt result", - Arrays.asList(message1, message2)); + GetPromptResult result = new GetPromptResult("A test prompt result", Arrays.asList(message1, message2)); String value = JSON_MAPPER.writeValueAsString(result); @@ -713,13 +730,13 @@ void testJsonSchema() throws Exception { """; // Deserialize the original string to a JsonSchema object - McpSchema.JsonSchema schema = JSON_MAPPER.readValue(schemaJson, McpSchema.JsonSchema.class); + JsonSchema schema = JSON_MAPPER.readValue(schemaJson, JsonSchema.class); // Serialize the object back to a string String serialized = JSON_MAPPER.writeValueAsString(schema); // Deserialize again - McpSchema.JsonSchema deserialized = JSON_MAPPER.readValue(serialized, McpSchema.JsonSchema.class); + JsonSchema deserialized = JSON_MAPPER.readValue(serialized, JsonSchema.class); // Serialize one more time and compare with the first serialization String serializedAgain = JSON_MAPPER.writeValueAsString(deserialized); @@ -756,13 +773,13 @@ void testJsonSchemaWithDefinitions() throws Exception { """; // Deserialize the original string to a JsonSchema object - McpSchema.JsonSchema schema = JSON_MAPPER.readValue(schemaJson, McpSchema.JsonSchema.class); + JsonSchema schema = JSON_MAPPER.readValue(schemaJson, JsonSchema.class); // Serialize the object back to a string String serialized = JSON_MAPPER.writeValueAsString(schema); // Deserialize again - McpSchema.JsonSchema deserialized = JSON_MAPPER.readValue(serialized, McpSchema.JsonSchema.class); + JsonSchema deserialized = JSON_MAPPER.readValue(serialized, JsonSchema.class); // Serialize one more time and compare with the first serialization String serializedAgain = JSON_MAPPER.writeValueAsString(deserialized); @@ -788,7 +805,7 @@ void testTool() throws Exception { } """; - McpSchema.Tool tool = McpSchema.Tool.builder() + Tool tool = Tool.builder() .name("test-tool") .description("A test tool") .inputSchema(JSON_MAPPER, schemaJson) @@ -826,7 +843,7 @@ void testToolWithComplexSchema() throws Exception { } """; - McpSchema.Tool tool = McpSchema.Tool.builder() + Tool tool = Tool.builder() .name("addressTool") .title("Handles addresses") .inputSchema(JSON_MAPPER, complexSchemaJson) @@ -836,7 +853,7 @@ void testToolWithComplexSchema() throws Exception { String serialized = JSON_MAPPER.writeValueAsString(tool); // Deserialize back to a Tool object - McpSchema.Tool deserializedTool = JSON_MAPPER.readValue(serialized, McpSchema.Tool.class); + Tool deserializedTool = JSON_MAPPER.readValue(serialized, Tool.class); // Serialize again and compare with first serialization String serializedAgain = JSON_MAPPER.writeValueAsString(deserializedTool); @@ -866,10 +883,10 @@ void testToolWithMeta() throws Exception { } """; - McpSchema.JsonSchema schema = JSON_MAPPER.readValue(schemaJson, McpSchema.JsonSchema.class); + JsonSchema schema = JSON_MAPPER.readValue(schemaJson, JsonSchema.class); Map meta = Map.of("metaKey", "metaValue"); - McpSchema.Tool tool = McpSchema.Tool.builder() + Tool tool = Tool.builder() .name("addressTool") .title("addressTool") .description("Handles addresses") @@ -898,10 +915,9 @@ void testToolWithAnnotations() throws Exception { "required": ["name"] } """; - McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations("A test tool", false, false, false, false, - false); + ToolAnnotations annotations = new ToolAnnotations("A test tool", false, false, false, false, false); - McpSchema.Tool tool = McpSchema.Tool.builder() + Tool tool = Tool.builder() .name("test-tool") .description("A test tool") .inputSchema(JSON_MAPPER, schemaJson) @@ -969,7 +985,7 @@ void testToolWithOutputSchema() throws Exception { } """; - McpSchema.Tool tool = McpSchema.Tool.builder() + Tool tool = Tool.builder() .name("test-tool") .description("A test tool") .inputSchema(JSON_MAPPER, inputSchemaJson) @@ -1033,10 +1049,9 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception { } """; - McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations("A test tool with output", true, false, - true, false, true); + ToolAnnotations annotations = new ToolAnnotations("A test tool with output", true, false, true, false, true); - McpSchema.Tool tool = McpSchema.Tool.builder() + Tool tool = Tool.builder() .name("test-tool") .description("A test tool") .inputSchema(JSON_MAPPER, inputSchemaJson) @@ -1108,7 +1123,7 @@ void testToolDeserialization() throws Exception { } """; - McpSchema.Tool tool = JSON_MAPPER.readValue(toolJson, McpSchema.Tool.class); + Tool tool = JSON_MAPPER.readValue(toolJson, Tool.class); assertThat(tool).isNotNull(); assertThat(tool.name()).isEqualTo("test-tool"); @@ -1142,7 +1157,7 @@ void testToolDeserializationWithoutOutputSchema() throws Exception { } """; - McpSchema.Tool tool = JSON_MAPPER.readValue(toolJson, McpSchema.Tool.class); + Tool tool = JSON_MAPPER.readValue(toolJson, Tool.class); assertThat(tool).isNotNull(); assertThat(tool.name()).isEqualTo("test-tool"); @@ -1158,7 +1173,7 @@ void testCallToolRequest() throws Exception { arguments.put("name", "test"); arguments.put("value", 42); - McpSchema.CallToolRequest request = new McpSchema.CallToolRequest("test-tool", arguments); + CallToolRequest request = new CallToolRequest("test-tool", arguments); String value = JSON_MAPPER.writeValueAsString(request); @@ -1172,7 +1187,7 @@ void testCallToolRequest() throws Exception { @Test void testCallToolRequestJsonArguments() throws Exception { - McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(JSON_MAPPER, "test-tool", """ + CallToolRequest request = new CallToolRequest(JSON_MAPPER, "test-tool", """ { "name": "test", "value": 42 @@ -1191,7 +1206,7 @@ void testCallToolRequestJsonArguments() throws Exception { @Test void testCallToolRequestWithMeta() throws Exception { - McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder() + CallToolRequest request = CallToolRequest.builder() .name("test-tool") .arguments(Map.of("name", "test", "value", 42)) .progressToken("tool-progress-123") @@ -1215,16 +1230,12 @@ void testCallToolRequestBuilderWithJsonArguments() throws Exception { Map meta = new HashMap<>(); meta.put("progressToken", "json-builder-789"); - McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder() - .name("test-tool") - .arguments(JSON_MAPPER, """ - { - "name": "test", - "value": 42 - } - """) - .meta(meta) - .build(); + CallToolRequest request = CallToolRequest.builder().name("test-tool").arguments(JSON_MAPPER, """ + { + "name": "test", + "value": 42 + } + """).meta(meta).build(); String value = JSON_MAPPER.writeValueAsString(request); @@ -1245,7 +1256,7 @@ void testCallToolRequestBuilderNameRequired() { Map arguments = new HashMap<>(); arguments.put("name", "test"); - McpSchema.CallToolRequest.Builder builder = McpSchema.CallToolRequest.builder().arguments(arguments); + CallToolRequest.Builder builder = CallToolRequest.builder().arguments(arguments); assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("name must not be empty"); @@ -1255,9 +1266,7 @@ void testCallToolRequestBuilderNameRequired() { void testCallToolResult() throws Exception { McpSchema.TextContent content = new McpSchema.TextContent("Tool execution result"); - McpSchema.CallToolResult result = McpSchema.CallToolResult.builder() - .content(Collections.singletonList(content)) - .build(); + CallToolResult result = CallToolResult.builder().content(Collections.singletonList(content)).build(); String value = JSON_MAPPER.writeValueAsString(result); @@ -1270,10 +1279,7 @@ void testCallToolResult() throws Exception { @Test void testCallToolResultBuilder() throws Exception { - McpSchema.CallToolResult result = McpSchema.CallToolResult.builder() - .addTextContent("Tool execution result") - .isError(false) - .build(); + CallToolResult result = CallToolResult.builder().addTextContent("Tool execution result").isError(false).build(); String value = JSON_MAPPER.writeValueAsString(result); @@ -1289,7 +1295,7 @@ void testCallToolResultBuilderWithMultipleContents() throws Exception { McpSchema.TextContent textContent = new McpSchema.TextContent("Text result"); McpSchema.ImageContent imageContent = new McpSchema.ImageContent(null, "base64data", "image/png"); - McpSchema.CallToolResult result = McpSchema.CallToolResult.builder() + CallToolResult result = CallToolResult.builder() .addContent(textContent) .addContent(imageContent) .isError(false) @@ -1311,7 +1317,7 @@ void testCallToolResultBuilderWithContentList() throws Exception { McpSchema.ImageContent imageContent = new McpSchema.ImageContent(null, "base64data", "image/png"); List contents = Arrays.asList(textContent, imageContent); - McpSchema.CallToolResult result = McpSchema.CallToolResult.builder().content(contents).isError(true).build(); + CallToolResult result = CallToolResult.builder().content(contents).isError(true).build(); String value = JSON_MAPPER.writeValueAsString(result); @@ -1325,7 +1331,7 @@ void testCallToolResultBuilderWithContentList() throws Exception { @Test void testCallToolResultBuilderWithErrorResult() throws Exception { - McpSchema.CallToolResult result = McpSchema.CallToolResult.builder() + CallToolResult result = CallToolResult.builder() .addTextContent("Error: Operation failed") .isError(true) .build(); @@ -1345,21 +1351,20 @@ void testCallToolResultBuilderWithErrorResult() throws Exception { void testCreateMessageRequest() throws Exception { McpSchema.TextContent content = new McpSchema.TextContent("User message"); - McpSchema.SamplingMessage message = new McpSchema.SamplingMessage(McpSchema.Role.USER, content); + SamplingMessage message = new SamplingMessage(McpSchema.Role.USER, content); - McpSchema.ModelHint hint = new McpSchema.ModelHint("gpt-4"); + ModelHint hint = new ModelHint("gpt-4"); - McpSchema.ModelPreferences preferences = new McpSchema.ModelPreferences(Collections.singletonList(hint), 0.3, - 0.7, 0.9); + ModelPreferences preferences = new ModelPreferences(Collections.singletonList(hint), 0.3, 0.7, 0.9); Map metadata = new HashMap<>(); metadata.put("session", "test-session"); - McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() + CreateMessageRequest request = CreateMessageRequest.builder() .messages(Collections.singletonList(message)) .modelPreferences(preferences) .systemPrompt("You are a helpful assistant") - .includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER) + .includeContext(CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER) .temperature(0.7) .maxTokens(1000) .stopSequences(Arrays.asList("STOP", "END")) @@ -1380,11 +1385,11 @@ void testCreateMessageRequest() throws Exception { void testCreateMessageResult() throws Exception { McpSchema.TextContent content = new McpSchema.TextContent("Assistant response"); - McpSchema.CreateMessageResult result = McpSchema.CreateMessageResult.builder() + CreateMessageResult result = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(content) .model("gpt-4") - .stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN) + .stopReason(CreateMessageResult.StopReason.END_TURN) .build(); String value = JSON_MAPPER.writeValueAsString(result); @@ -1402,14 +1407,14 @@ void testCreateMessageResultUnknownStopReason() throws Exception { String input = """ {"role":"assistant","content":{"type":"text","text":"Assistant response"},"model":"gpt-4","stopReason":"arbitrary value"}"""; - McpSchema.CreateMessageResult value = JSON_MAPPER.readValue(input, McpSchema.CreateMessageResult.class); + CreateMessageResult value = JSON_MAPPER.readValue(input, CreateMessageResult.class); McpSchema.TextContent expectedContent = new McpSchema.TextContent("Assistant response"); - McpSchema.CreateMessageResult expected = McpSchema.CreateMessageResult.builder() + CreateMessageResult expected = CreateMessageResult.builder() .role(McpSchema.Role.ASSISTANT) .content(expectedContent) .model("gpt-4") - .stopReason(McpSchema.CreateMessageResult.StopReason.UNKNOWN) + .stopReason(CreateMessageResult.StopReason.UNKNOWN) .build(); assertThat(value).isEqualTo(expected); } @@ -1418,7 +1423,7 @@ void testCreateMessageResultUnknownStopReason() throws Exception { @Test void testCreateElicitationRequest() throws Exception { - McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder() + ElicitRequest request = ElicitRequest.builder() .requestedSchema(Map.of("type", "object", "required", List.of("a"), "properties", Map.of("foo", Map.of("type", "string")))) .build(); @@ -1434,9 +1439,9 @@ void testCreateElicitationRequest() throws Exception { @Test void testCreateElicitationResult() throws Exception { - McpSchema.ElicitResult result = McpSchema.ElicitResult.builder() + ElicitResult result = ElicitResult.builder() .content(Map.of("foo", "bar")) - .message(McpSchema.ElicitResult.Action.ACCEPT) + .message(ElicitResult.Action.ACCEPT) .build(); String value = JSON_MAPPER.writeValueAsString(result); @@ -1456,7 +1461,7 @@ void testElicitRequestWithMeta() throws Exception { Map meta = new HashMap<>(); meta.put("progressToken", "elicit-token-789"); - McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder() + ElicitRequest request = ElicitRequest.builder() .message("Please provide your name") .requestedSchema(requestedSchema) .meta(meta) diff --git a/pom.xml b/pom.xml index b1eedd38e..5c3fcaea4 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,7 @@ mcp-json-jackson2 mcp-json-jackson3 mcp-test + mcp-migrate conformance-tests