feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization (#928)
Every wire-serialized record in McpSchema now validates spec-required
fields at construction time. Wire deserialization is intentionally
lenient: a missing required field is replaced with a documented default
and a WARN is logged, instead of failing the parse. A required-first
builder convention is introduced across the schema so it is no longer
possible to obtain a builder that is missing required state.
Required-field guards
---------------------
Every wire record's compact constructor asserts non-null (and non-empty
for String identifiers like name, uri, uriTemplate, version) on its
spec-required components. Passing null now throws
IllegalArgumentException at construction time instead of silently
producing invalid JSON via @JsonInclude(NON_ABSENT). This applies to
the JSON-RPC envelopes, lifecycle types, resource/prompt/tool requests
and results, sampling and elicitation, content records, root, complete,
logging and progress notifications, and the two CompleteReference
implementations. See MIGRATION-2.0.md for the full list.
Lenient wire deserialization
----------------------------
For each of those records (except JSONRPCResponse.JSONRPCError, which
still fails fast) a @JsonCreator static `fromJson` factory substitutes
a documented default for any absent required field — "" for strings,
[] for collections, {} for maps, 0 / 0.0 for numerics, INFO for
LoggingLevel, USER for SamplingMessage.role, ASSISTANT for
CreateMessageResult.role, CANCEL for ElicitResult.action, {values=[]}
for CompleteResult.completion — and logs a WARN naming the field and
the value used. Application code can still observe a malformed message
and react, but the SDK no longer halts the conversation.
Builder convention
------------------
Records that have a builder gain a required-first factory method
`builder(req1, req2, …)`; setters for required fields are removed from
the builder so it cannot be left in an invalid state. Existing no-arg
`builder()` factories and required-field setters are kept where source
compatibility demands it but are marked @Deprecated. New builders are
also added for several records that previously had none (ProgressNotification,
JSONRPCError, CompleteRequest, list/result types, content records, ...).
JSON-RPC envelope ergonomics
----------------------------
Previously every JSON-RPC envelope had to be constructed via the
canonical record constructor and the literal "2.0" string had to be
threaded through every call site, e.g.
new JSONRPCRequest("2.0", "tools/call", id, params)
new JSONRPCResponse("2.0", id, result, null)
new JSONRPCResponse("2.0", id, null, new JSONRPCError(code, message, null))
Now:
new JSONRPCRequest("tools/call", id, params) // jsonrpc defaulted
new JSONRPCNotification("notifications/initialized") // params optional
JSONRPCResponse.result(id, result) // factory
JSONRPCResponse.error(id, new JSONRPCError(code, message)) // 2-arg error
JSONRPCResponse's compact constructor additionally enforces the
JSON-RPC invariant that exactly one of `result` / `error` is set —
previously the SDK could build envelopes that violated the protocol.
CompleteReference changes
-------------------------
- PromptReference.equals/hashCode now key on `name` only (previously
derived from identifier()+type()). Two refs with the same name but
different titles now compare equal — code using PromptReference as a
map/set key should be audited.
- PromptReference's compact constructor pins `type` to "ref/prompt"
and logs a WARN if the caller supplies a different non-null value.
The legacy two-arg `PromptReference(String type, String name)`
constructor remains @Deprecated.
- ResourceReference's record components are reduced from (type, uri)
to (uri) — positional construction breaks. The legacy
`ResourceReference(String type, String uri)` constructor stays
@Deprecated and ignores `type`.
- CompleteReference.identifier() is @Deprecated and now returns null
via a default method on the interface.
Tests / refactor
----------------
Conformance harness, integration tests, sample apps, and internal
callers were migrated to the new builder factories and convenience
constructors. No semantic changes to client/server runtime behaviour
beyond the McpSchema changes above.
Docs
----
- MIGRATION-2.0.md: required-field section now covers the broader
record set and the lenient-deserialization behaviour; PromptReference
WARN behaviour and ResourceReference component reduction are
documented; the JSON-RPC envelope section is rewritten to compare the
pre-2.0 surface with the new one.
- CONTRIBUTING.md: the "Evolving wire-serialized records" recipe is
split into two cases — adding a new optional field (existing rules)
and adding/maintaining a spec-required field (new rules covering
Assert in compact constructor, @JsonCreator fromJson with defaults
and WARN, required-first builder factory).
Signed-off-by: Dariusz Jędrzejczyk <2554306+chemicL@users.noreply.github.com>
This commit is contained in:
parent
599b43cac4
commit
22a0efa577
30 changed files with 4258 additions and 1688 deletions
|
|
@ -302,8 +302,9 @@ class LifecycleInitializer {
|
|||
|
||||
String latestVersion = this.protocolVersions.get(this.protocolVersions.size() - 1);
|
||||
|
||||
McpSchema.InitializeRequest initializeRequest = new McpSchema.InitializeRequest(latestVersion,
|
||||
this.clientCapabilities, this.clientInfo);
|
||||
McpSchema.InitializeRequest initializeRequest = McpSchema.InitializeRequest
|
||||
.builder(latestVersion, this.clientCapabilities, this.clientInfo)
|
||||
.build();
|
||||
|
||||
Mono<McpSchema.InitializeResult> result = mcpClientSession.sendRequest(McpSchema.METHOD_INITIALIZE,
|
||||
initializeRequest, McpAsyncClient.INITIALIZE_RESULT_TYPE_REF);
|
||||
|
|
|
|||
|
|
@ -538,7 +538,7 @@ public class McpAsyncClient {
|
|||
|
||||
List<Root> roots = this.roots.values().stream().toList();
|
||||
|
||||
return Mono.just(new McpSchema.ListRootsResult(roots));
|
||||
return Mono.just(McpSchema.ListRootsResult.builder(roots).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -633,10 +633,10 @@ public class McpAsyncClient {
|
|||
return this.listTools(McpSchema.FIRST_PAGE).expand(result -> {
|
||||
String next = result.nextCursor();
|
||||
return (next != null && !next.isEmpty()) ? this.listTools(next) : Mono.empty();
|
||||
}).reduce(new McpSchema.ListToolsResult(new ArrayList<>(), null), (allToolsResult, result) -> {
|
||||
allToolsResult.tools().addAll(result.tools());
|
||||
return allToolsResult;
|
||||
}).map(result -> new McpSchema.ListToolsResult(Collections.unmodifiableList(result.tools()), null));
|
||||
}).reduce(new ArrayList<McpSchema.Tool>(), (accumulated, result) -> {
|
||||
accumulated.addAll(result.tools());
|
||||
return accumulated;
|
||||
}).map(all -> McpSchema.ListToolsResult.builder(Collections.unmodifiableList(all)).build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -719,11 +719,11 @@ public class McpAsyncClient {
|
|||
public Mono<McpSchema.ListResourcesResult> listResources() {
|
||||
return this.listResources(McpSchema.FIRST_PAGE)
|
||||
.expand(result -> (result.nextCursor() != null) ? this.listResources(result.nextCursor()) : Mono.empty())
|
||||
.reduce(new McpSchema.ListResourcesResult(new ArrayList<>(), null), (allResourcesResult, result) -> {
|
||||
allResourcesResult.resources().addAll(result.resources());
|
||||
return allResourcesResult;
|
||||
.reduce(new ArrayList<McpSchema.Resource>(), (accumulated, result) -> {
|
||||
accumulated.addAll(result.resources());
|
||||
return accumulated;
|
||||
})
|
||||
.map(result -> new McpSchema.ListResourcesResult(Collections.unmodifiableList(result.resources()), null));
|
||||
.map(all -> McpSchema.ListResourcesResult.builder(Collections.unmodifiableList(all)).build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -774,7 +774,7 @@ public class McpAsyncClient {
|
|||
* @see McpSchema.ReadResourceResult
|
||||
*/
|
||||
public Mono<McpSchema.ReadResourceResult> readResource(McpSchema.Resource resource) {
|
||||
return this.readResource(new McpSchema.ReadResourceRequest(resource.uri()));
|
||||
return this.readResource(McpSchema.ReadResourceRequest.builder(resource.uri()).build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -806,13 +806,11 @@ public class McpAsyncClient {
|
|||
return this.listResourceTemplates(McpSchema.FIRST_PAGE)
|
||||
.expand(result -> (result.nextCursor() != null) ? this.listResourceTemplates(result.nextCursor())
|
||||
: Mono.empty())
|
||||
.reduce(new McpSchema.ListResourceTemplatesResult(new ArrayList<>(), null),
|
||||
(allResourceTemplatesResult, result) -> {
|
||||
allResourceTemplatesResult.resourceTemplates().addAll(result.resourceTemplates());
|
||||
return allResourceTemplatesResult;
|
||||
})
|
||||
.map(result -> new McpSchema.ListResourceTemplatesResult(
|
||||
Collections.unmodifiableList(result.resourceTemplates()), null));
|
||||
.reduce(new ArrayList<McpSchema.ResourceTemplate>(), (accumulated, result) -> {
|
||||
accumulated.addAll(result.resourceTemplates());
|
||||
return accumulated;
|
||||
})
|
||||
.map(all -> McpSchema.ListResourceTemplatesResult.builder(Collections.unmodifiableList(all)).build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -898,7 +896,7 @@ public class McpAsyncClient {
|
|||
new TypeRef<>() {
|
||||
});
|
||||
|
||||
return readResource(new McpSchema.ReadResourceRequest(resourcesUpdatedNotification.uri()))
|
||||
return readResource(McpSchema.ReadResourceRequest.builder(resourcesUpdatedNotification.uri()).build())
|
||||
.flatMap(readResourceResult -> Flux.fromIterable(resourcesUpdateConsumers)
|
||||
.flatMap(consumer -> consumer.apply(readResourceResult.contents()))
|
||||
.onErrorResume(error -> {
|
||||
|
|
@ -927,11 +925,11 @@ public class McpAsyncClient {
|
|||
public Mono<ListPromptsResult> listPrompts() {
|
||||
return this.listPrompts(McpSchema.FIRST_PAGE)
|
||||
.expand(result -> (result.nextCursor() != null) ? this.listPrompts(result.nextCursor()) : Mono.empty())
|
||||
.reduce(new ListPromptsResult(new ArrayList<>(), null), (allPromptsResult, result) -> {
|
||||
allPromptsResult.prompts().addAll(result.prompts());
|
||||
return allPromptsResult;
|
||||
.reduce(new ArrayList<McpSchema.Prompt>(), (accumulated, result) -> {
|
||||
accumulated.addAll(result.prompts());
|
||||
return accumulated;
|
||||
})
|
||||
.map(result -> new McpSchema.ListPromptsResult(Collections.unmodifiableList(result.prompts()), null));
|
||||
.map(all -> McpSchema.ListPromptsResult.builder(Collections.unmodifiableList(all)).build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ public interface McpClient {
|
|||
|
||||
private ClientCapabilities capabilities;
|
||||
|
||||
private Implementation clientInfo = new Implementation("Java SDK MCP Client", "0.15.0");
|
||||
private Implementation clientInfo = Implementation.builder("Java SDK MCP Client", "0.15.0").build();
|
||||
|
||||
private final Map<String, Root> roots = new HashMap<>();
|
||||
|
||||
|
|
@ -525,7 +525,7 @@ public interface McpClient {
|
|||
|
||||
private ClientCapabilities capabilities;
|
||||
|
||||
private Implementation clientInfo = new Implementation("Java SDK MCP Client", "0.15.0");
|
||||
private Implementation clientInfo = Implementation.builder("Java SDK MCP Client", "0.15.0").build();
|
||||
|
||||
private final Map<String, Root> roots = new HashMap<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,8 @@ class McpClientFeatures {
|
|||
: new McpSchema.ClientCapabilities(null,
|
||||
!Utils.isEmpty(roots) ? new McpSchema.ClientCapabilities.RootCapabilities(false) : null,
|
||||
samplingHandler != null ? new McpSchema.ClientCapabilities.Sampling() : null,
|
||||
elicitationHandler != null ? new McpSchema.ClientCapabilities.Elicitation() : null);
|
||||
elicitationHandler != null ? McpSchema.ClientCapabilities.Elicitation.builder().build()
|
||||
: null);
|
||||
this.roots = roots != null ? new ConcurrentHashMap<>(roots) : new ConcurrentHashMap<>();
|
||||
|
||||
this.toolsChangeConsumers = toolsChangeConsumers != null ? toolsChangeConsumers : List.of();
|
||||
|
|
@ -256,7 +257,8 @@ class McpClientFeatures {
|
|||
: new McpSchema.ClientCapabilities(null,
|
||||
!Utils.isEmpty(roots) ? new McpSchema.ClientCapabilities.RootCapabilities(false) : null,
|
||||
samplingHandler != null ? new McpSchema.ClientCapabilities.Sampling() : null,
|
||||
elicitationHandler != null ? new McpSchema.ClientCapabilities.Elicitation() : null);
|
||||
elicitationHandler != null ? McpSchema.ClientCapabilities.Elicitation.builder().build()
|
||||
: null);
|
||||
this.roots = roots != null ? new HashMap<>(roots) : new HashMap<>();
|
||||
|
||||
this.toolsChangeConsumers = toolsChangeConsumers != null ? toolsChangeConsumers : List.of();
|
||||
|
|
|
|||
|
|
@ -11,17 +11,15 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.modelcontextprotocol.util.Assert;
|
||||
|
||||
/**
|
||||
* Server parameters for stdio client.
|
||||
* Server parameters for stdio client. This is not a wire type; Jackson annotations are
|
||||
* intentionally omitted.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Dariusz Jędrzejczyk
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
public class ServerParameters {
|
||||
|
||||
// Environment variables to inherit by default
|
||||
|
|
@ -32,13 +30,10 @@ public class ServerParameters {
|
|||
"SYSTEMDRIVE", "SYSTEMROOT", "TEMP", "USERNAME", "USERPROFILE")
|
||||
: Arrays.asList("HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER");
|
||||
|
||||
@JsonProperty("command")
|
||||
private String command;
|
||||
|
||||
@JsonProperty("args")
|
||||
private List<String> args = new ArrayList<>();
|
||||
|
||||
@JsonProperty("env")
|
||||
private Map<String, String> env;
|
||||
|
||||
private ServerParameters(String command, List<String> args, Map<String, String> env) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class DefaultMcpStatelessServerHandler implements McpStatelessServerHandler {
|
|||
.build());
|
||||
}
|
||||
return requestHandler.handle(transportContext, request.params())
|
||||
.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null))
|
||||
.map(result -> McpSchema.JSONRPCResponse.result(request.id(), result))
|
||||
.onErrorResume(t -> {
|
||||
McpSchema.JSONRPCResponse.JSONRPCError error;
|
||||
if (t instanceof McpError mcpError && mcpError.getJsonRpcError() != null) {
|
||||
|
|
@ -45,9 +45,9 @@ class DefaultMcpStatelessServerHandler implements McpStatelessServerHandler {
|
|||
}
|
||||
else {
|
||||
error = new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
|
||||
t.getMessage(), null);
|
||||
t.getMessage());
|
||||
}
|
||||
return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, error));
|
||||
return Mono.just(McpSchema.JSONRPCResponse.error(request.id(), error));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@ public class McpAsyncServer {
|
|||
String content = "Response missing structured content which is expected when calling tool with non-empty outputSchema";
|
||||
logger.warn(content);
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(content)))
|
||||
.content(List.of(McpSchema.TextContent.builder(content).build()))
|
||||
.isError(true)
|
||||
.build();
|
||||
}
|
||||
|
|
@ -425,7 +425,7 @@ public class McpAsyncServer {
|
|||
if (!validation.valid()) {
|
||||
logger.warn("Tool call result validation failed: {}", validation.errorMessage());
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(validation.errorMessage())))
|
||||
.content(List.of(McpSchema.TextContent.builder(validation.errorMessage()).build()))
|
||||
.isError(true)
|
||||
.build();
|
||||
}
|
||||
|
|
@ -438,7 +438,7 @@ public class McpAsyncServer {
|
|||
// https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content
|
||||
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(validation.jsonStructuredOutput())))
|
||||
.content(List.of(McpSchema.TextContent.builder(validation.jsonStructuredOutput()).build()))
|
||||
.isError(result.isError())
|
||||
.structuredContent(result.structuredContent())
|
||||
.build();
|
||||
|
|
@ -529,7 +529,7 @@ public class McpAsyncServer {
|
|||
return (exchange, params) -> {
|
||||
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList();
|
||||
|
||||
return Mono.just(new McpSchema.ListToolsResult(tools, null));
|
||||
return Mono.just(McpSchema.ListToolsResult.builder(tools).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -781,7 +781,7 @@ public class McpAsyncServer {
|
|||
.stream()
|
||||
.map(McpServerFeatures.AsyncResourceSpecification::resource)
|
||||
.toList();
|
||||
return Mono.just(new McpSchema.ListResourcesResult(resourceList, null));
|
||||
return Mono.just(McpSchema.ListResourcesResult.builder(resourceList).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -791,7 +791,7 @@ public class McpAsyncServer {
|
|||
.stream()
|
||||
.map(McpServerFeatures.AsyncResourceTemplateSpecification::resourceTemplate)
|
||||
.toList();
|
||||
return Mono.just(new McpSchema.ListResourceTemplatesResult(resourceList, null));
|
||||
return Mono.just(McpSchema.ListResourceTemplatesResult.builder(resourceList).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -923,7 +923,7 @@ public class McpAsyncServer {
|
|||
.map(McpServerFeatures.AsyncPromptSpecification::prompt)
|
||||
.toList();
|
||||
|
||||
return Mono.just(new McpSchema.ListPromptsResult(promptList, null));
|
||||
return Mono.just(McpSchema.ListPromptsResult.builder(promptList).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,13 +166,13 @@ public class McpAsyncServerExchange {
|
|||
return this.listRoots(McpSchema.FIRST_PAGE)
|
||||
.expand(result -> (result.nextCursor() != null) ?
|
||||
this.listRoots(result.nextCursor()) : Mono.empty())
|
||||
.reduce(new McpSchema.ListRootsResult(new ArrayList<>(), null),
|
||||
.reduce(McpSchema.ListRootsResult.builder(new ArrayList<>()).build(),
|
||||
(allRootsResult, result) -> {
|
||||
allRootsResult.roots().addAll(result.roots());
|
||||
return allRootsResult;
|
||||
})
|
||||
.map(result -> new McpSchema.ListRootsResult(Collections.unmodifiableList(result.roots()),
|
||||
result.nextCursor()));
|
||||
.map(result -> McpSchema.ListRootsResult.builder(Collections.unmodifiableList(result.roots()))
|
||||
.nextCursor(result.nextCursor()).build());
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,9 +66,9 @@ import reactor.core.publisher.Mono;
|
|||
* Example of creating a basic synchronous server: <pre>{@code
|
||||
* McpServer.sync(transportProvider)
|
||||
* .serverInfo("my-server", "1.0.0")
|
||||
* .toolCall(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
||||
* .toolCall(Tool.builder("calculator", schema).title("Performs calculations").build(),
|
||||
* (exchange, request) -> CallToolResult.builder()
|
||||
* .content(List.of(new McpSchema.TextContent("Result: " + calculate(request.arguments()))))
|
||||
* .content(List.of(McpSchema.TextContent.builder("Result: " + calculate(request.arguments())).build()))
|
||||
* .isError(false)
|
||||
* .build())
|
||||
* .build();
|
||||
|
|
@ -77,10 +77,10 @@ import reactor.core.publisher.Mono;
|
|||
* Example of creating a basic asynchronous server: <pre>{@code
|
||||
* McpServer.async(transportProvider)
|
||||
* .serverInfo("my-server", "1.0.0")
|
||||
* .toolCall(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
|
||||
* .toolCall(Tool.builder("calculator", schema).title("Performs calculations").build(),
|
||||
* (exchange, request) -> Mono.fromSupplier(() -> calculate(request.arguments()))
|
||||
* .map(result -> CallToolResult.builder()
|
||||
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
|
||||
* .content(List.of(McpSchema.TextContent.builder("Result: " + result).build()))
|
||||
* .isError(false)
|
||||
* .build()))
|
||||
* .build();
|
||||
|
|
@ -97,7 +97,7 @@ import reactor.core.publisher.Mono;
|
|||
* .tool(calculatorTool)
|
||||
* .callTool((exchange, args) -> Mono.fromSupplier(() -> calculate(args.arguments()))
|
||||
* .map(result -> CallToolResult.builder()
|
||||
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
|
||||
* .content(List.of(McpSchema.TextContent.builder("Result: " + result).build()))
|
||||
* .isError(false)
|
||||
* .build()))
|
||||
*. .build(),
|
||||
|
|
@ -105,7 +105,7 @@ import reactor.core.publisher.Mono;
|
|||
* .tool((weatherTool)
|
||||
* .callTool((exchange, args) -> Mono.fromSupplier(() -> getWeather(args.arguments()))
|
||||
* .map(result -> CallToolResult.builder()
|
||||
* .content(List.of(new McpSchema.TextContent("Weather: " + result)))
|
||||
* .content(List.of(McpSchema.TextContent.builder("Weather: " + result).build()))
|
||||
* .isError(false)
|
||||
* .build()))
|
||||
* .build()
|
||||
|
|
@ -145,7 +145,8 @@ import reactor.core.publisher.Mono;
|
|||
*/
|
||||
public interface McpServer {
|
||||
|
||||
McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("Java SDK MCP Server", "0.15.0");
|
||||
McpSchema.Implementation DEFAULT_SERVER_INFO = McpSchema.Implementation.builder("Java SDK MCP Server", "0.15.0")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Starts building a synchronous MCP server that provides blocking operations.
|
||||
|
|
@ -395,7 +396,7 @@ public interface McpServer {
|
|||
public AsyncSpecification<S> serverInfo(String name, String version) {
|
||||
Assert.hasText(name, "Name must not be null or empty");
|
||||
Assert.hasText(version, "Version must not be null or empty");
|
||||
this.serverInfo = new McpSchema.Implementation(name, version);
|
||||
this.serverInfo = McpSchema.Implementation.builder(name, version).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -992,7 +993,7 @@ public interface McpServer {
|
|||
public SyncSpecification<S> serverInfo(String name, String version) {
|
||||
Assert.hasText(name, "Name must not be null or empty");
|
||||
Assert.hasText(version, "Version must not be null or empty");
|
||||
this.serverInfo = new McpSchema.Implementation(name, version);
|
||||
this.serverInfo = McpSchema.Implementation.builder(name, version).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -1531,7 +1532,7 @@ public interface McpServer {
|
|||
public StatelessAsyncSpecification serverInfo(String name, String version) {
|
||||
Assert.hasText(name, "Name must not be null or empty");
|
||||
Assert.hasText(version, "Version must not be null or empty");
|
||||
this.serverInfo = new McpSchema.Implementation(name, version);
|
||||
this.serverInfo = McpSchema.Implementation.builder(name, version).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -2028,7 +2029,7 @@ public interface McpServer {
|
|||
public StatelessSyncSpecification serverInfo(String name, String version) {
|
||||
Assert.hasText(name, "Name must not be null or empty");
|
||||
Assert.hasText(version, "Version must not be null or empty");
|
||||
this.serverInfo = new McpSchema.Implementation(name, version);
|
||||
this.serverInfo = McpSchema.Implementation.builder(name, version).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,12 @@ public class McpServerFeatures {
|
|||
// logging
|
||||
// by
|
||||
// default
|
||||
!Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
|
||||
!Utils.isEmpty(prompts) ? McpSchema.ServerCapabilities.PromptCapabilities.builder().build()
|
||||
: null,
|
||||
!Utils.isEmpty(resources)
|
||||
? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null,
|
||||
!Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);
|
||||
? McpSchema.ServerCapabilities.ResourceCapabilities.builder().build() : null,
|
||||
!Utils.isEmpty(tools) ? McpSchema.ServerCapabilities.ToolCapabilities.builder().build()
|
||||
: null);
|
||||
|
||||
this.tools = (tools != null) ? tools : List.of();
|
||||
this.resources = (resources != null) ? resources : Map.of();
|
||||
|
|
@ -192,10 +194,12 @@ public class McpServerFeatures {
|
|||
// logging
|
||||
// by
|
||||
// default
|
||||
!Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
|
||||
!Utils.isEmpty(prompts) ? McpSchema.ServerCapabilities.PromptCapabilities.builder().build()
|
||||
: null,
|
||||
!Utils.isEmpty(resources)
|
||||
? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null,
|
||||
!Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);
|
||||
? McpSchema.ServerCapabilities.ResourceCapabilities.builder().build() : null,
|
||||
!Utils.isEmpty(tools) ? McpSchema.ServerCapabilities.ToolCapabilities.builder().build()
|
||||
: null);
|
||||
|
||||
this.tools = (tools != null) ? tools : new ArrayList<>();
|
||||
this.resources = (resources != null) ? resources : new HashMap<>();
|
||||
|
|
@ -487,20 +491,19 @@ public class McpServerFeatures {
|
|||
*
|
||||
* <pre>{@code
|
||||
* McpServerFeatures.SyncToolSpecification.builder()
|
||||
* .tool(Tool.builder()
|
||||
* .name("calculator")
|
||||
* .tool(Tool.builder("calculator",
|
||||
* Map.of("type", "object", "properties",
|
||||
* Map.of("expression", Map.of("type", "string")),
|
||||
* "required", List.of("expression")))
|
||||
* .title("Performs mathematical calculations")
|
||||
* .inputSchema(new JsonSchemaObject()
|
||||
* .required("expression")
|
||||
* .property("expression", JsonSchemaType.STRING))
|
||||
* .build()
|
||||
* .build())
|
||||
* .toolHandler((exchange, req) -> {
|
||||
* String expr = (String) req.arguments().get("expression");
|
||||
* return CallToolResult.builder()
|
||||
* .content(List.of(new McpSchema.TextContent("Result: " + evaluate(expr))))
|
||||
* .content(List.of(McpSchema.TextContent.builder("Result: " + evaluate(expr)).build()))
|
||||
* .isError(false)
|
||||
* .build();
|
||||
* }))
|
||||
* })
|
||||
* .build();
|
||||
* }</pre>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ public class McpStatelessAsyncServer {
|
|||
String content = "Response missing structured content which is expected when calling tool with non-empty outputSchema";
|
||||
logger.warn(content);
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(content)))
|
||||
.content(List.of(McpSchema.TextContent.builder(content).build()))
|
||||
.isError(true)
|
||||
.build();
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ public class McpStatelessAsyncServer {
|
|||
if (!validation.valid()) {
|
||||
logger.warn("Tool call result validation failed: {}", validation.errorMessage());
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(validation.errorMessage())))
|
||||
.content(List.of(McpSchema.TextContent.builder(validation.errorMessage()).build()))
|
||||
.isError(true)
|
||||
.build();
|
||||
}
|
||||
|
|
@ -308,7 +308,7 @@ public class McpStatelessAsyncServer {
|
|||
// https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content
|
||||
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(validation.jsonStructuredOutput())))
|
||||
.content(List.of(McpSchema.TextContent.builder(validation.jsonStructuredOutput()).build()))
|
||||
.isError(result.isError())
|
||||
.structuredContent(result.structuredContent())
|
||||
.build();
|
||||
|
|
@ -393,7 +393,7 @@ public class McpStatelessAsyncServer {
|
|||
List<Tool> tools = this.tools.stream()
|
||||
.map(McpStatelessServerFeatures.AsyncToolSpecification::tool)
|
||||
.toList();
|
||||
return Mono.just(new McpSchema.ListToolsResult(tools, null));
|
||||
return Mono.just(McpSchema.ListToolsResult.builder(tools).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -557,7 +557,7 @@ public class McpStatelessAsyncServer {
|
|||
.stream()
|
||||
.map(McpStatelessServerFeatures.AsyncResourceSpecification::resource)
|
||||
.toList();
|
||||
return Mono.just(new McpSchema.ListResourcesResult(resourceList, null));
|
||||
return Mono.just(McpSchema.ListResourcesResult.builder(resourceList).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -567,7 +567,7 @@ public class McpStatelessAsyncServer {
|
|||
.stream()
|
||||
.map(AsyncResourceTemplateSpecification::resourceTemplate)
|
||||
.toList();
|
||||
return Mono.just(new McpSchema.ListResourceTemplatesResult(resourceList, null));
|
||||
return Mono.just(McpSchema.ListResourceTemplatesResult.builder(resourceList).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -687,7 +687,7 @@ public class McpStatelessAsyncServer {
|
|||
.map(McpStatelessServerFeatures.AsyncPromptSpecification::prompt)
|
||||
.toList();
|
||||
|
||||
return Mono.just(new McpSchema.ListPromptsResult(promptList, null));
|
||||
return Mono.just(McpSchema.ListPromptsResult.builder(promptList).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,10 +71,12 @@ public class McpStatelessServerFeatures {
|
|||
: new McpSchema.ServerCapabilities(null, // completions
|
||||
null, // experimental
|
||||
null, // currently statless server doesn't support set logging
|
||||
!Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
|
||||
!Utils.isEmpty(prompts) ? McpSchema.ServerCapabilities.PromptCapabilities.builder().build()
|
||||
: null,
|
||||
!Utils.isEmpty(resources)
|
||||
? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null,
|
||||
!Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);
|
||||
? McpSchema.ServerCapabilities.ResourceCapabilities.builder().build() : null,
|
||||
!Utils.isEmpty(tools) ? McpSchema.ServerCapabilities.ToolCapabilities.builder().build()
|
||||
: null);
|
||||
|
||||
this.tools = (tools != null) ? tools : List.of();
|
||||
this.resources = (resources != null) ? resources : Map.of();
|
||||
|
|
@ -172,10 +174,12 @@ public class McpStatelessServerFeatures {
|
|||
// logging
|
||||
// by
|
||||
// default
|
||||
!Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
|
||||
!Utils.isEmpty(prompts) ? McpSchema.ServerCapabilities.PromptCapabilities.builder().build()
|
||||
: null,
|
||||
!Utils.isEmpty(resources)
|
||||
? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null,
|
||||
!Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);
|
||||
? McpSchema.ServerCapabilities.ResourceCapabilities.builder().build() : null,
|
||||
!Utils.isEmpty(tools) ? McpSchema.ServerCapabilities.ToolCapabilities.builder().build()
|
||||
: null);
|
||||
|
||||
this.tools = (tools != null) ? tools : new ArrayList<>();
|
||||
this.resources = (resources != null) ? resources : new HashMap<>();
|
||||
|
|
|
|||
|
|
@ -462,8 +462,8 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
|
|||
response.setHeader(HttpHeaders.MCP_SESSION_ID, init.session().getId());
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
|
||||
String jsonResponse = jsonMapper.writeValueAsString(new McpSchema.JSONRPCResponse(
|
||||
McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), initResult, null));
|
||||
String jsonResponse = jsonMapper
|
||||
.writeValueAsString(McpSchema.JSONRPCResponse.result(jsonrpcRequest.id(), initResult));
|
||||
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write(jsonResponse);
|
||||
|
|
|
|||
|
|
@ -154,12 +154,10 @@ public class McpClientSession implements McpSession {
|
|||
|
||||
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError
|
||||
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
|
||||
// TODO: add error message through the data field
|
||||
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
|
||||
error.getMessage(), McpError.aggregateExceptionMessages(error));
|
||||
|
||||
var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
|
||||
jsonRpcError);
|
||||
var errorResponse = McpSchema.JSONRPCResponse.error(request.id(), jsonRpcError);
|
||||
return Mono.just(errorResponse);
|
||||
}).flatMap(this.transport::sendMessage).onErrorComplete(t -> {
|
||||
logger.warn("Issue sending response to the client, ", t);
|
||||
|
|
@ -188,13 +186,13 @@ public class McpClientSession implements McpSession {
|
|||
var handler = this.requestHandlers.get(request.method());
|
||||
if (handler == null) {
|
||||
MethodNotFoundError error = getMethodNotFoundError(request.method());
|
||||
return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
|
||||
new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND,
|
||||
error.message(), error.data())));
|
||||
return Mono
|
||||
.just(McpSchema.JSONRPCResponse.error(request.id(), new McpSchema.JSONRPCResponse.JSONRPCError(
|
||||
McpSchema.ErrorCodes.METHOD_NOT_FOUND, error.message(), error.data())));
|
||||
}
|
||||
|
||||
return handler.handle(request.params())
|
||||
.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null));
|
||||
.map(result -> McpSchema.JSONRPCResponse.result(request.id(), result));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -251,8 +249,7 @@ public class McpClientSession implements McpSession {
|
|||
return Mono.deferContextual(ctx -> Mono.<McpSchema.JSONRPCResponse>create(pendingResponseSink -> {
|
||||
logger.debug("Sending message for method {}", method);
|
||||
this.pendingResponses.put(requestId, pendingResponseSink);
|
||||
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method,
|
||||
requestId, requestParams);
|
||||
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(method, requestId, requestParams);
|
||||
this.transport.sendMessage(jsonrpcRequest).contextWrite(ctx).subscribe(v -> {
|
||||
}, error -> {
|
||||
this.pendingResponses.remove(requestId);
|
||||
|
|
@ -282,8 +279,7 @@ public class McpClientSession implements McpSession {
|
|||
*/
|
||||
@Override
|
||||
public Mono<Void> sendNotification(String method, Object params) {
|
||||
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
|
||||
method, params);
|
||||
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(method, params);
|
||||
return this.transport.sendMessage(jsonrpcNotification);
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -153,8 +153,7 @@ public class McpServerSession implements McpLoggableSession {
|
|||
|
||||
return Mono.<McpSchema.JSONRPCResponse>create(sink -> {
|
||||
this.pendingResponses.put(requestId, sink);
|
||||
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method,
|
||||
requestId, requestParams);
|
||||
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(method, requestId, requestParams);
|
||||
this.transport.sendMessage(jsonrpcRequest).subscribe(v -> {
|
||||
}, error -> {
|
||||
this.pendingResponses.remove(requestId);
|
||||
|
|
@ -177,8 +176,7 @@ public class McpServerSession implements McpLoggableSession {
|
|||
|
||||
@Override
|
||||
public Mono<Void> sendNotification(String method, Object params) {
|
||||
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
|
||||
method, params);
|
||||
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(method, params);
|
||||
return this.transport.sendMessage(jsonrpcNotification);
|
||||
}
|
||||
|
||||
|
|
@ -223,8 +221,7 @@ public class McpServerSession implements McpLoggableSession {
|
|||
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
|
||||
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
|
||||
error.getMessage(), McpError.aggregateExceptionMessages(error));
|
||||
var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
|
||||
jsonRpcError);
|
||||
var errorResponse = McpSchema.JSONRPCResponse.error(request.id(), jsonRpcError);
|
||||
// TODO: Should the error go to SSE or back as POST return?
|
||||
return this.transport.sendMessage(errorResponse).then(Mono.empty());
|
||||
}).flatMap(this.transport::sendMessage);
|
||||
|
|
@ -270,24 +267,21 @@ public class McpServerSession implements McpLoggableSession {
|
|||
var handler = this.requestHandlers.get(request.method());
|
||||
if (handler == null) {
|
||||
MethodNotFoundError error = getMethodNotFoundError(request.method());
|
||||
return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
|
||||
new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND,
|
||||
error.message(), error.data())));
|
||||
return Mono
|
||||
.just(McpSchema.JSONRPCResponse.error(request.id(), new McpSchema.JSONRPCResponse.JSONRPCError(
|
||||
McpSchema.ErrorCodes.METHOD_NOT_FOUND, error.message(), error.data())));
|
||||
}
|
||||
|
||||
resultMono = this.exchangeSink.asMono()
|
||||
.flatMap(exchange -> handler.handle(copyExchange(exchange, transportContext), request.params()));
|
||||
}
|
||||
return resultMono
|
||||
.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null))
|
||||
return resultMono.map(result -> McpSchema.JSONRPCResponse.result(request.id(), result))
|
||||
.onErrorResume(error -> {
|
||||
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError
|
||||
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
|
||||
// TODO: add error message through the data field
|
||||
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
|
||||
error.getMessage(), McpError.aggregateExceptionMessages(error));
|
||||
return Mono.just(
|
||||
new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, jsonRpcError));
|
||||
return Mono.just(McpSchema.JSONRPCResponse.error(request.id(), jsonRpcError));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,24 +190,21 @@ public class McpStreamableServerSession implements McpLoggableSession {
|
|||
// (sink)
|
||||
if (requestHandler == null) {
|
||||
MethodNotFoundError error = getMethodNotFoundError(jsonrpcRequest.method());
|
||||
return transport
|
||||
.sendMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), null,
|
||||
new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND,
|
||||
error.message(), error.data())));
|
||||
return transport.sendMessage(
|
||||
McpSchema.JSONRPCResponse.error(jsonrpcRequest.id(), new McpSchema.JSONRPCResponse.JSONRPCError(
|
||||
McpSchema.ErrorCodes.METHOD_NOT_FOUND, error.message(), error.data())));
|
||||
}
|
||||
return requestHandler
|
||||
.handle(new McpAsyncServerExchange(this.id, stream, clientCapabilities.get(), clientInfo.get(),
|
||||
transportContext), jsonrpcRequest.params())
|
||||
.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), result,
|
||||
null))
|
||||
.map(result -> McpSchema.JSONRPCResponse.result(jsonrpcRequest.id(), result))
|
||||
.onErrorResume(e -> {
|
||||
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (e instanceof McpError mcpError
|
||||
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
|
||||
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
|
||||
e.getMessage(), McpError.aggregateExceptionMessages(e));
|
||||
|
||||
var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(),
|
||||
null, jsonRpcError);
|
||||
var errorResponse = McpSchema.JSONRPCResponse.error(jsonrpcRequest.id(), jsonRpcError);
|
||||
return Mono.just(errorResponse);
|
||||
})
|
||||
.flatMap(transport::sendMessage)
|
||||
|
|
@ -381,8 +378,8 @@ public class McpStreamableServerSession implements McpLoggableSession {
|
|||
|
||||
return Mono.<McpSchema.JSONRPCResponse>create(sink -> {
|
||||
this.pendingResponses.put(requestId, sink);
|
||||
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
|
||||
method, requestId, requestParams);
|
||||
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(method, requestId,
|
||||
requestParams);
|
||||
String messageId = this.uuidGenerator.get();
|
||||
// TODO: store message in history
|
||||
this.transport.sendMessage(jsonrpcRequest, messageId).subscribe(v -> {
|
||||
|
|
@ -407,8 +404,7 @@ public class McpStreamableServerSession implements McpLoggableSession {
|
|||
|
||||
@Override
|
||||
public Mono<Void> sendNotification(String method, Object params) {
|
||||
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(
|
||||
McpSchema.JSONRPC_VERSION, method, params);
|
||||
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(method, params);
|
||||
String messageId = this.uuidGenerator.get();
|
||||
// TODO: store message in history
|
||||
return this.transport.sendMessage(jsonrpcNotification, messageId);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public final class ToolInputValidator {
|
|||
*/
|
||||
public static CallToolResult validate(McpSchema.Tool tool, Map<String, Object> arguments,
|
||||
boolean validateToolInputs, JsonSchemaValidator validator) {
|
||||
if (!validateToolInputs || tool.inputSchema() == null || validator == null) {
|
||||
if (!validateToolInputs || tool.inputSchema() == null || tool.inputSchema().isEmpty() || validator == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> args = arguments != null ? arguments : Map.of();
|
||||
|
|
@ -44,7 +44,7 @@ public final class ToolInputValidator {
|
|||
if (!validation.valid()) {
|
||||
logger.warn("Tool '{}' input validation failed: {}", tool.name(), validation.errorMessage());
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(validation.errorMessage())))
|
||||
.content(List.of(McpSchema.TextContent.builder(validation.errorMessage()).build()))
|
||||
.isError(true)
|
||||
.build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,13 +43,16 @@ class LifecycleInitializerPostInitializationHookTests {
|
|||
private static final McpSchema.ClientCapabilities CLIENT_CAPABILITIES = McpSchema.ClientCapabilities.builder()
|
||||
.build();
|
||||
|
||||
private static final McpSchema.Implementation CLIENT_INFO = new McpSchema.Implementation("test-client", "1.0.0");
|
||||
private static final McpSchema.Implementation CLIENT_INFO = McpSchema.Implementation.builder("test-client", "1.0.0")
|
||||
.build();
|
||||
|
||||
private static final List<String> PROTOCOL_VERSIONS = List.of("1.0.0", "2.0.0");
|
||||
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult("2.0.0",
|
||||
McpSchema.ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"),
|
||||
"Test instructions");
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = McpSchema.InitializeResult
|
||||
.builder("2.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build();
|
||||
|
||||
@Mock
|
||||
private McpClientSession mockClientSession;
|
||||
|
|
|
|||
|
|
@ -44,13 +44,16 @@ class LifecycleInitializerTests {
|
|||
private static final McpSchema.ClientCapabilities CLIENT_CAPABILITIES = McpSchema.ClientCapabilities.builder()
|
||||
.build();
|
||||
|
||||
private static final McpSchema.Implementation CLIENT_INFO = new McpSchema.Implementation("test-client", "1.0.0");
|
||||
private static final McpSchema.Implementation CLIENT_INFO = McpSchema.Implementation.builder("test-client", "1.0.0")
|
||||
.build();
|
||||
|
||||
private static final List<String> PROTOCOL_VERSIONS = List.of("1.0.0", "2.0.0");
|
||||
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult("2.0.0",
|
||||
McpSchema.ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"),
|
||||
"Test instructions");
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = McpSchema.InitializeResult
|
||||
.builder("2.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build();
|
||||
|
||||
@Mock
|
||||
private McpClientSession mockClientSession;
|
||||
|
|
@ -148,10 +151,12 @@ class LifecycleInitializerTests {
|
|||
|
||||
@Test
|
||||
void shouldFailForUnsupportedProtocolVersion() {
|
||||
McpSchema.InitializeResult unsupportedResult = new McpSchema.InitializeResult("999.0.0", // Unsupported
|
||||
// version
|
||||
McpSchema.ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"),
|
||||
"Test instructions");
|
||||
McpSchema.InitializeResult unsupportedResult = McpSchema.InitializeResult.builder("999.0.0", // Unsupported
|
||||
// version
|
||||
McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build();
|
||||
|
||||
when(mockClientSession.sendRequest(eq(McpSchema.METHOD_INITIALIZE), any(), any()))
|
||||
.thenReturn(Mono.just(unsupportedResult));
|
||||
|
|
@ -342,8 +347,11 @@ class LifecycleInitializerTests {
|
|||
|
||||
when(mockClientSession.sendRequest(eq(McpSchema.METHOD_INITIALIZE), any(), any())).thenAnswer(invocation -> {
|
||||
capturedRequest.set((McpSchema.InitializeRequest) invocation.getArgument(1));
|
||||
return Mono.just(new McpSchema.InitializeResult("4.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
new McpSchema.Implementation("test-server", "1.0.0"), "Test instructions"));
|
||||
return Mono.just(McpSchema.InitializeResult
|
||||
.builder("4.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build());
|
||||
});
|
||||
|
||||
StepVerifier.create(initializer.withInitialization("test", init -> Mono.just(init.initializeResult())))
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue