Remove deprecated APIs and modules for 1.0.0 release (#807)
- Remove mcp-json-jackson module (JacksonMcpJsonMapper, DefaultJsonSchemaValidator) - Remove deprecated session interfaces (McpServerSession, McpClientSession methods) - Remove deprecated server methods (loggingNotification, McpAsyncServerExchange constructor) - Remove deprecated schema definitions and constructors (McpSchema, McpError, tool/content classes) - Replace generic McpError with appropriate exception types (IllegalStateException, IllegalArgumentException) - Update all tests to use current APIs --------- Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
This commit is contained in:
parent
77bc64abb8
commit
4c1c3d8d5c
39 changed files with 261 additions and 1940 deletions
|
|
@ -185,19 +185,6 @@ public class HttpClientSseClientTransport implements McpClientTransport {
|
||||||
// Default constructor
|
// Default constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new builder with the specified base URI.
|
|
||||||
* @param baseUri the base URI of the MCP server
|
|
||||||
* @deprecated Use {@link HttpClientSseClientTransport#builder(String)} instead.
|
|
||||||
* This constructor is deprecated and will be removed or made {@code protected} or
|
|
||||||
* {@code private} in a future release.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public Builder(String baseUri) {
|
|
||||||
Assert.hasText(baseUri, "baseUri must not be empty");
|
|
||||||
this.baseUri = baseUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the base URI.
|
* Sets the base URI.
|
||||||
* @param baseUri the base URI
|
* @param baseUri the base URI
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ class DefaultMcpStatelessServerHandler implements McpStatelessServerHandler {
|
||||||
McpSchema.JSONRPCRequest request) {
|
McpSchema.JSONRPCRequest request) {
|
||||||
McpStatelessRequestHandler<?> requestHandler = this.requestHandlers.get(request.method());
|
McpStatelessRequestHandler<?> requestHandler = this.requestHandlers.get(request.method());
|
||||||
if (requestHandler == null) {
|
if (requestHandler == null) {
|
||||||
return Mono.error(new McpError("Missing handler for request type: " + request.method()));
|
return Mono.error(McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND)
|
||||||
|
.message("Missing handler for request type: " + request.method())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
return requestHandler.handle(transportContext, request.params())
|
return requestHandler.handle(transportContext, request.params())
|
||||||
.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null))
|
.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null))
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ public class McpAsyncServer {
|
||||||
if (toolSpecification.tool() == null) {
|
if (toolSpecification.tool() == null) {
|
||||||
return Mono.error(new IllegalArgumentException("Tool must not be null"));
|
return Mono.error(new IllegalArgumentException("Tool must not be null"));
|
||||||
}
|
}
|
||||||
if (toolSpecification.call() == null && toolSpecification.callHandler() == null) {
|
if (toolSpecification.callHandler() == null) {
|
||||||
return Mono.error(new IllegalArgumentException("Tool call handler must not be null"));
|
return Mono.error(new IllegalArgumentException("Tool call handler must not be null"));
|
||||||
}
|
}
|
||||||
if (this.serverCapabilities.tools() == null) {
|
if (this.serverCapabilities.tools() == null) {
|
||||||
|
|
@ -869,32 +869,6 @@ public class McpAsyncServer {
|
||||||
// Logging Management
|
// Logging Management
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation would, incorrectly, broadcast the logging message to all
|
|
||||||
* connected clients, using a single minLoggingLevel for all of them. Similar to the
|
|
||||||
* sampling and roots, the logging level should be set per client session and use the
|
|
||||||
* ServerExchange to send the logging message to the right client.
|
|
||||||
* @param loggingMessageNotification The logging message to send
|
|
||||||
* @return A Mono that completes when the notification has been sent
|
|
||||||
* @deprecated Use
|
|
||||||
* {@link McpAsyncServerExchange#loggingNotification(LoggingMessageNotification)}
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Mono<Void> loggingNotification(LoggingMessageNotification loggingMessageNotification) {
|
|
||||||
|
|
||||||
if (loggingMessageNotification == null) {
|
|
||||||
return Mono.error(new McpError("Logging message must not be null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loggingMessageNotification.level().level() < minLoggingLevel.level()) {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.mcpTransportProvider.notifyClients(McpSchema.METHOD_NOTIFICATION_MESSAGE,
|
|
||||||
loggingMessageNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
private McpRequestHandler<Object> setLoggerRequestHandler() {
|
private McpRequestHandler<Object> setLoggerRequestHandler() {
|
||||||
return (exchange, params) -> {
|
return (exchange, params) -> {
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
|
|
|
||||||
|
|
@ -49,28 +49,6 @@ public class McpAsyncServerExchange {
|
||||||
public static final TypeRef<Object> OBJECT_TYPE_REF = new TypeRef<>() {
|
public static final TypeRef<Object> OBJECT_TYPE_REF = new TypeRef<>() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new asynchronous exchange with the client.
|
|
||||||
* @param session The server session representing a 1-1 interaction.
|
|
||||||
* @param clientCapabilities The client capabilities that define the supported
|
|
||||||
* features and functionality.
|
|
||||||
* @param clientInfo The client implementation information.
|
|
||||||
* @deprecated Use
|
|
||||||
* {@link #McpAsyncServerExchange(String, McpLoggableSession, McpSchema.ClientCapabilities, McpSchema.Implementation, McpTransportContext)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public McpAsyncServerExchange(McpSession session, McpSchema.ClientCapabilities clientCapabilities,
|
|
||||||
McpSchema.Implementation clientInfo) {
|
|
||||||
this.sessionId = null;
|
|
||||||
if (!(session instanceof McpLoggableSession)) {
|
|
||||||
throw new IllegalArgumentException("Expecting session to be a McpLoggableSession instance");
|
|
||||||
}
|
|
||||||
this.session = (McpLoggableSession) session;
|
|
||||||
this.clientCapabilities = clientCapabilities;
|
|
||||||
this.clientInfo = clientInfo;
|
|
||||||
this.transportContext = McpTransportContext.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new asynchronous exchange with the client.
|
* Create a new asynchronous exchange with the client.
|
||||||
* @param session The server session representing a 1-1 interaction.
|
* @param session The server session representing a 1-1 interaction.
|
||||||
|
|
@ -142,10 +120,11 @@ public class McpAsyncServerExchange {
|
||||||
*/
|
*/
|
||||||
public Mono<McpSchema.CreateMessageResult> createMessage(McpSchema.CreateMessageRequest createMessageRequest) {
|
public Mono<McpSchema.CreateMessageResult> createMessage(McpSchema.CreateMessageRequest createMessageRequest) {
|
||||||
if (this.clientCapabilities == null) {
|
if (this.clientCapabilities == null) {
|
||||||
return Mono.error(new McpError("Client must be initialized. Call the initialize method first!"));
|
return Mono
|
||||||
|
.error(new IllegalStateException("Client must be initialized. Call the initialize method first!"));
|
||||||
}
|
}
|
||||||
if (this.clientCapabilities.sampling() == null) {
|
if (this.clientCapabilities.sampling() == null) {
|
||||||
return Mono.error(new McpError("Client must be configured with sampling capabilities"));
|
return Mono.error(new IllegalStateException("Client must be configured with sampling capabilities"));
|
||||||
}
|
}
|
||||||
return this.session.sendRequest(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, createMessageRequest,
|
return this.session.sendRequest(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, createMessageRequest,
|
||||||
CREATE_MESSAGE_RESULT_TYPE_REF);
|
CREATE_MESSAGE_RESULT_TYPE_REF);
|
||||||
|
|
@ -167,10 +146,11 @@ public class McpAsyncServerExchange {
|
||||||
*/
|
*/
|
||||||
public Mono<McpSchema.ElicitResult> createElicitation(McpSchema.ElicitRequest elicitRequest) {
|
public Mono<McpSchema.ElicitResult> createElicitation(McpSchema.ElicitRequest elicitRequest) {
|
||||||
if (this.clientCapabilities == null) {
|
if (this.clientCapabilities == null) {
|
||||||
return Mono.error(new McpError("Client must be initialized. Call the initialize method first!"));
|
return Mono
|
||||||
|
.error(new IllegalStateException("Client must be initialized. Call the initialize method first!"));
|
||||||
}
|
}
|
||||||
if (this.clientCapabilities.elicitation() == null) {
|
if (this.clientCapabilities.elicitation() == null) {
|
||||||
return Mono.error(new McpError("Client must be configured with elicitation capabilities"));
|
return Mono.error(new IllegalStateException("Client must be configured with elicitation capabilities"));
|
||||||
}
|
}
|
||||||
return this.session.sendRequest(McpSchema.METHOD_ELICITATION_CREATE, elicitRequest,
|
return this.session.sendRequest(McpSchema.METHOD_ELICITATION_CREATE, elicitRequest,
|
||||||
ELICITATION_RESULT_TYPE_REF);
|
ELICITATION_RESULT_TYPE_REF);
|
||||||
|
|
@ -215,7 +195,7 @@ public class McpAsyncServerExchange {
|
||||||
public Mono<Void> loggingNotification(LoggingMessageNotification loggingMessageNotification) {
|
public Mono<Void> loggingNotification(LoggingMessageNotification loggingMessageNotification) {
|
||||||
|
|
||||||
if (loggingMessageNotification == null) {
|
if (loggingMessageNotification == null) {
|
||||||
return Mono.error(new McpError("Logging message must not be null"));
|
return Mono.error(new IllegalStateException("Logging message must not be null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
|
|
@ -234,7 +214,7 @@ public class McpAsyncServerExchange {
|
||||||
*/
|
*/
|
||||||
public Mono<Void> progressNotification(McpSchema.ProgressNotification progressNotification) {
|
public Mono<Void> progressNotification(McpSchema.ProgressNotification progressNotification) {
|
||||||
if (progressNotification == null) {
|
if (progressNotification == null) {
|
||||||
return Mono.error(new McpError("Progress notification must not be null"));
|
return Mono.error(new IllegalStateException("Progress notification must not be null"));
|
||||||
}
|
}
|
||||||
return this.session.sendNotification(McpSchema.METHOD_NOTIFICATION_PROGRESS, progressNotification);
|
return this.session.sendNotification(McpSchema.METHOD_NOTIFICATION_PROGRESS, progressNotification);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,9 @@ import reactor.core.publisher.Mono;
|
||||||
* Example of creating a basic synchronous server: <pre>{@code
|
* Example of creating a basic synchronous server: <pre>{@code
|
||||||
* McpServer.sync(transportProvider)
|
* McpServer.sync(transportProvider)
|
||||||
* .serverInfo("my-server", "1.0.0")
|
* .serverInfo("my-server", "1.0.0")
|
||||||
* .tool(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
* .toolCall(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
||||||
* (exchange, args) -> CallToolResult.builder()
|
* (exchange, request) -> CallToolResult.builder()
|
||||||
* .content(List.of(new McpSchema.TextContent("Result: " + calculate(args))))
|
* .content(List.of(new McpSchema.TextContent("Result: " + calculate(request.arguments()))))
|
||||||
* .isError(false)
|
* .isError(false)
|
||||||
* .build())
|
* .build())
|
||||||
* .build();
|
* .build();
|
||||||
|
|
@ -77,8 +77,8 @@ import reactor.core.publisher.Mono;
|
||||||
* Example of creating a basic asynchronous server: <pre>{@code
|
* Example of creating a basic asynchronous server: <pre>{@code
|
||||||
* McpServer.async(transportProvider)
|
* McpServer.async(transportProvider)
|
||||||
* .serverInfo("my-server", "1.0.0")
|
* .serverInfo("my-server", "1.0.0")
|
||||||
* .tool(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
* .toolCall(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
||||||
* (exchange, args) -> Mono.fromSupplier(() -> calculate(args))
|
* (exchange, request) -> Mono.fromSupplier(() -> calculate(request.arguments()))
|
||||||
* .map(result -> CallToolResult.builder()
|
* .map(result -> CallToolResult.builder()
|
||||||
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
|
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
|
||||||
* .isError(false)
|
* .isError(false)
|
||||||
|
|
@ -441,46 +441,6 @@ public interface McpServer {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a single tool with its implementation handler to the server. This is a
|
|
||||||
* convenience method for registering individual tools without creating a
|
|
||||||
* {@link McpServerFeatures.AsyncToolSpecification} explicitly.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Example usage: <pre>{@code
|
|
||||||
* .tool(
|
|
||||||
* Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
|
||||||
* (exchange, args) -> Mono.fromSupplier(() -> calculate(args))
|
|
||||||
* .map(result -> CallToolResult.builder()
|
|
||||||
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
|
|
||||||
* .isError(false)
|
|
||||||
* .build()))
|
|
||||||
* )
|
|
||||||
* }</pre>
|
|
||||||
* @param tool The tool definition including name, description, and schema. Must
|
|
||||||
* not be null.
|
|
||||||
* @param handler 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
|
|
||||||
* map of arguments passed to the tool.
|
|
||||||
* @return This builder instance for method chaining
|
|
||||||
* @throws IllegalArgumentException if tool or handler is null
|
|
||||||
* @deprecated Use {@link #toolCall(McpSchema.Tool, BiFunction)} instead for tool
|
|
||||||
* calls that require a request object.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public AsyncSpecification<S> tool(McpSchema.Tool tool,
|
|
||||||
BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<CallToolResult>> handler) {
|
|
||||||
Assert.notNull(tool, "Tool must not be null");
|
|
||||||
Assert.notNull(handler, "Handler must not be null");
|
|
||||||
validateToolName(tool.name());
|
|
||||||
assertNoDuplicateTool(tool.name());
|
|
||||||
|
|
||||||
this.tools.add(new McpServerFeatures.AsyncToolSpecification(tool, handler));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a single tool with its implementation handler to the server. This is a
|
* Adds a single tool with its implementation handler to the server. This is a
|
||||||
* convenience method for registering individual tools without creating a
|
* convenience method for registering individual tools without creating a
|
||||||
|
|
@ -1064,45 +1024,6 @@ public interface McpServer {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a single tool with its implementation handler to the server. This is a
|
|
||||||
* convenience method for registering individual tools without creating a
|
|
||||||
* {@link McpServerFeatures.SyncToolSpecification} explicitly.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Example usage: <pre>{@code
|
|
||||||
* .tool(
|
|
||||||
* Tool.builder().name("calculator").title("Performs calculations".inputSchema(schema).build(),
|
|
||||||
* (exchange, args) -> CallToolResult.builder()
|
|
||||||
* .content(List.of(new McpSchema.TextContent("Result: " + calculate(args))))
|
|
||||||
* .isError(false)
|
|
||||||
* .build())
|
|
||||||
* )
|
|
||||||
* }</pre>
|
|
||||||
* @param tool The tool definition including name, description, and schema. Must
|
|
||||||
* not be null.
|
|
||||||
* @param handler 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
|
|
||||||
* list of arguments passed to the tool.
|
|
||||||
* @return This builder instance for method chaining
|
|
||||||
* @throws IllegalArgumentException if tool or handler is null
|
|
||||||
* @deprecated Use {@link #toolCall(McpSchema.Tool, BiFunction)} instead for tool
|
|
||||||
* calls that require a request object.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public SyncSpecification<S> tool(McpSchema.Tool tool,
|
|
||||||
BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> handler) {
|
|
||||||
Assert.notNull(tool, "Tool must not be null");
|
|
||||||
Assert.notNull(handler, "Handler must not be null");
|
|
||||||
validateToolName(tool.name());
|
|
||||||
assertNoDuplicateTool(tool.name());
|
|
||||||
|
|
||||||
this.tools.add(new McpServerFeatures.SyncToolSpecification(tool, handler));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a single tool with its implementation handler to the server. This is a
|
* Adds a single tool with its implementation handler to the server. This is a
|
||||||
* convenience method for registering individual tools without creating a
|
* convenience method for registering individual tools without creating a
|
||||||
|
|
@ -1123,7 +1044,7 @@ public interface McpServer {
|
||||||
validateToolName(tool.name());
|
validateToolName(tool.name());
|
||||||
assertNoDuplicateTool(tool.name());
|
assertNoDuplicateTool(tool.name());
|
||||||
|
|
||||||
this.tools.add(new McpServerFeatures.SyncToolSpecification(tool, null, handler));
|
this.tools.add(new McpServerFeatures.SyncToolSpecification(tool, handler));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -223,19 +223,8 @@ public class McpServerFeatures {
|
||||||
* map of tool arguments.
|
* map of tool arguments.
|
||||||
*/
|
*/
|
||||||
public record AsyncToolSpecification(McpSchema.Tool tool,
|
public record AsyncToolSpecification(McpSchema.Tool tool,
|
||||||
@Deprecated BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> call,
|
|
||||||
BiFunction<McpAsyncServerExchange, McpSchema.CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler) {
|
BiFunction<McpAsyncServerExchange, McpSchema.CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler) {
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link AsyncToolSpecification(McpSchema.Tool, null,
|
|
||||||
* BiFunction)} instead.
|
|
||||||
**/
|
|
||||||
@Deprecated
|
|
||||||
public AsyncToolSpecification(McpSchema.Tool tool,
|
|
||||||
BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> call) {
|
|
||||||
this(tool, call, (exchange, toolReq) -> call.apply(exchange, toolReq.arguments()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec) {
|
static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec) {
|
||||||
return fromSync(syncToolSpec, false);
|
return fromSync(syncToolSpec, false);
|
||||||
}
|
}
|
||||||
|
|
@ -247,13 +236,6 @@ public class McpServerFeatures {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> deprecatedCall = (syncToolSpec
|
|
||||||
.call() != null) ? (exchange, map) -> {
|
|
||||||
var toolResult = Mono
|
|
||||||
.fromCallable(() -> syncToolSpec.call().apply(new McpSyncServerExchange(exchange), map));
|
|
||||||
return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic());
|
|
||||||
} : null;
|
|
||||||
|
|
||||||
BiFunction<McpAsyncServerExchange, McpSchema.CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler = (
|
BiFunction<McpAsyncServerExchange, McpSchema.CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler = (
|
||||||
exchange, req) -> {
|
exchange, req) -> {
|
||||||
var toolResult = Mono
|
var toolResult = Mono
|
||||||
|
|
@ -261,7 +243,7 @@ public class McpServerFeatures {
|
||||||
return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic());
|
return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic());
|
||||||
};
|
};
|
||||||
|
|
||||||
return new AsyncToolSpecification(syncToolSpec.tool(), deprecatedCall, callHandler);
|
return new AsyncToolSpecification(syncToolSpec.tool(), callHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -304,7 +286,7 @@ public class McpServerFeatures {
|
||||||
Assert.notNull(tool, "Tool must not be null");
|
Assert.notNull(tool, "Tool must not be null");
|
||||||
Assert.notNull(callHandler, "Call handler function must not be null");
|
Assert.notNull(callHandler, "Call handler function must not be null");
|
||||||
|
|
||||||
return new AsyncToolSpecification(tool, null, callHandler);
|
return new AsyncToolSpecification(tool, callHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -523,26 +505,16 @@ public class McpServerFeatures {
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* @param tool The tool definition including name, description, and parameter schema
|
* @param tool The tool definition including name, description, and parameter schema
|
||||||
* @param call (Deprected) The function that implements the tool's logic, receiving
|
|
||||||
* arguments and returning results. The function's first argument is an
|
|
||||||
* {@link McpSyncServerExchange} upon which the server can interact with the connected
|
|
||||||
* @param callHandler The function that implements the tool's logic, receiving a
|
* @param callHandler The function that implements the tool's logic, receiving a
|
||||||
* {@link McpSyncServerExchange} and a
|
* {@link McpSyncServerExchange} and a
|
||||||
* {@link io.modelcontextprotocol.spec.McpSchema.CallToolRequest} and returning
|
* {@link io.modelcontextprotocol.spec.McpSchema.CallToolRequest} and returning
|
||||||
* results. The function's first argument is an {@link McpSyncServerExchange} upon
|
* results. The function's first argument is an {@link McpSyncServerExchange} upon
|
||||||
* which the server can interact with the client. The second arguments is a map of
|
* which the server can interact with the client. The second argument is a request
|
||||||
* arguments passed to the tool.
|
* object containing the arguments passed to the tool.
|
||||||
*/
|
*/
|
||||||
public record SyncToolSpecification(McpSchema.Tool tool,
|
public record SyncToolSpecification(McpSchema.Tool tool,
|
||||||
@Deprecated BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> call,
|
|
||||||
BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler) {
|
BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler) {
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public SyncToolSpecification(McpSchema.Tool tool,
|
|
||||||
BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> call) {
|
|
||||||
this(tool, call, (exchange, toolReq) -> call.apply(exchange, toolReq.arguments()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder for creating SyncToolSpecification instances.
|
* Builder for creating SyncToolSpecification instances.
|
||||||
*/
|
*/
|
||||||
|
|
@ -583,7 +555,7 @@ public class McpServerFeatures {
|
||||||
Assert.notNull(tool, "Tool must not be null");
|
Assert.notNull(tool, "Tool must not be null");
|
||||||
Assert.notNull(callHandler, "CallTool function must not be null");
|
Assert.notNull(callHandler, "CallTool function must not be null");
|
||||||
|
|
||||||
return new SyncToolSpecification(tool, null, callHandler);
|
return new SyncToolSpecification(tool, callHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -230,21 +230,6 @@ public class McpSyncServer {
|
||||||
this.asyncServer.notifyPromptsListChanged().block();
|
this.asyncServer.notifyPromptsListChanged().block();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation would, incorrectly, broadcast the logging message to all
|
|
||||||
* connected clients, using a single minLoggingLevel for all of them. Similar to the
|
|
||||||
* sampling and roots, the logging level should be set per client session and use the
|
|
||||||
* ServerExchange to send the logging message to the right client.
|
|
||||||
* @param loggingMessageNotification The logging message to send
|
|
||||||
* @deprecated Use
|
|
||||||
* {@link McpSyncServerExchange#loggingNotification(LoggingMessageNotification)}
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void loggingNotification(LoggingMessageNotification loggingMessageNotification) {
|
|
||||||
this.asyncServer.loggingNotification(loggingMessageNotification).block();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the server gracefully.
|
* Close the server gracefully.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,9 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
|
||||||
response.setContentType(APPLICATION_JSON);
|
response.setContentType(APPLICATION_JSON);
|
||||||
response.setCharacterEncoding(UTF_8);
|
response.setCharacterEncoding(UTF_8);
|
||||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
String jsonError = jsonMapper.writeValueAsString(new McpError("Session ID missing in message endpoint"));
|
String jsonError = jsonMapper.writeValueAsString(McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND)
|
||||||
|
.message("Session ID missing in message endpoint")
|
||||||
|
.build());
|
||||||
PrintWriter writer = response.getWriter();
|
PrintWriter writer = response.getWriter();
|
||||||
writer.write(jsonError);
|
writer.write(jsonError);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
@ -356,7 +358,9 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
|
||||||
response.setContentType(APPLICATION_JSON);
|
response.setContentType(APPLICATION_JSON);
|
||||||
response.setCharacterEncoding(UTF_8);
|
response.setCharacterEncoding(UTF_8);
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
String jsonError = jsonMapper.writeValueAsString(new McpError("Session not found: " + sessionId));
|
String jsonError = jsonMapper.writeValueAsString(McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Session not found: " + sessionId)
|
||||||
|
.build());
|
||||||
PrintWriter writer = response.getWriter();
|
PrintWriter writer = response.getWriter();
|
||||||
writer.write(jsonError);
|
writer.write(jsonError);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
@ -383,7 +387,9 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("Error processing message: {}", e.getMessage());
|
logger.error("Error processing message: {}", e.getMessage());
|
||||||
try {
|
try {
|
||||||
McpError mcpError = new McpError(e.getMessage());
|
McpError mcpError = McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message(e.getMessage())
|
||||||
|
.build();
|
||||||
response.setContentType(APPLICATION_JSON);
|
response.setContentType(APPLICATION_JSON);
|
||||||
response.setCharacterEncoding(UTF_8);
|
response.setCharacterEncoding(UTF_8);
|
||||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,9 @@ public class HttpServletStatelessServerTransport extends HttpServlet implements
|
||||||
String accept = request.getHeader(ACCEPT);
|
String accept = request.getHeader(ACCEPT);
|
||||||
if (accept == null || !(accept.contains(APPLICATION_JSON) && accept.contains(TEXT_EVENT_STREAM))) {
|
if (accept == null || !(accept.contains(APPLICATION_JSON) && accept.contains(TEXT_EVENT_STREAM))) {
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
new McpError("Both application/json and text/event-stream required in Accept header"));
|
McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND)
|
||||||
|
.message("Both application/json and text/event-stream required in Accept header")
|
||||||
|
.build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +182,9 @@ public class HttpServletStatelessServerTransport extends HttpServlet implements
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("Failed to handle request: {}", e.getMessage());
|
logger.error("Failed to handle request: {}", e.getMessage());
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError("Failed to handle request: " + e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Failed to handle request: " + e.getMessage())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (message instanceof McpSchema.JSONRPCNotification jsonrpcNotification) {
|
else if (message instanceof McpSchema.JSONRPCNotification jsonrpcNotification) {
|
||||||
|
|
@ -193,22 +197,29 @@ public class HttpServletStatelessServerTransport extends HttpServlet implements
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("Failed to handle notification: {}", e.getMessage());
|
logger.error("Failed to handle notification: {}", e.getMessage());
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError("Failed to handle notification: " + e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Failed to handle notification: " + e.getMessage())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
new McpError("The server accepts either requests or notifications"));
|
McpError.builder(McpSchema.ErrorCodes.INVALID_REQUEST)
|
||||||
|
.message("The server accepts either requests or notifications")
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException | IOException e) {
|
catch (IllegalArgumentException | IOException e) {
|
||||||
logger.error("Failed to deserialize message: {}", e.getMessage());
|
logger.error("Failed to deserialize message: {}", e.getMessage());
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST, new McpError("Invalid message format"));
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
|
McpError.builder(McpSchema.ErrorCodes.INVALID_REQUEST).message("Invalid message format").build());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("Unexpected error handling message: {}", e.getMessage());
|
logger.error("Unexpected error handling message: {}", e.getMessage());
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError("Unexpected error: " + e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Unexpected error: " + e.getMessage())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
|
|
||||||
if (!badRequestErrors.isEmpty()) {
|
if (!badRequestErrors.isEmpty()) {
|
||||||
String combinedMessage = String.join("; ", badRequestErrors);
|
String combinedMessage = String.join("; ", badRequestErrors);
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST, new McpError(combinedMessage));
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
|
McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND).message(combinedMessage).build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,7 +431,8 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
&& jsonrpcRequest.method().equals(McpSchema.METHOD_INITIALIZE)) {
|
&& jsonrpcRequest.method().equals(McpSchema.METHOD_INITIALIZE)) {
|
||||||
if (!badRequestErrors.isEmpty()) {
|
if (!badRequestErrors.isEmpty()) {
|
||||||
String combinedMessage = String.join("; ", badRequestErrors);
|
String combinedMessage = String.join("; ", badRequestErrors);
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST, new McpError(combinedMessage));
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
|
McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND).message(combinedMessage).build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,7 +462,9 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("Failed to initialize session: {}", e.getMessage());
|
logger.error("Failed to initialize session: {}", e.getMessage());
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError("Failed to initialize session: " + e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Failed to initialize session: " + e.getMessage())
|
||||||
|
.build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -473,7 +477,8 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
|
|
||||||
if (!badRequestErrors.isEmpty()) {
|
if (!badRequestErrors.isEmpty()) {
|
||||||
String combinedMessage = String.join("; ", badRequestErrors);
|
String combinedMessage = String.join("; ", badRequestErrors);
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST, new McpError(combinedMessage));
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
|
McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND).message(combinedMessage).build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,7 +486,9 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
|
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
this.responseError(response, HttpServletResponse.SC_NOT_FOUND,
|
this.responseError(response, HttpServletResponse.SC_NOT_FOUND,
|
||||||
new McpError("Session not found: " + sessionId));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Session not found: " + sessionId)
|
||||||
|
.build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -523,19 +530,23 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError("Unknown message type"));
|
McpError.builder(McpSchema.ErrorCodes.INVALID_REQUEST).message("Unknown message type").build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException | IOException e) {
|
catch (IllegalArgumentException | IOException e) {
|
||||||
logger.error("Failed to deserialize message: {}", e.getMessage());
|
logger.error("Failed to deserialize message: {}", e.getMessage());
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
new McpError("Invalid message format: " + e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INVALID_REQUEST)
|
||||||
|
.message("Invalid message format: " + e.getMessage())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("Error handling message: {}", e.getMessage());
|
logger.error("Error handling message: {}", e.getMessage());
|
||||||
try {
|
try {
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError("Error processing message: " + e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
|
||||||
|
.message("Error processing message: " + e.getMessage())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
logger.error(FAILED_TO_SEND_ERROR_RESPONSE, ex.getMessage());
|
logger.error(FAILED_TO_SEND_ERROR_RESPONSE, ex.getMessage());
|
||||||
|
|
@ -584,7 +595,9 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
|
|
||||||
if (request.getHeader(HttpHeaders.MCP_SESSION_ID) == null) {
|
if (request.getHeader(HttpHeaders.MCP_SESSION_ID) == null) {
|
||||||
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
this.responseError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||||
new McpError("Session ID required in mcp-session-id header"));
|
McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND)
|
||||||
|
.message("Session ID required in mcp-session-id header")
|
||||||
|
.build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -605,7 +618,7 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
||||||
logger.error("Failed to delete session {}: {}", sessionId, e.getMessage());
|
logger.error("Failed to delete session {}: {}", sessionId, e.getMessage());
|
||||||
try {
|
try {
|
||||||
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
this.responseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
new McpError(e.getMessage()));
|
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR).message(e.getMessage()).build());
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
logger.error(FAILED_TO_SEND_ERROR_RESPONSE, ex.getMessage());
|
logger.error(FAILED_TO_SEND_ERROR_RESPONSE, ex.getMessage());
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ public class StdioServerTransportProvider implements McpServerTransportProvider
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> notifyClients(String method, Object params) {
|
public Mono<Void> notifyClients(String method, Object params) {
|
||||||
if (this.session == null) {
|
if (this.session == null) {
|
||||||
return Mono.error(new McpError("No session to close"));
|
return Mono.error(new IllegalStateException("No session to close"));
|
||||||
}
|
}
|
||||||
return this.session.sendNotification(method, params)
|
return this.session.sendNotification(method, params)
|
||||||
.doOnError(e -> logger.error("Failed to send notification: {}", e.getMessage()));
|
.doOnError(e -> logger.error("Failed to send notification: {}", e.getMessage()));
|
||||||
|
|
|
||||||
|
|
@ -96,21 +96,6 @@ public class McpClientSession implements McpSession {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new McpClientSession with the specified configuration and handlers.
|
|
||||||
* @param requestTimeout Duration to wait for responses
|
|
||||||
* @param transport Transport implementation for message exchange
|
|
||||||
* @param requestHandlers Map of method names to request handlers
|
|
||||||
* @param notificationHandlers Map of method names to notification handlers
|
|
||||||
* @deprecated Use
|
|
||||||
* {@link #McpClientSession(Duration, McpClientTransport, Map, Map, Function)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public McpClientSession(Duration requestTimeout, McpClientTransport transport,
|
|
||||||
Map<String, RequestHandler<?>> requestHandlers, Map<String, NotificationHandler> notificationHandlers) {
|
|
||||||
this(requestTimeout, transport, requestHandlers, notificationHandlers, Function.identity());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new McpClientSession with the specified configuration and handlers.
|
* Creates a new McpClientSession with the specified configuration and handlers.
|
||||||
* @param requestTimeout Duration to wait for responses
|
* @param requestTimeout Duration to wait for responses
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,6 @@ public class McpError extends RuntimeException {
|
||||||
this.jsonRpcError = jsonRpcError;
|
this.jsonRpcError = jsonRpcError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public McpError(Object error) {
|
|
||||||
super(error.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSONRPCError getJsonRpcError() {
|
public JSONRPCError getJsonRpcError() {
|
||||||
return jsonRpcError;
|
return jsonRpcError;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,6 @@ public final class McpSchema {
|
||||||
private McpSchema() {
|
private McpSchema() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static final String LATEST_PROTOCOL_VERSION = ProtocolVersions.MCP_2025_11_25;
|
|
||||||
|
|
||||||
public static final String JSONRPC_VERSION = "2.0";
|
public static final String JSONRPC_VERSION = "2.0";
|
||||||
|
|
||||||
public static final String FIRST_PAGE = null;
|
public static final String FIRST_PAGE = null;
|
||||||
|
|
@ -798,35 +795,6 @@ public final class McpSchema {
|
||||||
@JsonProperty("annotations") Annotations annotations,
|
@JsonProperty("annotations") Annotations annotations,
|
||||||
@JsonProperty("_meta") Map<String, Object> meta) implements ResourceContent { // @formatter:on
|
@JsonProperty("_meta") Map<String, Object> meta) implements ResourceContent { // @formatter:on
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link Resource#builder()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Resource(String uri, String name, String title, String description, String mimeType, Long size,
|
|
||||||
Annotations annotations) {
|
|
||||||
this(uri, name, title, description, mimeType, size, annotations, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link Resource#builder()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Resource(String uri, String name, String description, String mimeType, Long size,
|
|
||||||
Annotations annotations) {
|
|
||||||
this(uri, name, null, description, mimeType, size, annotations, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link Resource#builder()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Resource(String uri, String name, String description, String mimeType, Annotations annotations) {
|
|
||||||
this(uri, name, null, description, mimeType, null, annotations, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
|
@ -1592,36 +1560,6 @@ public final class McpSchema {
|
||||||
@JsonProperty("structuredContent") Object structuredContent,
|
@JsonProperty("structuredContent") Object structuredContent,
|
||||||
@JsonProperty("_meta") Map<String, Object> meta) implements Result { // @formatter:on
|
@JsonProperty("_meta") Map<String, Object> meta) implements Result { // @formatter:on
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use the builder instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public CallToolResult(List<Content> content, Boolean isError) {
|
|
||||||
this(content, isError, (Object) null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use the builder instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public CallToolResult(List<Content> content, Boolean isError, Map<String, Object> structuredContent) {
|
|
||||||
this(content, isError, structuredContent, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of {@link CallToolResult} with a string containing the
|
|
||||||
* tool result.
|
|
||||||
* @param content The content of the tool result. This will be mapped to a
|
|
||||||
* one-sized list with a {@link TextContent} element.
|
|
||||||
* @param isError If true, indicates that the tool execution failed and the
|
|
||||||
* content contains error information. If false or absent, indicates successful
|
|
||||||
* execution.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public CallToolResult(String content, Boolean isError) {
|
|
||||||
this(List.of(new TextContent(content)), isError, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a builder for {@link CallToolResult}.
|
* Creates a builder for {@link CallToolResult}.
|
||||||
* @return a new builder instance
|
* @return a new builder instance
|
||||||
|
|
@ -2619,33 +2557,6 @@ public final class McpSchema {
|
||||||
public TextContent(String content) {
|
public TextContent(String content) {
|
||||||
this(null, content, null);
|
this(null, content, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link TextContent#TextContent(Annotations, String)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public TextContent(List<Role> audience, Double priority, String content) {
|
|
||||||
this(audience != null || priority != null ? new Annotations(audience, priority) : null, content, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link TextContent#annotations()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public List<Role> audience() {
|
|
||||||
return annotations == null ? null : annotations.audience();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link TextContent#annotations()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Double priority() {
|
|
||||||
return annotations == null ? null : annotations.priority();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2668,34 +2579,6 @@ public final class McpSchema {
|
||||||
public ImageContent(Annotations annotations, String data, String mimeType) {
|
public ImageContent(Annotations annotations, String data, String mimeType) {
|
||||||
this(annotations, data, mimeType, null);
|
this(annotations, data, mimeType, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link ImageContent#ImageContent(Annotations, String, String)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public ImageContent(List<Role> audience, Double priority, String data, String mimeType) {
|
|
||||||
this(audience != null || priority != null ? new Annotations(audience, priority) : null, data, mimeType,
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link ImageContent#annotations()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public List<Role> audience() {
|
|
||||||
return annotations == null ? null : annotations.audience();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link ImageContent#annotations()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Double priority() {
|
|
||||||
return annotations == null ? null : annotations.priority();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2742,34 +2625,6 @@ public final class McpSchema {
|
||||||
public EmbeddedResource(Annotations annotations, ResourceContents resource) {
|
public EmbeddedResource(Annotations annotations, ResourceContents resource) {
|
||||||
this(annotations, resource, null);
|
this(annotations, resource, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link EmbeddedResource#EmbeddedResource(Annotations, ResourceContents)}
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public EmbeddedResource(List<Role> audience, Double priority, ResourceContents resource) {
|
|
||||||
this(audience != null || priority != null ? new Annotations(audience, priority) : null, resource, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link EmbeddedResource#annotations()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public List<Role> audience() {
|
|
||||||
return annotations == null ? null : annotations.audience();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Only exists for backwards-compatibility purposes. Use
|
|
||||||
* {@link EmbeddedResource#annotations()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public Double priority() {
|
|
||||||
return annotations == null ? null : annotations.priority();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -86,34 +86,6 @@ public class McpServerSession implements McpLoggableSession {
|
||||||
this.notificationHandlers = notificationHandlers;
|
this.notificationHandlers = notificationHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new server session with the given parameters and the transport to use.
|
|
||||||
* @param id session id
|
|
||||||
* @param transport the transport to use
|
|
||||||
* @param initHandler called when a
|
|
||||||
* {@link io.modelcontextprotocol.spec.McpSchema.InitializeRequest} is received by the
|
|
||||||
* server
|
|
||||||
* @param initNotificationHandler called when a
|
|
||||||
* {@link io.modelcontextprotocol.spec.McpSchema#METHOD_NOTIFICATION_INITIALIZED} is
|
|
||||||
* received.
|
|
||||||
* @param requestHandlers map of request handlers to use
|
|
||||||
* @param notificationHandlers map of notification handlers to use
|
|
||||||
* @deprecated Use
|
|
||||||
* {@link #McpServerSession(String, Duration, McpServerTransport, McpInitRequestHandler, Map, Map)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public McpServerSession(String id, Duration requestTimeout, McpServerTransport transport,
|
|
||||||
McpInitRequestHandler initHandler, InitNotificationHandler initNotificationHandler,
|
|
||||||
Map<String, McpRequestHandler<?>> requestHandlers,
|
|
||||||
Map<String, McpNotificationHandler> notificationHandlers) {
|
|
||||||
this.id = id;
|
|
||||||
this.requestTimeout = requestTimeout;
|
|
||||||
this.transport = transport;
|
|
||||||
this.initRequestHandler = initHandler;
|
|
||||||
this.requestHandlers = requestHandlers;
|
|
||||||
this.notificationHandlers = notificationHandlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the session id.
|
* Retrieve the session id.
|
||||||
* @return session id
|
* @return session id
|
||||||
|
|
@ -355,23 +327,6 @@ public class McpServerSession implements McpLoggableSession {
|
||||||
this.transport.close();
|
this.transport.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Request handler for the initialization request.
|
|
||||||
*
|
|
||||||
* @deprecated Use {@link McpInitRequestHandler}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public interface InitRequestHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the initialization request.
|
|
||||||
* @param initializeRequest the initialization request by the client
|
|
||||||
* @return a Mono that will emit the result of the initialization
|
|
||||||
*/
|
|
||||||
Mono<McpSchema.InitializeResult> handle(McpSchema.InitializeRequest initializeRequest);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification handler for the initialization notification from the client.
|
* Notification handler for the initialization notification from the client.
|
||||||
*/
|
*/
|
||||||
|
|
@ -385,46 +340,6 @@ public class McpServerSession implements McpLoggableSession {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A handler for client-initiated notifications.
|
|
||||||
*
|
|
||||||
* @deprecated Use {@link McpNotificationHandler}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public interface NotificationHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a notification from the client.
|
|
||||||
* @param exchange the exchange associated with the client that allows calling
|
|
||||||
* back to the connected client or inspecting its capabilities.
|
|
||||||
* @param params the parameters of the notification.
|
|
||||||
* @return a Mono that completes once the notification is handled.
|
|
||||||
*/
|
|
||||||
Mono<Void> handle(McpAsyncServerExchange exchange, Object params);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A handler for client-initiated requests.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the response that is expected as a result of handling the
|
|
||||||
* request.
|
|
||||||
* @deprecated Use {@link McpRequestHandler}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public interface RequestHandler<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a request from the client.
|
|
||||||
* @param exchange the exchange associated with the client that allows calling
|
|
||||||
* back to the connected client or inspecting its capabilities.
|
|
||||||
* @param params the parameters of the request.
|
|
||||||
* @return a Mono that will emit the response to the request.
|
|
||||||
*/
|
|
||||||
Mono<T> handle(McpAsyncServerExchange exchange, Object params);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for creating server sessions which delegate to a provided 1:1 transport
|
* Factory for creating server sessions which delegate to a provided 1:1 transport
|
||||||
* with a connected client.
|
* with a connected client.
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ import java.util.List;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import io.modelcontextprotocol.json.McpJsonDefaults;
|
|
||||||
import io.modelcontextprotocol.json.TypeRef;
|
import io.modelcontextprotocol.json.TypeRef;
|
||||||
import io.modelcontextprotocol.spec.McpClientTransport;
|
import io.modelcontextprotocol.spec.McpClientTransport;
|
||||||
import io.modelcontextprotocol.spec.McpSchema;
|
import io.modelcontextprotocol.spec.McpSchema;
|
||||||
|
import io.modelcontextprotocol.spec.ProtocolVersions;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification;
|
import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest;
|
import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
@ -29,7 +29,7 @@ public class MockMcpClientTransport implements McpClientTransport {
|
||||||
|
|
||||||
private final BiConsumer<MockMcpClientTransport, McpSchema.JSONRPCMessage> interceptor;
|
private final BiConsumer<MockMcpClientTransport, McpSchema.JSONRPCMessage> interceptor;
|
||||||
|
|
||||||
private String protocolVersion = McpSchema.LATEST_PROTOCOL_VERSION;
|
private String protocolVersion = ProtocolVersions.MCP_2025_11_25;
|
||||||
|
|
||||||
public MockMcpClientTransport() {
|
public MockMcpClientTransport() {
|
||||||
this((t, msg) -> {
|
this((t, msg) -> {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
assertThat(specification).isNotNull();
|
assertThat(specification).isNotNull();
|
||||||
assertThat(specification.tool()).isEqualTo(tool);
|
assertThat(specification.tool()).isEqualTo(tool);
|
||||||
assertThat(specification.callHandler()).isNotNull();
|
assertThat(specification.callHandler()).isNotNull();
|
||||||
assertThat(specification.call()).isNull(); // deprecated field should be null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -107,12 +106,8 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
|
|
||||||
McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder()
|
McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(tool)
|
.tool(tool)
|
||||||
.callHandler((exchange, request) -> {
|
.callHandler((exchange, request) -> Mono.just(
|
||||||
return Mono.just(CallToolResult.builder()
|
CallToolResult.builder().content(List.of(new TextContent(expectedResult))).isError(false).build()))
|
||||||
.content(List.of(new TextContent(expectedResult)))
|
|
||||||
.isError(false)
|
|
||||||
.build());
|
|
||||||
})
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
CallToolRequest request = new CallToolRequest("calculator", Map.of());
|
CallToolRequest request = new CallToolRequest("calculator", Map.of());
|
||||||
|
|
@ -127,54 +122,6 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
void deprecatedConstructorShouldWorkCorrectly() {
|
|
||||||
Tool tool = McpSchema.Tool.builder()
|
|
||||||
.name("deprecated-tool")
|
|
||||||
.title("A deprecated tool")
|
|
||||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
|
||||||
.build();
|
|
||||||
String expectedResult = "deprecated result";
|
|
||||||
|
|
||||||
// Test the deprecated constructor that takes a 'call' function
|
|
||||||
McpServerFeatures.AsyncToolSpecification specification = new McpServerFeatures.AsyncToolSpecification(tool,
|
|
||||||
(exchange,
|
|
||||||
arguments) -> Mono.just(CallToolResult.builder()
|
|
||||||
.content(List.of(new TextContent(expectedResult)))
|
|
||||||
.isError(false)
|
|
||||||
.build()));
|
|
||||||
|
|
||||||
assertThat(specification).isNotNull();
|
|
||||||
assertThat(specification.tool()).isEqualTo(tool);
|
|
||||||
assertThat(specification.call()).isNotNull(); // deprecated field should be set
|
|
||||||
assertThat(specification.callHandler()).isNotNull(); // should be automatically
|
|
||||||
// created
|
|
||||||
|
|
||||||
// Test that the callTool function works (it should delegate to the call function)
|
|
||||||
CallToolRequest request = new CallToolRequest("deprecated-tool", Map.of("arg1", "value1"));
|
|
||||||
Mono<CallToolResult> resultMono = specification.callHandler().apply(null, request);
|
|
||||||
|
|
||||||
StepVerifier.create(resultMono).assertNext(result -> {
|
|
||||||
assertThat(result).isNotNull();
|
|
||||||
assertThat(result.content()).hasSize(1);
|
|
||||||
assertThat(result.content().get(0)).isInstanceOf(TextContent.class);
|
|
||||||
assertThat(((TextContent) result.content().get(0)).text()).isEqualTo(expectedResult);
|
|
||||||
assertThat(result.isError()).isFalse();
|
|
||||||
}).verifyComplete();
|
|
||||||
|
|
||||||
// Test that the deprecated call function also works directly
|
|
||||||
Mono<CallToolResult> callResultMono = specification.call().apply(null, request.arguments());
|
|
||||||
|
|
||||||
StepVerifier.create(callResultMono).assertNext(result -> {
|
|
||||||
assertThat(result).isNotNull();
|
|
||||||
assertThat(result.content()).hasSize(1);
|
|
||||||
assertThat(result.content().get(0)).isInstanceOf(TextContent.class);
|
|
||||||
assertThat(((TextContent) result.content().get(0)).text()).isEqualTo(expectedResult);
|
|
||||||
assertThat(result.isError()).isFalse();
|
|
||||||
}).verifyComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void fromSyncShouldConvertSyncToolSpecificationCorrectly() {
|
void fromSyncShouldConvertSyncToolSpecificationCorrectly() {
|
||||||
Tool tool = McpSchema.Tool.builder()
|
Tool tool = McpSchema.Tool.builder()
|
||||||
|
|
@ -200,8 +147,6 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
assertThat(asyncSpec).isNotNull();
|
assertThat(asyncSpec).isNotNull();
|
||||||
assertThat(asyncSpec.tool()).isEqualTo(tool);
|
assertThat(asyncSpec.tool()).isEqualTo(tool);
|
||||||
assertThat(asyncSpec.callHandler()).isNotNull();
|
assertThat(asyncSpec.callHandler()).isNotNull();
|
||||||
assertThat(asyncSpec.call()).isNull(); // should be null since sync spec doesn't
|
|
||||||
// have deprecated call
|
|
||||||
|
|
||||||
// Test that the converted async specification works correctly
|
// Test that the converted async specification works correctly
|
||||||
CallToolRequest request = new CallToolRequest("sync-tool", Map.of("param", "value"));
|
CallToolRequest request = new CallToolRequest("sync-tool", Map.of("param", "value"));
|
||||||
|
|
@ -216,59 +161,6 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
void fromSyncShouldConvertSyncToolSpecificationWithDeprecatedCallCorrectly() {
|
|
||||||
Tool tool = McpSchema.Tool.builder()
|
|
||||||
.name("sync-deprecated-tool")
|
|
||||||
.title("A sync tool with deprecated call")
|
|
||||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
|
||||||
.build();
|
|
||||||
String expectedResult = "sync deprecated result";
|
|
||||||
McpAsyncServerExchange nullExchange = null; // Mock or create a suitable exchange
|
|
||||||
// if needed
|
|
||||||
|
|
||||||
// Create a sync tool specification using the deprecated constructor
|
|
||||||
McpServerFeatures.SyncToolSpecification syncSpec = new McpServerFeatures.SyncToolSpecification(tool,
|
|
||||||
(exchange, arguments) -> CallToolResult.builder()
|
|
||||||
.content(List.of(new TextContent(expectedResult)))
|
|
||||||
.isError(false)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
// Convert to async using fromSync
|
|
||||||
McpServerFeatures.AsyncToolSpecification asyncSpec = McpServerFeatures.AsyncToolSpecification
|
|
||||||
.fromSync(syncSpec);
|
|
||||||
|
|
||||||
assertThat(asyncSpec).isNotNull();
|
|
||||||
assertThat(asyncSpec.tool()).isEqualTo(tool);
|
|
||||||
assertThat(asyncSpec.callHandler()).isNotNull();
|
|
||||||
assertThat(asyncSpec.call()).isNotNull(); // should be set since sync spec has
|
|
||||||
// deprecated call
|
|
||||||
|
|
||||||
// Test that the converted async specification works correctly via callTool
|
|
||||||
CallToolRequest request = new CallToolRequest("sync-deprecated-tool", Map.of("param", "value"));
|
|
||||||
Mono<CallToolResult> resultMono = asyncSpec.callHandler().apply(nullExchange, request);
|
|
||||||
|
|
||||||
StepVerifier.create(resultMono).assertNext(result -> {
|
|
||||||
assertThat(result).isNotNull();
|
|
||||||
assertThat(result.content()).hasSize(1);
|
|
||||||
assertThat(result.content().get(0)).isInstanceOf(TextContent.class);
|
|
||||||
assertThat(((TextContent) result.content().get(0)).text()).isEqualTo(expectedResult);
|
|
||||||
assertThat(result.isError()).isFalse();
|
|
||||||
}).verifyComplete();
|
|
||||||
|
|
||||||
// Test that the deprecated call function also works
|
|
||||||
Mono<CallToolResult> callResultMono = asyncSpec.call().apply(nullExchange, request.arguments());
|
|
||||||
|
|
||||||
StepVerifier.create(callResultMono).assertNext(result -> {
|
|
||||||
assertThat(result).isNotNull();
|
|
||||||
assertThat(result.content()).hasSize(1);
|
|
||||||
assertThat(result.content().get(0)).isInstanceOf(TextContent.class);
|
|
||||||
assertThat(((TextContent) result.content().get(0)).text()).isEqualTo(expectedResult);
|
|
||||||
assertThat(result.isError()).isFalse();
|
|
||||||
}).verifyComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void fromSyncShouldReturnNullWhenSyncSpecIsNull() {
|
void fromSyncShouldReturnNullWhenSyncSpecIsNull() {
|
||||||
assertThat(McpServerFeatures.AsyncToolSpecification.fromSync(null)).isNull();
|
assertThat(McpServerFeatures.AsyncToolSpecification.fromSync(null)).isNull();
|
||||||
|
|
@ -302,7 +194,8 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
void defaultShouldThrowOnInvalidName() {
|
void defaultShouldThrowOnInvalidName() {
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
assertThatThrownBy(() -> McpServer.async(transportProvider).tool(invalidTool, (exchange, args) -> null))
|
assertThatThrownBy(
|
||||||
|
() -> McpServer.async(transportProvider).toolCall(invalidTool, (exchange, request) -> null))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("invalid characters");
|
.hasMessageContaining("invalid characters");
|
||||||
}
|
}
|
||||||
|
|
@ -312,7 +205,7 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
assertThatCode(() -> McpServer.async(transportProvider).tool(invalidTool, (exchange, args) -> null))
|
assertThatCode(() -> McpServer.async(transportProvider).toolCall(invalidTool, (exchange, request) -> null))
|
||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
assertThat(logAppender.list).hasSize(1);
|
assertThat(logAppender.list).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +216,7 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
|
|
||||||
assertThatCode(() -> McpServer.async(transportProvider)
|
assertThatCode(() -> McpServer.async(transportProvider)
|
||||||
.strictToolNameValidation(false)
|
.strictToolNameValidation(false)
|
||||||
.tool(invalidTool, (exchange, args) -> null)).doesNotThrowAnyException();
|
.toolCall(invalidTool, (exchange, request) -> null)).doesNotThrowAnyException();
|
||||||
assertThat(logAppender.list).hasSize(1);
|
assertThat(logAppender.list).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,7 +227,7 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
|
|
||||||
assertThatThrownBy(() -> McpServer.async(transportProvider)
|
assertThatThrownBy(() -> McpServer.async(transportProvider)
|
||||||
.strictToolNameValidation(true)
|
.strictToolNameValidation(true)
|
||||||
.tool(invalidTool, (exchange, args) -> null)).isInstanceOf(IllegalArgumentException.class)
|
.toolCall(invalidTool, (exchange, request) -> null)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("invalid characters");
|
.hasMessageContaining("invalid characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ class McpAsyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testLoggingNotificationWithNullMessage() {
|
void testLoggingNotificationWithNullMessage() {
|
||||||
StepVerifier.create(exchange.loggingNotification(null)).verifyErrorSatisfies(error -> {
|
StepVerifier.create(exchange.loggingNotification(null)).verifyErrorSatisfies(error -> {
|
||||||
assertThat(error).isInstanceOf(McpError.class).hasMessage("Logging message must not be null");
|
assertThat(error).isInstanceOf(IllegalStateException.class).hasMessage("Logging message must not be null");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +301,8 @@ class McpAsyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testCreateElicitationWithNullCapabilities() {
|
void testCreateElicitationWithNullCapabilities() {
|
||||||
// Given - Create exchange with null capabilities
|
// Given - Create exchange with null capabilities
|
||||||
McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange(mockSession, null, clientInfo);
|
McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
|
null, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
.message("Please provide your name")
|
.message("Please provide your name")
|
||||||
|
|
@ -309,7 +310,7 @@ class McpAsyncServerExchangeTests {
|
||||||
|
|
||||||
StepVerifier.create(exchangeWithNullCapabilities.createElicitation(elicitRequest))
|
StepVerifier.create(exchangeWithNullCapabilities.createElicitation(elicitRequest))
|
||||||
.verifyErrorSatisfies(error -> {
|
.verifyErrorSatisfies(error -> {
|
||||||
assertThat(error).isInstanceOf(McpError.class)
|
assertThat(error).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be initialized. Call the initialize method first!");
|
.hasMessage("Client must be initialized. Call the initialize method first!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -324,15 +325,15 @@ class McpAsyncServerExchangeTests {
|
||||||
.roots(true)
|
.roots(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithoutElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange exchangeWithoutElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithoutElicitation, clientInfo);
|
capabilitiesWithoutElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
.message("Please provide your name")
|
.message("Please provide your name")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
StepVerifier.create(exchangeWithoutElicitation.createElicitation(elicitRequest)).verifyErrorSatisfies(error -> {
|
StepVerifier.create(exchangeWithoutElicitation.createElicitation(elicitRequest)).verifyErrorSatisfies(error -> {
|
||||||
assertThat(error).isInstanceOf(McpError.class)
|
assertThat(error).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be configured with elicitation capabilities");
|
.hasMessage("Client must be configured with elicitation capabilities");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -348,8 +349,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
// Create a complex elicit request with schema
|
// Create a complex elicit request with schema
|
||||||
java.util.Map<String, Object> requestedSchema = new java.util.HashMap<>();
|
java.util.Map<String, Object> requestedSchema = new java.util.HashMap<>();
|
||||||
|
|
@ -391,8 +392,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
.message("Please provide sensitive information")
|
.message("Please provide sensitive information")
|
||||||
|
|
@ -418,8 +419,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
.message("Please provide your information")
|
.message("Please provide your information")
|
||||||
|
|
@ -445,8 +446,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
.message("Please provide your name")
|
.message("Please provide your name")
|
||||||
|
|
@ -467,7 +468,8 @@ class McpAsyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testCreateMessageWithNullCapabilities() {
|
void testCreateMessageWithNullCapabilities() {
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange(mockSession, null, clientInfo);
|
McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
|
null, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
.messages(Arrays
|
.messages(Arrays
|
||||||
|
|
@ -476,7 +478,7 @@ class McpAsyncServerExchangeTests {
|
||||||
|
|
||||||
StepVerifier.create(exchangeWithNullCapabilities.createMessage(createMessageRequest))
|
StepVerifier.create(exchangeWithNullCapabilities.createMessage(createMessageRequest))
|
||||||
.verifyErrorSatisfies(error -> {
|
.verifyErrorSatisfies(error -> {
|
||||||
assertThat(error).isInstanceOf(McpError.class)
|
assertThat(error).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be initialized. Call the initialize method first!");
|
.hasMessage("Client must be initialized. Call the initialize method first!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -492,8 +494,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.roots(true)
|
.roots(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithoutSampling = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange exchangeWithoutSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithoutSampling, clientInfo);
|
capabilitiesWithoutSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
.messages(Arrays
|
.messages(Arrays
|
||||||
|
|
@ -501,7 +503,7 @@ class McpAsyncServerExchangeTests {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
StepVerifier.create(exchangeWithoutSampling.createMessage(createMessageRequest)).verifyErrorSatisfies(error -> {
|
StepVerifier.create(exchangeWithoutSampling.createMessage(createMessageRequest)).verifyErrorSatisfies(error -> {
|
||||||
assertThat(error).isInstanceOf(McpError.class)
|
assertThat(error).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be configured with sampling capabilities");
|
.hasMessage("Client must be configured with sampling capabilities");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -517,8 +519,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange(mockSession, capabilitiesWithSampling,
|
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
.messages(Arrays
|
.messages(Arrays
|
||||||
|
|
@ -553,8 +555,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange(mockSession, capabilitiesWithSampling,
|
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
// Create request with image content
|
// Create request with image content
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
|
|
@ -588,8 +590,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange(mockSession, capabilitiesWithSampling,
|
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
.messages(Arrays
|
.messages(Arrays
|
||||||
|
|
@ -612,8 +614,8 @@ class McpAsyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange(mockSession, capabilitiesWithSampling,
|
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
.messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
.messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||||
|
|
@ -662,7 +664,7 @@ class McpAsyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testPingWithMcpError() {
|
void testPingWithMcpError() {
|
||||||
// Given - Mock an MCP-specific error during ping
|
// Given - Mock an MCP-specific error during ping
|
||||||
McpError mcpError = new McpError("Server unavailable");
|
McpError mcpError = McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR).message("Server unavailable").build();
|
||||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_PING), eq(null), any(TypeRef.class)))
|
when(mockSession.sendRequest(eq(McpSchema.METHOD_PING), eq(null), any(TypeRef.class)))
|
||||||
.thenReturn(Mono.error(mcpError));
|
.thenReturn(Mono.error(mcpError));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.modelcontextprotocol.common.McpTransportContext;
|
||||||
import io.modelcontextprotocol.spec.McpError;
|
import io.modelcontextprotocol.spec.McpError;
|
||||||
import io.modelcontextprotocol.spec.McpSchema;
|
import io.modelcontextprotocol.spec.McpSchema;
|
||||||
import io.modelcontextprotocol.spec.McpServerSession;
|
import io.modelcontextprotocol.spec.McpServerSession;
|
||||||
|
|
@ -54,7 +55,8 @@ class McpSyncServerExchangeTests {
|
||||||
|
|
||||||
clientInfo = new McpSchema.Implementation("test-client", "1.0.0");
|
clientInfo = new McpSchema.Implementation("test-client", "1.0.0");
|
||||||
|
|
||||||
asyncExchange = new McpAsyncServerExchange(mockSession, clientCapabilities, clientInfo);
|
asyncExchange = new McpAsyncServerExchange("testSessionId", mockSession, clientCapabilities, clientInfo,
|
||||||
|
McpTransportContext.EMPTY);
|
||||||
exchange = new McpSyncServerExchange(asyncExchange);
|
exchange = new McpSyncServerExchange(asyncExchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +214,7 @@ class McpSyncServerExchangeTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testLoggingNotificationWithNullMessage() {
|
void testLoggingNotificationWithNullMessage() {
|
||||||
assertThatThrownBy(() -> exchange.loggingNotification(null)).isInstanceOf(McpError.class)
|
assertThatThrownBy(() -> exchange.loggingNotification(null)).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Logging message must not be null");
|
.hasMessage("Logging message must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,8 +296,8 @@ class McpSyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testCreateElicitationWithNullCapabilities() {
|
void testCreateElicitationWithNullCapabilities() {
|
||||||
// Given - Create exchange with null capabilities
|
// Given - Create exchange with null capabilities
|
||||||
McpAsyncServerExchange asyncExchangeWithNullCapabilities = new McpAsyncServerExchange(mockSession, null,
|
McpAsyncServerExchange asyncExchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId",
|
||||||
clientInfo);
|
mockSession, null, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange(
|
McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange(
|
||||||
asyncExchangeWithNullCapabilities);
|
asyncExchangeWithNullCapabilities);
|
||||||
|
|
||||||
|
|
@ -304,7 +306,7 @@ class McpSyncServerExchangeTests {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertThatThrownBy(() -> exchangeWithNullCapabilities.createElicitation(elicitRequest))
|
assertThatThrownBy(() -> exchangeWithNullCapabilities.createElicitation(elicitRequest))
|
||||||
.isInstanceOf(McpError.class)
|
.isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be initialized. Call the initialize method first!");
|
.hasMessage("Client must be initialized. Call the initialize method first!");
|
||||||
|
|
||||||
// Verify that sendRequest was never called due to null capabilities
|
// Verify that sendRequest was never called due to null capabilities
|
||||||
|
|
@ -318,8 +320,8 @@ class McpSyncServerExchangeTests {
|
||||||
.roots(true)
|
.roots(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithoutElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithoutElicitation = new McpAsyncServerExchange("testSessionId",
|
||||||
capabilitiesWithoutElicitation, clientInfo);
|
mockSession, capabilitiesWithoutElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithoutElicitation = new McpSyncServerExchange(asyncExchangeWithoutElicitation);
|
McpSyncServerExchange exchangeWithoutElicitation = new McpSyncServerExchange(asyncExchangeWithoutElicitation);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
|
|
@ -327,7 +329,7 @@ class McpSyncServerExchangeTests {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertThatThrownBy(() -> exchangeWithoutElicitation.createElicitation(elicitRequest))
|
assertThatThrownBy(() -> exchangeWithoutElicitation.createElicitation(elicitRequest))
|
||||||
.isInstanceOf(McpError.class)
|
.isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be configured with elicitation capabilities");
|
.hasMessage("Client must be configured with elicitation capabilities");
|
||||||
|
|
||||||
// Verify that sendRequest was never called due to missing elicitation
|
// Verify that sendRequest was never called due to missing elicitation
|
||||||
|
|
@ -342,8 +344,8 @@ class McpSyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||||
|
|
||||||
// Create a complex elicit request with schema
|
// Create a complex elicit request with schema
|
||||||
|
|
@ -386,8 +388,8 @@ class McpSyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
|
|
@ -414,8 +416,8 @@ class McpSyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
|
|
@ -442,8 +444,8 @@ class McpSyncServerExchangeTests {
|
||||||
.elicitation()
|
.elicitation()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithElicitation, clientInfo);
|
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||||
|
|
||||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||||
|
|
@ -465,8 +467,8 @@ class McpSyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testCreateMessageWithNullCapabilities() {
|
void testCreateMessageWithNullCapabilities() {
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithNullCapabilities = new McpAsyncServerExchange(mockSession, null,
|
McpAsyncServerExchange asyncExchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId",
|
||||||
clientInfo);
|
mockSession, null, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange(
|
McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange(
|
||||||
asyncExchangeWithNullCapabilities);
|
asyncExchangeWithNullCapabilities);
|
||||||
|
|
||||||
|
|
@ -476,7 +478,7 @@ class McpSyncServerExchangeTests {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertThatThrownBy(() -> exchangeWithNullCapabilities.createMessage(createMessageRequest))
|
assertThatThrownBy(() -> exchangeWithNullCapabilities.createMessage(createMessageRequest))
|
||||||
.isInstanceOf(McpError.class)
|
.isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be initialized. Call the initialize method first!");
|
.hasMessage("Client must be initialized. Call the initialize method first!");
|
||||||
|
|
||||||
// Verify that sendRequest was never called due to null capabilities
|
// Verify that sendRequest was never called due to null capabilities
|
||||||
|
|
@ -491,8 +493,8 @@ class McpSyncServerExchangeTests {
|
||||||
.roots(true)
|
.roots(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithoutSampling = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithoutSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithoutSampling, clientInfo);
|
capabilitiesWithoutSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithoutSampling = new McpSyncServerExchange(asyncExchangeWithoutSampling);
|
McpSyncServerExchange exchangeWithoutSampling = new McpSyncServerExchange(asyncExchangeWithoutSampling);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
|
|
@ -501,7 +503,7 @@ class McpSyncServerExchangeTests {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertThatThrownBy(() -> exchangeWithoutSampling.createMessage(createMessageRequest))
|
assertThatThrownBy(() -> exchangeWithoutSampling.createMessage(createMessageRequest))
|
||||||
.isInstanceOf(McpError.class)
|
.isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessage("Client must be configured with sampling capabilities");
|
.hasMessage("Client must be configured with sampling capabilities");
|
||||||
|
|
||||||
// Verify that sendRequest was never called due to missing sampling capabilities
|
// Verify that sendRequest was never called due to missing sampling capabilities
|
||||||
|
|
@ -516,8 +518,8 @@ class McpSyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithSampling, clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
|
|
@ -553,8 +555,8 @@ class McpSyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithSampling, clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||||
|
|
||||||
// Create request with image content
|
// Create request with image content
|
||||||
|
|
@ -589,8 +591,8 @@ class McpSyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithSampling, clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
|
|
@ -614,8 +616,8 @@ class McpSyncServerExchangeTests {
|
||||||
.sampling()
|
.sampling()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange(mockSession,
|
McpAsyncServerExchange asyncExchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||||
capabilitiesWithSampling, clientInfo);
|
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||||
|
|
||||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||||
|
|
@ -662,7 +664,7 @@ class McpSyncServerExchangeTests {
|
||||||
@Test
|
@Test
|
||||||
void testPingWithMcpError() {
|
void testPingWithMcpError() {
|
||||||
// Given - Mock an MCP-specific error during ping
|
// Given - Mock an MCP-specific error during ping
|
||||||
McpError mcpError = new McpError("Server unavailable");
|
McpError mcpError = McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR).message("Server unavailable").build();
|
||||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_PING), eq(null), any(TypeRef.class)))
|
when(mockSession.sendRequest(eq(McpSchema.METHOD_PING), eq(null), any(TypeRef.class)))
|
||||||
.thenReturn(Mono.error(mcpError));
|
.thenReturn(Mono.error(mcpError));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,26 @@ public class ResourceTemplateListingTest {
|
||||||
void testResourceListingWithMixedResources() {
|
void testResourceListingWithMixedResources() {
|
||||||
// Create resource list with both regular and template resources
|
// Create resource list with both regular and template resources
|
||||||
List<McpSchema.Resource> allResources = List.of(
|
List<McpSchema.Resource> allResources = List.of(
|
||||||
new McpSchema.Resource("file:///test/doc1.txt", "Document 1", "text/plain", null, null),
|
McpSchema.Resource.builder()
|
||||||
new McpSchema.Resource("file:///test/doc2.txt", "Document 2", "text/plain", null, null),
|
.uri("file:///test/doc1.txt")
|
||||||
new McpSchema.Resource("file:///test/{type}/document.txt", "Typed Document", "text/plain", null, null),
|
.name("Document 1")
|
||||||
new McpSchema.Resource("file:///users/{userId}/files/{fileId}", "User File", "text/plain", null, null));
|
.mimeType("text/plain")
|
||||||
|
.build(),
|
||||||
|
McpSchema.Resource.builder()
|
||||||
|
.uri("file:///test/doc2.txt")
|
||||||
|
.name("Document 2")
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build(),
|
||||||
|
McpSchema.Resource.builder()
|
||||||
|
.uri("file:///test/{type}/document.txt")
|
||||||
|
.name("Typed Document")
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build(),
|
||||||
|
McpSchema.Resource.builder()
|
||||||
|
.uri("file:///users/{userId}/files/{fileId}")
|
||||||
|
.name("User File")
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build());
|
||||||
|
|
||||||
// Apply the filter logic from McpAsyncServer line 438
|
// Apply the filter logic from McpAsyncServer line 438
|
||||||
List<McpSchema.Resource> filteredResources = allResources.stream()
|
List<McpSchema.Resource> filteredResources = allResources.stream()
|
||||||
|
|
@ -61,9 +77,16 @@ public class ResourceTemplateListingTest {
|
||||||
void testResourceTemplatesListedSeparately() {
|
void testResourceTemplatesListedSeparately() {
|
||||||
// Create mixed resources
|
// Create mixed resources
|
||||||
List<McpSchema.Resource> resources = List.of(
|
List<McpSchema.Resource> resources = List.of(
|
||||||
new McpSchema.Resource("file:///test/regular.txt", "Regular Resource", "text/plain", null, null),
|
McpSchema.Resource.builder()
|
||||||
new McpSchema.Resource("file:///test/user/{userId}/profile.txt", "User Profile", "text/plain", null,
|
.uri("file:///test/regular.txt")
|
||||||
null));
|
.name("Regular Resource")
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build(),
|
||||||
|
McpSchema.Resource.builder()
|
||||||
|
.uri("file:///test/user/{userId}/profile.txt")
|
||||||
|
.name("User Profile")
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build());
|
||||||
|
|
||||||
// Create explicit resource template
|
// Create explicit resource template
|
||||||
McpSchema.ResourceTemplate explicitTemplate = new McpSchema.ResourceTemplate(
|
McpSchema.ResourceTemplate explicitTemplate = new McpSchema.ResourceTemplate(
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue