Skip to content

Commit 52edd37

Browse files
committed
Merge branch 'main' into json-compatibility
Signed-off-by: Dariusz Jędrzejczyk <2554306+chemicL@users.noreply.github.com>
2 parents d7de6b8 + d182338 commit 52edd37

24 files changed

Lines changed: 945 additions & 118 deletions

conformance-tests/server-servlet/src/main/java/io/modelcontextprotocol/conformance/server/ConformanceServlet.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import io.modelcontextprotocol.spec.McpSchema.EmbeddedResource;
2121
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
2222
import io.modelcontextprotocol.spec.McpSchema.ImageContent;
23-
import io.modelcontextprotocol.spec.McpSchema.JsonSchema;
2423
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
2524
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
2625
import io.modelcontextprotocol.spec.McpSchema.ProgressNotification;
@@ -51,8 +50,8 @@ public class ConformanceServlet {
5150

5251
private static final String MCP_ENDPOINT = "/mcp";
5352

54-
private static final JsonSchema EMPTY_JSON_SCHEMA = new JsonSchema("object", Collections.emptyMap(), null, null,
55-
null, null);
53+
private static final Map<String, Object> EMPTY_JSON_SCHEMA = Map.of("type", "object", "properties",
54+
Collections.emptyMap());
5655

5756
// Minimal 1x1 red pixel PNG (base64 encoded)
5857
private static final String RED_PIXEL_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";
@@ -326,10 +325,10 @@ private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
326325
.tool(Tool.builder()
327326
.name("test_sampling")
328327
.description("Tool that requests LLM sampling from client")
329-
.inputSchema(new JsonSchema("object",
328+
.inputSchema(Map.of("type", "object", "properties",
330329
Map.of("prompt",
331330
Map.of("type", "string", "description", "The prompt to send to the LLM")),
332-
List.of("prompt"), null, null, null))
331+
"required", List.of("prompt")))
333332
.build())
334333
.callHandler((exchange, request) -> {
335334
logger.info("Tool 'test_sampling' called");
@@ -355,10 +354,10 @@ private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
355354
.tool(Tool.builder()
356355
.name("test_elicitation")
357356
.description("Tool that requests user input from client")
358-
.inputSchema(new JsonSchema("object",
357+
.inputSchema(Map.of("type", "object", "properties",
359358
Map.of("message",
360359
Map.of("type", "string", "description", "The message to show the user")),
361-
List.of("message"), null, null, null))
360+
"required", List.of("message")))
362361
.build())
363362
.callHandler((exchange, request) -> {
364363
logger.info("Tool 'test_elicitation' called");

mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ public class McpAsyncClient {
303303
return Mono.empty();
304304
}
305305

306-
return this.listToolsInternal(init, McpSchema.FIRST_PAGE).doOnNext(listToolsResult -> {
306+
return this.listToolsInternal(init, McpSchema.FIRST_PAGE, null).doOnNext(listToolsResult -> {
307307
listToolsResult.tools()
308308
.forEach(tool -> logger.debug("Tool {} schema: {}", tool.name(), tool.outputSchema()));
309309
if (enableCallToolSchemaCaching && listToolsResult.tools() != null) {
@@ -645,16 +645,27 @@ public Mono<McpSchema.ListToolsResult> listTools() {
645645
* @return A Mono that emits the list of tools result
646646
*/
647647
public Mono<McpSchema.ListToolsResult> listTools(String cursor) {
648-
return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor));
648+
return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor, null));
649649
}
650650

651-
private Mono<McpSchema.ListToolsResult> listToolsInternal(Initialization init, String cursor) {
651+
/**
652+
* Retrieves a paginated list of tools with optional metadata.
653+
* @param cursor Optional pagination cursor from a previous list request
654+
* @param meta Optional metadata to include in the request (_meta field)
655+
* @return A Mono that emits the list of tools result
656+
*/
657+
public Mono<McpSchema.ListToolsResult> listTools(String cursor, Map<String, Object> meta) {
658+
return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor, meta));
659+
}
660+
661+
private Mono<McpSchema.ListToolsResult> listToolsInternal(Initialization init, String cursor,
662+
Map<String, Object> meta) {
652663

653664
if (init.initializeResult().capabilities().tools() == null) {
654665
return Mono.error(new IllegalStateException("Server does not provide tools capability"));
655666
}
656667
return init.mcpSession()
657-
.sendRequest(McpSchema.METHOD_TOOLS_LIST, new McpSchema.PaginatedRequest(cursor),
668+
.sendRequest(McpSchema.METHOD_TOOLS_LIST, new McpSchema.PaginatedRequest(cursor, meta),
658669
LIST_TOOLS_RESULT_TYPE_REF)
659670
.doOnNext(result -> {
660671
// Validate tool names (warn only)
@@ -725,12 +736,30 @@ public Mono<McpSchema.ListResourcesResult> listResources() {
725736
* @see #readResource(McpSchema.Resource)
726737
*/
727738
public Mono<McpSchema.ListResourcesResult> listResources(String cursor) {
739+
return this.listResourcesInternal(cursor, null);
740+
}
741+
742+
/**
743+
* Retrieves a paginated list of resources provided by the server. Resources represent
744+
* any kind of UTF-8 encoded data that an MCP server makes available to clients, such
745+
* as database records, API responses, log files, and more.
746+
* @param cursor Optional pagination cursor from a previous list request
747+
* @param meta Optional metadata to include in the request (_meta field)
748+
* @return A Mono that completes with the list of resources result.
749+
* @see McpSchema.ListResourcesResult
750+
* @see #readResource(McpSchema.Resource)
751+
*/
752+
public Mono<McpSchema.ListResourcesResult> listResources(String cursor, Map<String, Object> meta) {
753+
return this.listResourcesInternal(cursor, meta);
754+
}
755+
756+
private Mono<McpSchema.ListResourcesResult> listResourcesInternal(String cursor, Map<String, Object> meta) {
728757
return this.initializer.withInitialization("listing resources", init -> {
729758
if (init.initializeResult().capabilities().resources() == null) {
730759
return Mono.error(new IllegalStateException("Server does not provide the resources capability"));
731760
}
732761
return init.mcpSession()
733-
.sendRequest(McpSchema.METHOD_RESOURCES_LIST, new McpSchema.PaginatedRequest(cursor),
762+
.sendRequest(McpSchema.METHOD_RESOURCES_LIST, new McpSchema.PaginatedRequest(cursor, meta),
734763
LIST_RESOURCES_RESULT_TYPE_REF);
735764
});
736765
}
@@ -795,12 +824,30 @@ public Mono<McpSchema.ListResourceTemplatesResult> listResourceTemplates() {
795824
* @see McpSchema.ListResourceTemplatesResult
796825
*/
797826
public Mono<McpSchema.ListResourceTemplatesResult> listResourceTemplates(String cursor) {
827+
return this.listResourceTemplatesInternal(cursor, null);
828+
}
829+
830+
/**
831+
* Retrieves a paginated list of resource templates provided by the server. Resource
832+
* templates allow servers to expose parameterized resources using URI templates,
833+
* enabling dynamic resource access based on variable parameters.
834+
* @param cursor Optional pagination cursor from a previous list request
835+
* @param meta Optional metadata to include in the request (_meta field)
836+
* @return A Mono that completes with the list of resource templates result.
837+
* @see McpSchema.ListResourceTemplatesResult
838+
*/
839+
public Mono<McpSchema.ListResourceTemplatesResult> listResourceTemplates(String cursor, Map<String, Object> meta) {
840+
return this.listResourceTemplatesInternal(cursor, meta);
841+
}
842+
843+
private Mono<McpSchema.ListResourceTemplatesResult> listResourceTemplatesInternal(String cursor,
844+
Map<String, Object> meta) {
798845
return this.initializer.withInitialization("listing resource templates", init -> {
799846
if (init.initializeResult().capabilities().resources() == null) {
800847
return Mono.error(new IllegalStateException("Server does not provide the resources capability"));
801848
}
802849
return init.mcpSession()
803-
.sendRequest(McpSchema.METHOD_RESOURCES_TEMPLATES_LIST, new McpSchema.PaginatedRequest(cursor),
850+
.sendRequest(McpSchema.METHOD_RESOURCES_TEMPLATES_LIST, new McpSchema.PaginatedRequest(cursor, meta),
804851
LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF);
805852
});
806853
}
@@ -895,8 +942,26 @@ public Mono<ListPromptsResult> listPrompts() {
895942
* @see #getPrompt(GetPromptRequest)
896943
*/
897944
public Mono<ListPromptsResult> listPrompts(String cursor) {
898-
return this.initializer.withInitialization("listing prompts", init -> init.mcpSession()
899-
.sendRequest(McpSchema.METHOD_PROMPT_LIST, new PaginatedRequest(cursor), LIST_PROMPTS_RESULT_TYPE_REF));
945+
return this.listPromptsInternal(cursor, null);
946+
}
947+
948+
/**
949+
* Retrieves a paginated list of prompts with optional metadata.
950+
* @param cursor Optional pagination cursor from a previous list request
951+
* @param meta Optional metadata to include in the request (_meta field)
952+
* @return A Mono that completes with the list of prompts result.
953+
* @see McpSchema.ListPromptsResult
954+
* @see #getPrompt(GetPromptRequest)
955+
*/
956+
public Mono<ListPromptsResult> listPrompts(String cursor, Map<String, Object> meta) {
957+
return this.listPromptsInternal(cursor, meta);
958+
}
959+
960+
private Mono<ListPromptsResult> listPromptsInternal(String cursor, Map<String, Object> meta) {
961+
return this.initializer.withInitialization("listing prompts",
962+
init -> init.mcpSession()
963+
.sendRequest(McpSchema.METHOD_PROMPT_LIST, new PaginatedRequest(cursor, meta),
964+
LIST_PROMPTS_RESULT_TYPE_REF));
900965
}
901966

902967
/**

mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.modelcontextprotocol.client;
66

77
import java.time.Duration;
8+
import java.util.Map;
89
import java.util.function.Supplier;
910

1011
import org.slf4j.Logger;
@@ -259,6 +260,18 @@ public McpSchema.ListToolsResult listTools(String cursor) {
259260

260261
}
261262

263+
/**
264+
* Retrieves a paginated list of tools provided by the server.
265+
* @param cursor Optional pagination cursor from a previous list request
266+
* @param meta Optional metadata to include in the request (_meta field)
267+
* @return The list of tools result containing: - tools: List of available tools, each
268+
* with a name, description, and input schema - nextCursor: Optional cursor for
269+
* pagination if more tools are available
270+
*/
271+
public McpSchema.ListToolsResult listTools(String cursor, Map<String, Object> meta) {
272+
return withProvidedContext(this.delegate.listTools(cursor, meta)).block();
273+
}
274+
262275
// --------------------------
263276
// Resources
264277
// --------------------------
@@ -282,6 +295,17 @@ public McpSchema.ListResourcesResult listResources(String cursor) {
282295

283296
}
284297

298+
/**
299+
* Retrieves a paginated list of resources with optional metadata.
300+
* @param cursor Optional pagination cursor from a previous list request
301+
* @param meta Optional metadata to include in the request (_meta field)
302+
* @return The list of resources result
303+
*/
304+
public McpSchema.ListResourcesResult listResources(String cursor, Map<String, Object> meta) {
305+
return withProvidedContext(this.delegate.listResources(cursor, meta)).block();
306+
307+
}
308+
285309
/**
286310
* Send a resources/read request.
287311
* @param resource the resource to read
@@ -324,6 +348,20 @@ public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor
324348

325349
}
326350

351+
/**
352+
* Resource templates allow servers to expose parameterized resources using URI
353+
* templates. Arguments may be auto-completed through the completion API.
354+
*
355+
* Retrieves a paginated list of resource templates provided by the server.
356+
* @param cursor Optional pagination cursor from a previous list request
357+
* @param meta Optional metadata to include in the request (_meta field)
358+
* @return The list of resource templates result.
359+
*/
360+
public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor, Map<String, Object> meta) {
361+
return withProvidedContext(this.delegate.listResourceTemplates(cursor, meta)).block();
362+
363+
}
364+
327365
/**
328366
* Subscriptions. The protocol supports optional subscriptions to resource changes.
329367
* Clients can subscribe to specific resources and receive notifications when they
@@ -370,6 +408,17 @@ public ListPromptsResult listPrompts(String cursor) {
370408

371409
}
372410

411+
/**
412+
* Retrieves a paginated list of prompts provided by the server.
413+
* @param cursor Optional pagination cursor from a previous list request
414+
* @param meta Optional metadata to include in the request (_meta field)
415+
* @return The list of prompts result.
416+
*/
417+
public ListPromptsResult listPrompts(String cursor, Map<String, Object> meta) {
418+
return withProvidedContext(this.delegate.listPrompts(cursor, meta)).block();
419+
420+
}
421+
373422
public GetPromptResult getPrompt(GetPromptRequest getPromptRequest) {
374423
return withProvidedContext(this.delegate.getPrompt(getPromptRequest)).block();
375424
}

mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ private Mono<HttpResponse<String>> sendHttpPost(final String endpoint, final Str
445445
return Mono.deferContextual(ctx -> {
446446
var builder = this.requestBuilder.copy()
447447
.uri(requestUri)
448-
.header(HttpHeaders.CONTENT_TYPE, "application/json")
448+
.header(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8")
449449
.header(MCP_PROTOCOL_VERSION_HEADER_NAME, MCP_PROTOCOL_VERSION)
450450
.POST(HttpRequest.BodyPublishers.ofString(body));
451451
var transportContext = ctx.getOrDefault(McpTransportContext.KEY, McpTransportContext.EMPTY);

mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
102102

103103
private static final String APPLICATION_JSON = "application/json";
104104

105+
private static final String APPLICATION_JSON_UTF8 = "application/json; charset=utf-8";
106+
105107
private static final String TEXT_EVENT_STREAM = "text/event-stream";
106108

107109
public static int NOT_FOUND = 404;
@@ -477,7 +479,7 @@ public Mono<Void> sendMessage(McpSchema.JSONRPCMessage sentMessage) {
477479

478480
var builder = requestBuilder.uri(uri)
479481
.header(HttpHeaders.ACCEPT, APPLICATION_JSON + ", " + TEXT_EVENT_STREAM)
480-
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
482+
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_UTF8)
481483
.header(HttpHeaders.CACHE_CONTROL, "no-cache")
482484
.header(HttpHeaders.PROTOCOL_VERSION,
483485
ctx.getOrDefault(McpAsyncClient.NEGOTIATED_PROTOCOL_VERSION,

mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import io.modelcontextprotocol.util.Assert;
3939
import io.modelcontextprotocol.util.DefaultMcpUriTemplateManagerFactory;
4040
import io.modelcontextprotocol.util.McpUriTemplateManagerFactory;
41+
import io.modelcontextprotocol.util.ToolInputValidator;
4142
import io.modelcontextprotocol.util.Utils;
4243
import org.slf4j.Logger;
4344
import org.slf4j.LoggerFactory;
@@ -98,6 +99,8 @@ public class McpAsyncServer {
9899

99100
private final JsonSchemaValidator jsonSchemaValidator;
100101

102+
private final boolean validateToolInputs;
103+
101104
private final McpSchema.ServerCapabilities serverCapabilities;
102105

103106
private final McpSchema.Implementation serverInfo;
@@ -129,7 +132,8 @@ public class McpAsyncServer {
129132
*/
130133
McpAsyncServer(McpServerTransportProvider mcpTransportProvider, McpJsonMapper jsonMapper,
131134
McpServerFeatures.Async features, Duration requestTimeout,
132-
McpUriTemplateManagerFactory uriTemplateManagerFactory, JsonSchemaValidator jsonSchemaValidator) {
135+
McpUriTemplateManagerFactory uriTemplateManagerFactory, JsonSchemaValidator jsonSchemaValidator,
136+
boolean validateToolInputs) {
133137
this.mcpTransportProvider = mcpTransportProvider;
134138
this.jsonMapper = jsonMapper;
135139
this.serverInfo = features.serverInfo();
@@ -142,6 +146,7 @@ public class McpAsyncServer {
142146
this.completions.putAll(features.completions());
143147
this.uriTemplateManagerFactory = uriTemplateManagerFactory;
144148
this.jsonSchemaValidator = jsonSchemaValidator;
149+
this.validateToolInputs = validateToolInputs;
145150

146151
Map<String, McpRequestHandler<?>> requestHandlers = prepareRequestHandlers();
147152
Map<String, McpNotificationHandler> notificationHandlers = prepareNotificationHandlers(features);
@@ -157,7 +162,8 @@ public class McpAsyncServer {
157162

158163
McpAsyncServer(McpStreamableServerTransportProvider mcpTransportProvider, McpJsonMapper jsonMapper,
159164
McpServerFeatures.Async features, Duration requestTimeout,
160-
McpUriTemplateManagerFactory uriTemplateManagerFactory, JsonSchemaValidator jsonSchemaValidator) {
165+
McpUriTemplateManagerFactory uriTemplateManagerFactory, JsonSchemaValidator jsonSchemaValidator,
166+
boolean validateToolInputs) {
161167
this.mcpTransportProvider = mcpTransportProvider;
162168
this.jsonMapper = jsonMapper;
163169
this.serverInfo = features.serverInfo();
@@ -170,6 +176,7 @@ public class McpAsyncServer {
170176
this.completions.putAll(features.completions());
171177
this.uriTemplateManagerFactory = uriTemplateManagerFactory;
172178
this.jsonSchemaValidator = jsonSchemaValidator;
179+
this.validateToolInputs = validateToolInputs;
173180

174181
Map<String, McpRequestHandler<?>> requestHandlers = prepareRequestHandlers();
175182
Map<String, McpNotificationHandler> notificationHandlers = prepareNotificationHandlers(features);
@@ -543,6 +550,13 @@ private McpRequestHandler<CallToolResult> toolsCallRequestHandler() {
543550
.build());
544551
}
545552

553+
McpSchema.Tool tool = toolSpecification.get().tool();
554+
CallToolResult validationError = ToolInputValidator.validate(tool, callToolRequest.arguments(),
555+
this.validateToolInputs, this.jsonSchemaValidator);
556+
if (validationError != null) {
557+
return Mono.just(validationError);
558+
}
559+
546560
return toolSpecification.get().callHandler().apply(exchange, callToolRequest);
547561
};
548562
}

0 commit comments

Comments
 (0)