Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 180 additions & 89 deletions MIGRATION-2.0.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions conformance-tests/client-jdk-http-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>conformance-tests</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</parent>
<artifactId>client-jdk-http-client</artifactId>
<packaging>jar</packaging>
Expand All @@ -28,7 +28,7 @@
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</dependency>

<!-- Logging -->
Expand Down
2 changes: 1 addition & 1 deletion conformance-tests/client-spring-http-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>conformance-tests</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</parent>
<artifactId>client-spring-http-client</artifactId>
<packaging>jar</packaging>
Expand Down
2 changes: 1 addition & 1 deletion conformance-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</parent>
<artifactId>conformance-tests</artifactId>
<packaging>pom</packaging>
Expand Down
4 changes: 2 additions & 2 deletions conformance-tests/server-servlet/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>conformance-tests</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</parent>
<artifactId>server-servlet</artifactId>
<packaging>jar</packaging>
Expand All @@ -28,7 +28,7 @@
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import java.util.List;
import java.util.Map;

import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.transport.DefaultServerTransportSecurityValidator;
import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.AudioContent;
import io.modelcontextprotocol.spec.McpSchema.BlobResourceContents;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
Expand Down Expand Up @@ -43,6 +44,16 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.modelcontextprotocol.spec.McpSchema.EnumSchemaOption;
import static io.modelcontextprotocol.spec.McpSchema.JSON_SCHEMA_DIALECT_2020_12;
import static io.modelcontextprotocol.spec.McpSchema.LegacyTitledEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.TitledMultiSelectEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.TitledMultiSelectItems;
import static io.modelcontextprotocol.spec.McpSchema.TitledSingleSelectEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.UntitledMultiSelectEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.UntitledMultiSelectItems;
import static io.modelcontextprotocol.spec.McpSchema.UntitledSingleSelectEnumSchema;

public class ConformanceServlet {

private static final Logger logger = LoggerFactory.getLogger(ConformanceServlet.class);
Expand Down Expand Up @@ -141,6 +152,7 @@ private static Tomcat createEmbeddedTomcat(HttpServletStreamableServerTransportP
return tomcat;
}

@SuppressWarnings("deprecation")
private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
return List.of(
// test_simple_text - Returns simple text content
Expand Down Expand Up @@ -406,8 +418,8 @@ private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
// json_schema_2020_12_tool - SEP-1613 dialect/keyword preservation
McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool
.builder("json_schema_2020_12_tool", Map.of("$schema", McpSchema.JSON_SCHEMA_DIALECT_2020_12,
"type", "object", "$defs",
.builder("json_schema_2020_12_tool", Map.of("$schema", JSON_SCHEMA_DIALECT_2020_12, "type",
"object", "$defs",
Map.of("address",
Map.of("type", "object", "properties",
Map.of("street", Map.of("type", "string"), "city",
Expand All @@ -434,33 +446,44 @@ private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
.callHandler((exchange, request) -> {
logger.info("Tool 'test_elicitation_sep1330_enums' called");

// Create schema with all 5 enum variants
Map<String, Object> requestedSchema = Map.of("type", "object", "properties", Map.of(
// 1. Untitled single-select
"untitledSingle",
Map.of("type", "string", "enum", List.of("option1", "option2", "option3")),
// 2. Titled single-select using oneOf with const/title
"titledSingle",
Map.of("type", "string", "oneOf",
List.of(Map.of("const", "value1", "title", "First Option"),
Map.of("const", "value2", "title", "Second Option"),
Map.of("const", "value3", "title", "Third Option"))),
// 3. Legacy titled using enumNames (deprecated)
"legacyEnum",
Map.of("type", "string", "enum", List.of("opt1", "opt2", "opt3"), "enumNames",
List.of("Option One", "Option Two", "Option Three")),
// 4. Untitled multi-select
"untitledMulti",
Map.of("type", "array", "items",
Map.of("type", "string", "enum", List.of("option1", "option2", "option3"))),
// 5. Titled multi-select using items.anyOf with
// const/title
"titledMulti",
Map.of("type", "array", "items",
Map.of("anyOf",
List.of(Map.of("const", "value1", "title", "First Choice"),
Map.of("const", "value2", "title", "Second Choice"),
Map.of("const", "value3", "title", "Third Choice"))))),
TypeRef<Map<String, Object>> mapType = new TypeRef<>() {
};
var mapper = McpJsonDefaults.getMapper();

// 1. Untitled single-select
var untitledSingle = UntitledSingleSelectEnumSchema.builder()
.enumValues("option1", "option2", "option3")
.build();
// 2. Titled single-select using oneOf with const/title
var titledSingle = TitledSingleSelectEnumSchema.builder()
.oneOf(new EnumSchemaOption("value1", "First Option"),
new EnumSchemaOption("value2", "Second Option"),
new EnumSchemaOption("value3", "Third Option"))
.build();
// 3. Legacy titled using enumNames (deprecated)
var legacyEnum = LegacyTitledEnumSchema.builder()
.enumValues("opt1", "opt2", "opt3")
.enumNames("Option One", "Option Two", "Option Three")
.build();
// 4. Untitled multi-select
var untitledMulti = UntitledMultiSelectEnumSchema.builder(
UntitledMultiSelectItems.builder().enumValues("option1", "option2", "option3").build())
.build();
// 5. Titled multi-select using items.anyOf with const/title
var titledMulti = TitledMultiSelectEnumSchema
.builder(TitledMultiSelectItems.builder()
.anyOf(new EnumSchemaOption("value1", "First Choice"),
new EnumSchemaOption("value2", "Second Choice"),
new EnumSchemaOption("value3", "Third Choice"))
.build())
.build();

Map<String, Object> requestedSchema = Map.of("type", "object", "properties",
Map.of("untitledSingle", mapper.convertValue(untitledSingle, mapType), "titledSingle",
mapper.convertValue(titledSingle, mapType), "legacyEnum",
mapper.convertValue(legacyEnum, mapType), "untitledMulti",
mapper.convertValue(untitledMulti, mapType), "titledMulti",
mapper.convertValue(titledMulti, mapType)),
"required", List.of("untitledSingle", "titledSingle", "legacyEnum", "untitledMulti",
"titledMulti"));

Expand Down
82 changes: 48 additions & 34 deletions docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,21 @@ The client provides both synchronous and asynchronous APIs for flexibility in di

// Call a tool
CallToolResult result = client.callTool(
new CallToolRequest("calculator",
Map.of("operation", "add", "a", 2, "b", 3))
CallToolRequest.builder("calculator")
.arguments(Map.of("operation", "add", "a", 2, "b", 3))
.build()
);

// List and read resources
ListResourcesResult resources = client.listResources();
ReadResourceResult resource = client.readResource(
new ReadResourceRequest("resource://uri")
ReadResourceRequest.builder("resource://uri").build()
);

// List and use prompts
ListPromptsResult prompts = client.listPrompts();
GetPromptResult prompt = client.getPrompt(
new GetPromptRequest("greeting", Map.of("name", "Spring"))
GetPromptRequest.builder("greeting").arguments(Map.of("name", "Spring")).build()
);

// Add/remove roots
Expand Down Expand Up @@ -102,24 +103,22 @@ The client provides both synchronous and asynchronous APIs for flexibility in di
client.initialize()
.flatMap(initResult -> client.listTools())
.flatMap(tools -> {
return client.callTool(new CallToolRequest(
"calculator",
Map.of("operation", "add", "a", 2, "b", 3)
));
return client.callTool(CallToolRequest.builder("calculator")
.arguments(Map.of("operation", "add", "a", 2, "b", 3))
.build());
})
.flatMap(result -> {
return client.listResources()
.flatMap(resources ->
client.readResource(new ReadResourceRequest("resource://uri"))
client.readResource(ReadResourceRequest.builder("resource://uri").build())
);
})
.flatMap(resource -> {
return client.listPrompts()
.flatMap(prompts ->
client.getPrompt(new GetPromptRequest(
"greeting",
Map.of("name", "Spring")
))
client.getPrompt(GetPromptRequest.builder("greeting")
.arguments(Map.of("name", "Spring"))
.build())
);
})
.flatMap(prompt -> {
Expand All @@ -144,7 +143,7 @@ Creates transport for process-based communication using stdin/stdout:
ServerParameters params = ServerParameters.builder("npx")
.args("-y", "@modelcontextprotocol/server-everything", "dir")
.build();
McpTransport transport = new StdioClientTransport(params);
McpTransport transport = new StdioClientTransport(params, McpJsonDefaults.getMapper());
```

### Streamable HTTP
Expand Down Expand Up @@ -184,7 +183,7 @@ McpTransport transport = new StdioClientTransport(params);
Creates a framework-agnostic (pure Java API) SSE client transport. Included in the core `mcp` module:

```java
McpTransport transport = new HttpClientSseClientTransport("http://your-mcp-server");
McpTransport transport = HttpClientSseClientTransport.builder("http://your-mcp-server").build();
```
=== "SSE WebClient (external)"

Expand Down Expand Up @@ -301,6 +300,17 @@ The `ElicitResult` supports three actions:
- `DECLINE` - The user declined to provide the information
- `CANCEL` - The operation was cancelled

You can optionally have the client fill in missing values from the schema's `default` declarations before returning an accepted result to the server:

```java
var client = McpClient.sync(transport)
.applyElicitationDefaults(true) // default is false
.elicitation(formElicitationHandler)
.build();
```

When enabled, any keys absent from an accepted `ElicitResult.content` are populated with the `default` values declared in the request's `requestedSchema`.

#### URL Elicitation Required Handling

When a server requires out-of-band URL elicitation but the client has not negotiated support for it (or the server strictly requires out-of-band handling), the server may return a `URL_ELICITATION_REQUIRED` error during tool execution or prompt retrieval.
Expand Down Expand Up @@ -339,7 +349,7 @@ mcpClient.initialize();
mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO);

// Call the tool that sends logging notifications
CallToolResult result = mcpClient.callTool(new CallToolRequest("logging-test", Map.of()));
CallToolResult result = mcpClient.callTool(CallToolRequest.builder("logging-test").build());
```

Clients can control the minimum logging level they receive through the `mcpClient.setLoggingLevel(level)` request. Messages below the set level will be filtered out.
Expand Down Expand Up @@ -371,11 +381,13 @@ Tools are server-side functions that clients can discover and execute. The MCP c

// Call a tool with a CallToolRequest
CallToolResult result = client.callTool(
new CallToolRequest("calculator", Map.of(
"operation", "add",
"a", 1,
"b", 2
))
CallToolRequest.builder("calculator")
.arguments(Map.of(
"operation", "add",
"a", 1,
"b", 2
))
.build()
);
```

Expand All @@ -389,11 +401,13 @@ Tools are server-side functions that clients can discover and execute. The MCP c
.subscribe();

// Call a tool asynchronously
client.callTool(new CallToolRequest("calculator", Map.of(
"operation", "add",
"a", 1,
"b", 2
)))
client.callTool(CallToolRequest.builder("calculator")
.arguments(Map.of(
"operation", "add",
"a", 1,
"b", 2
))
.build())
.subscribe();
```

Expand All @@ -420,7 +434,7 @@ Resources represent server-side data sources that clients can access using URI t

// Read a resource
ReadResourceResult resource = client.readResource(
new ReadResourceRequest("resource://uri")
ReadResourceRequest.builder("resource://uri").build()
);
```

Expand All @@ -434,7 +448,7 @@ Resources represent server-side data sources that clients can access using URI t
.subscribe();

// Read a resource asynchronously
client.readResource(new ReadResourceRequest("resource://uri"))
client.readResource(ReadResourceRequest.builder("resource://uri").build())
.subscribe();
```

Expand All @@ -457,10 +471,10 @@ Register a consumer on the client builder, then subscribe/unsubscribe at any tim
client.initialize();

// Subscribe to a specific resource URI
client.subscribeResource(new McpSchema.SubscribeRequest("custom://resource"));
client.subscribeResource(McpSchema.SubscribeRequest.builder("custom://resource").build());

// ... later, stop receiving updates
client.unsubscribeResource(new McpSchema.UnsubscribeRequest("custom://resource"));
client.unsubscribeResource(McpSchema.UnsubscribeRequest.builder("custom://resource").build());
```

=== "Async API"
Expand All @@ -473,11 +487,11 @@ Register a consumer on the client builder, then subscribe/unsubscribe at any tim
.build();

client.initialize()
.then(client.subscribeResource(new McpSchema.SubscribeRequest("custom://resource")))
.then(client.subscribeResource(McpSchema.SubscribeRequest.builder("custom://resource").build()))
.subscribe();

// ... later, stop receiving updates
client.unsubscribeResource(new McpSchema.UnsubscribeRequest("custom://resource"))
client.unsubscribeResource(McpSchema.UnsubscribeRequest.builder("custom://resource").build())
.subscribe();
```

Expand All @@ -493,7 +507,7 @@ The prompt system enables interaction with server-side prompt templates. These t

// Get a prompt with parameters
GetPromptResult prompt = client.getPrompt(
new GetPromptRequest("greeting", Map.of("name", "World"))
GetPromptRequest.builder("greeting").arguments(Map.of("name", "World")).build()
);
```

Expand All @@ -507,6 +521,6 @@ The prompt system enables interaction with server-side prompt templates. These t
.subscribe();

// Get a prompt asynchronously
client.getPrompt(new GetPromptRequest("greeting", Map.of("name", "World")))
client.getPrompt(GetPromptRequest.builder("greeting").arguments(Map.of("name", "World")).build())
.subscribe();
```
4 changes: 2 additions & 2 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Add the BOM to your project:
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-bom</artifactId>
<version>1.0.0</version>
<version>2.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -135,7 +135,7 @@ Add the BOM to your project:

```groovy
dependencies {
implementation platform("io.modelcontextprotocol.sdk:mcp-bom:1.0.0")
implementation platform("io.modelcontextprotocol.sdk:mcp-bom:2.0.0")
//...
}
```
Expand Down
Loading
Loading