Update documentation and migration notes for v2 (#1024)
Signed-off-by: Dariusz Jędrzejczyk <2554306+chemicL@users.noreply.github.com>
This commit is contained in:
parent
fe82ad508a
commit
275633775c
4 changed files with 273 additions and 191 deletions
256
MIGRATION-2.0.md
256
MIGRATION-2.0.md
|
|
@ -1,81 +1,19 @@
|
|||
# Migration Guide — 2.0
|
||||
|
||||
This document covers breaking and behavioural changes introduced in the 2.0 release of the MCP Java SDK.
|
||||
This guide covers the breaking and behavioural changes introduced in the 2.0 release of the MCP Java SDK, relative to 1.x, and how to update existing code.
|
||||
|
||||
The changes fall into these areas:
|
||||
|
||||
- [Schema construction and required fields](#schema-construction-and-required-fields) — non-null enforcement and the builder API.
|
||||
- [Schema type and shape changes](#schema-type-and-shape-changes) — record component and type changes in `McpSchema`.
|
||||
- [JSON serialization behaviour](#json-serialization-behaviour) — wire-format changes.
|
||||
- [Server-side validation](#server-side-validation) — runtime validation of tool arguments and embedded schemas.
|
||||
- [Transport changes](#transport-changes) — removed methods and the SSE deprecation.
|
||||
- [New features](#new-features) — additive, backward-compatible capabilities.
|
||||
|
||||
---
|
||||
|
||||
## Jackson / JSON serialization changes
|
||||
|
||||
### Sealed interfaces removed
|
||||
|
||||
The following interfaces were `sealed` in 1.x and are now plain interfaces in 2.0:
|
||||
|
||||
- `McpSchema.JSONRPCMessage`
|
||||
- `McpSchema.Request`
|
||||
- `McpSchema.Result`
|
||||
- `McpSchema.Notification`
|
||||
- `McpSchema.ResourceContents`
|
||||
- `McpSchema.CompleteReference`
|
||||
- `McpSchema.Content`
|
||||
|
||||
**Impact:** Exhaustive `switch` expressions or `switch` statements that relied on the sealed hierarchy for completeness checking must add a `default` branch. The compiler will no longer reject switches that omit one of the known subtypes.
|
||||
|
||||
### `CompleteReference` now carries `@JsonTypeInfo`
|
||||
|
||||
`CompleteReference` (and its implementations `PromptReference` and `ResourceReference`) is now annotated with `@JsonTypeInfo(use = NAME, include = EXISTING_PROPERTY, property = "type", visible = true)`. Jackson will automatically dispatch to the correct subtype based on the `"type"` field in the JSON without any hand-written map-walking code.
|
||||
|
||||
**Action:** Remove any custom code that manually inspected the `"type"` field of a completion reference map and instantiated `PromptReference` / `ResourceReference` by hand. A plain `mapper.readValue(json, CompleteRequest.class)` or `mapper.convertValue(paramsMap, CompleteRequest.class)` is sufficient.
|
||||
|
||||
### `Prompt` canonical constructor no longer coerces `null` arguments
|
||||
|
||||
In 1.x, `new Prompt(name, description, null)` silently stored an empty list for `arguments`. In 2.0 it stores `null`.
|
||||
|
||||
**Action:**
|
||||
|
||||
- Code that expected `prompt.arguments()` to return an empty list when not provided will now receive `null`. Add a null-check or use the new `Prompt.withDefaults(name, description, arguments)` factory, which preserves the old behaviour by coercing `null` to `[]`.
|
||||
- On the wire, a prompt without an `arguments` field deserializes with `arguments == null` (it is not coerced to an empty list).
|
||||
|
||||
### `CompleteCompletion` optional fields omitted when null
|
||||
|
||||
`CompleteResult.CompleteCompletion.total` and `CompleteCompletion.hasMore` are now omitted from serialized JSON when they are `null` (previously they were always emitted). Deserializers that required these fields to be present in every response must be updated to treat their absence as `null`.
|
||||
|
||||
### `CompleteCompletion.values` is mandatory in the Java API
|
||||
|
||||
The compact constructor for `CompleteCompletion` asserts that `values` is not `null`. Code that constructed a completion result with a null `values` list will now fail at runtime.
|
||||
|
||||
**Action:** Always pass a non-null list (for example `List.of()` when there are no suggestions).
|
||||
|
||||
### `LoggingLevel` deserialization is lenient
|
||||
|
||||
`LoggingLevel` now uses a `@JsonCreator` factory (`fromValue`) so that JSON string values deserialize in a case-insensitive way. **Unrecognized level strings deserialize to `null`** instead of causing deserialization to fail.
|
||||
|
||||
**Impact:** `SetLevelRequest`, `LoggingMessageNotification`, and any other type that embeds `LoggingLevel` can observe a `null` level when the wire value is unknown or misspelled. Downstream code must null-check or validate before use.
|
||||
|
||||
### `Content.type()` is ignored for Jackson serialization
|
||||
|
||||
The `Content` interface still exposes `type()` as a convenience for Java callers, but the method is annotated with `@JsonIgnore` so Jackson does not treat it as a duplicate `"type"` property alongside `@JsonTypeInfo` on the interface.
|
||||
|
||||
**Impact:** Custom serializers or `ObjectMapper` configuration that relied on serializing `Content` through the default `type()` accessor alone should use the concrete content records (each of which carries a real `"type"` property) or the polymorphic setup on `Content`.
|
||||
|
||||
### `ServerParameters` no longer carries Jackson annotations
|
||||
|
||||
`ServerParameters` (in `client/transport`) has had its `@JsonProperty` and `@JsonInclude` annotations removed. It was never a wire type and is not serialized to JSON in normal SDK usage. If your code serialized or deserialized `ServerParameters` using Jackson, switch to a plain map or a dedicated DTO.
|
||||
|
||||
### Record annotation sweep
|
||||
|
||||
Wire-oriented `public record` types in `McpSchema` consistently use `@JsonInclude(JsonInclude.Include.NON_ABSENT)` (or equivalent per-type configuration) and `@JsonIgnoreProperties(ignoreUnknown = true)`. Nested capability objects under `ClientCapabilities` / `ServerCapabilities` (for example `Sampling`, `Elicitation`, `CompletionCapabilities`, `LoggingCapabilities`, prompt/resource/tool capability records) also ignore unknown JSON properties. This means:
|
||||
|
||||
- **Unknown fields** in incoming JSON are silently ignored, improving forward compatibility with newer server or client versions.
|
||||
- **Absent optional properties** are omitted from outgoing JSON where `NON_ABSENT` applies, and optional Java components deserialize as `null` when missing on the wire.
|
||||
|
||||
### `Tool.inputSchema` is `Map<String, Object>`, not `JsonSchema`
|
||||
|
||||
The `Tool` record now models `inputSchema` (and `outputSchema`) as arbitrary JSON Schema objects as `Map<String, Object>`, so dialect-specific keywords (`$ref`, `unevaluatedProperties`, vendor extensions, and so on) round-trip without being trimmed by a narrow `JsonSchema` record.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Java code that used `Tool.inputSchema()` as a `JsonSchema` must switch to `Map<String, Object>` (or copy into your own schema wrapper).
|
||||
- `Tool.Builder.inputSchema(JsonSchema)` remains as a **deprecated** helper that maps the old record into a map; prefer `inputSchema(Map)` or `inputSchema(McpJsonMapper, String)`.
|
||||
## Schema construction and required fields
|
||||
|
||||
### Required MCP spec fields are enforced at construction time
|
||||
|
||||
|
|
@ -93,30 +31,22 @@ This applies to (non-exhaustive):
|
|||
|
||||
**Action:** Audit any code that constructs these records with potentially-null values and provide valid, non-null arguments.
|
||||
|
||||
**Wire deserialization is lenient.** Records expose a `@JsonCreator fromJson` factory that substitutes safe defaults (e.g. `[]`, `""`, `0`, `INFO`, `Action.CANCEL`) for any absent required field and logs a `WARN` naming the field and the substituted value. `JSONRPCResponse.JSONRPCError` is excluded — malformed JSON-RPC error envelopes still fail immediately.
|
||||
**Wire deserialization stays lenient.** Records expose a `@JsonCreator fromJson` factory that substitutes safe defaults (e.g. `[]`, `""`, `0`, `INFO`, `Action.CANCEL`) for any absent required field and logs a `WARN` naming the field and the substituted value. `JSONRPCResponse.JSONRPCError` is excluded — malformed JSON-RPC error envelopes still fail immediately.
|
||||
|
||||
**Note:** `LoggingMessageNotification`/`SetLevelRequest` default a *missing* `level` to `INFO`, but an *unrecognized* level string still deserializes to `null` (see the `LoggingLevel` section above) and will then fail the canonical constructor. Ensure clients and servers send only recognized level strings.
|
||||
**Note:** `LoggingMessageNotification` / `SetLevelRequest` default a *missing* `level` to `INFO`, but an *unrecognized* level string still deserializes to `null` (see [`LoggingLevel` deserialization is lenient](#logginglevel-deserialization-is-lenient)) and will then fail the canonical constructor. Ensure clients and servers send only recognized level strings.
|
||||
|
||||
### `PromptReference` discriminator pinning and equality
|
||||
### `Prompt` no longer coerces `null` arguments
|
||||
|
||||
`PromptReference` keeps its `(type, name, title)` record components, so positional construction from 1.x still compiles. Two behavioural changes:
|
||||
In 1.x, `new Prompt(name, description, null)` silently stored an empty list for `arguments`. In 2.0 it stores `null`.
|
||||
|
||||
- The compact constructor pins `type` to `ref/prompt`. Any non-null value other than `ref/prompt` is replaced with `ref/prompt` and a `WARN` is logged. The legacy two-arg `PromptReference(String type, String name)` constructor remains `@Deprecated` and routes through the canonical constructor, so it triggers the same WARN.
|
||||
- `equals`/`hashCode` now consider `name` only (title and type are ignored). Two refs with the same name but different titles compare equal.
|
||||
**Action:**
|
||||
|
||||
**Action:** Audit any code that used `PromptReference` as a map key or in a `Set` — equality semantics changed. If your code constructed instances with a custom `type` string for testing, switch to `PromptReference.builder(name)` (or `new PromptReference(name)`); the WARN tells you which call sites still pass the discriminator.
|
||||
|
||||
`CompleteReference.identifier()` is `@Deprecated` and now returns `null` via a default method on the interface.
|
||||
|
||||
### `ResourceReference` record component reduced
|
||||
|
||||
Components changed from `(type, uri)` to `(uri)`. Positional construction with two arguments breaks. The legacy `ResourceReference(String type, String uri)` constructor stays `@Deprecated`; it ignores `type` and logs a `WARN`. Use `new ResourceReference(uri)` or `ResourceReference.builder(uri)`. The `type()` accessor still returns `ref/resource` and Jackson serializes it via `@JsonProperty("type")` on the accessor.
|
||||
- Code that expected `prompt.arguments()` to return an empty list when not provided will now receive `null`. Add a null-check.
|
||||
- On the wire, a prompt without an `arguments` field deserializes with `arguments == null` (it is not coerced to an empty list).
|
||||
|
||||
### Builder API: required-first factories; old setters/no-arg builders deprecated
|
||||
|
||||
Most records that have a builder have gained a required-first factory method (`builder(req1, req2, …)`) and the corresponding setters for required fields are removed from the builder. The old no-arg `builder()` factory and public no-arg `Builder()` constructor are kept but `@Deprecated` where they would allow constructing a builder without required state.
|
||||
|
||||
Examples:
|
||||
Most records that have a builder gained a required-first factory method (`builder(req1, req2, …)`). The old no-arg `builder()` factory, the public no-arg `Builder()` constructor, and the setters for the now-required fields are kept but `@Deprecated`. They still compile, so 1.x code keeps working with deprecation warnings; migrate to the required-first factories to clear them.
|
||||
|
||||
| Type | Old (deprecated) | New |
|
||||
|------|-----------------|-----|
|
||||
|
|
@ -124,7 +54,7 @@ Examples:
|
|||
| `ResourceTemplate` | `ResourceTemplate.builder().uriTemplate(u).name(n)…` | `ResourceTemplate.builder(uriTemplate, name)…` |
|
||||
| `Implementation` | `new Implementation(name, version)` | `Implementation.builder(name, version)…` |
|
||||
| `InitializeRequest` / `InitializeResult` | `… .builder()…` | `… .builder(protocolVersion, capabilities, clientInfo/serverInfo)` |
|
||||
| `Tool` | `Tool.builder().name(n)…` | `Tool.builder(name)…` |
|
||||
| `Tool` | `Tool.builder().name(n).inputSchema(s)…` | `Tool.builder(name, inputSchemaMap)…` or `Tool.builder(name, jsonMapper, inputSchemaJson)…` |
|
||||
| `Prompt` / `PromptArgument` / `GetPromptRequest` | `… .builder().name(n)…` | `… .builder(name)…` |
|
||||
| `PromptMessage` / `SamplingMessage` | `… .builder().role(r).content(c)…` | `… .builder(role, content)…` |
|
||||
| `CreateMessageRequest` | `… .builder().messages(m).maxTokens(n)…` | `… .builder(messages, maxTokens)…` |
|
||||
|
|
@ -133,15 +63,105 @@ Examples:
|
|||
| `ListResourcesResult` / `ListResourceTemplatesResult` / `ListPromptsResult` / `ListToolsResult` / `ListRootsResult` | `… .builder()…` | `… .builder(items)…` |
|
||||
| `ReadResourceRequest` / `SubscribeRequest` / `UnsubscribeRequest` / `ResourcesUpdatedNotification` / `Root` | n/a | `… .builder(uri)…` |
|
||||
| `ReadResourceResult` | n/a | `ReadResourceResult.builder(contents)…` |
|
||||
| `TextResourceContents` / `BlobResourceContents` | n/a | `… .builder(uri, text|blob)…` |
|
||||
| `GetPromptResult` | `new GetPromptResult(description, messages)` | `GetPromptResult.builder(messages).description(d)…` |
|
||||
| `TextResourceContents` / `BlobResourceContents` | n/a | `… .builder(uri, text\|blob)…` |
|
||||
| `TextContent` / `ImageContent` / `AudioContent` / `EmbeddedResource` | n/a | `… .builder(text \| data, mimeType \| resource)…` |
|
||||
| `CallToolResult` | unchanged | also: required-first content set via builder constructor remains optional |
|
||||
| `ProgressNotification` | n/a | `ProgressNotification.builder(progressToken, progress)` |
|
||||
| `JSONRPCResponse.JSONRPCError` | n/a | `JSONRPCError.builder(code, message)` |
|
||||
| `CompleteRequest` | n/a | `CompleteRequest.builder(ref, argument)` |
|
||||
| `Annotations` | n/a | `Annotations.builder()` |
|
||||
| Capabilities (`Sampling`, `Elicitation`, `Roots`, `LoggingCapabilities`, `CompletionCapabilities`, prompt/resource/tool capabilities) | n/a | `… .builder()…` |
|
||||
|
||||
---
|
||||
|
||||
## Schema type and shape changes
|
||||
|
||||
### `Tool.inputSchema` is `Map<String, Object>`, not `JsonSchema`
|
||||
|
||||
The `Tool` record now models `inputSchema` (and `outputSchema`) as arbitrary JSON Schema objects of type `Map<String, Object>`, so dialect-specific keywords (`$ref`, `unevaluatedProperties`, vendor extensions, and so on) round-trip without being trimmed by a narrow `JsonSchema` record.
|
||||
|
||||
**Action:**
|
||||
|
||||
- Java code that used `Tool.inputSchema()` as a `JsonSchema` must switch to `Map<String, Object>` (or copy into your own schema wrapper).
|
||||
- `Tool.Builder.inputSchema(JsonSchema)` remains as a **deprecated** helper that maps the old record into a map; prefer `inputSchema(Map)` or `inputSchema(McpJsonMapper, String)`.
|
||||
|
||||
### Sealed interfaces removed
|
||||
|
||||
The following interfaces were `sealed` in 1.x and are now plain interfaces in 2.0:
|
||||
|
||||
- `McpSchema.JSONRPCMessage`
|
||||
- `McpSchema.Request`
|
||||
- `McpSchema.Result`
|
||||
- `McpSchema.Notification`
|
||||
- `McpSchema.ResourceContents`
|
||||
- `McpSchema.CompleteReference`
|
||||
- `McpSchema.Content`
|
||||
|
||||
**Impact:** Exhaustive `switch` expressions or statements that relied on the sealed hierarchy for completeness checking must add a `default` branch. The compiler will no longer reject switches that omit one of the known subtypes.
|
||||
|
||||
### `CompleteReference` polymorphic deserialization
|
||||
|
||||
`CompleteReference` (and its implementations `PromptReference` and `ResourceReference`) is now annotated with `@JsonTypeInfo(use = NAME, include = EXISTING_PROPERTY, property = "type", visible = true)`. Jackson dispatches to the correct subtype based on the `"type"` field automatically.
|
||||
|
||||
**Action:** Remove any custom code that manually inspected the `"type"` field of a completion reference map and instantiated `PromptReference` / `ResourceReference` by hand. A plain `mapper.readValue(json, CompleteRequest.class)` or `mapper.convertValue(paramsMap, CompleteRequest.class)` is sufficient.
|
||||
|
||||
`CompleteReference.identifier()` is `@Deprecated` and now returns `null` via a default method on the interface.
|
||||
|
||||
### `PromptReference` discriminator pinning and equality
|
||||
|
||||
`PromptReference` keeps its `(type, name, title)` record components, so positional construction from 1.x still compiles, with two behavioural changes:
|
||||
|
||||
- The compact constructor pins `type` to `ref/prompt`. Any non-null value other than `ref/prompt` is replaced and a `WARN` is logged. The legacy two-arg `PromptReference(String type, String name)` constructor remains `@Deprecated` and routes through the canonical constructor, so it triggers the same WARN.
|
||||
- `equals`/`hashCode` now consider `name` only (title and type are ignored). Two refs with the same name but different titles compare equal.
|
||||
|
||||
**Action:** Audit any code that used `PromptReference` as a map key or in a `Set` — equality semantics changed. If you constructed instances with a custom `type` string, switch to `PromptReference.builder(name)` (or `new PromptReference(name)`); the WARN identifies the call sites still passing a discriminator.
|
||||
|
||||
### `ResourceReference` record component reduced
|
||||
|
||||
Components changed from `(type, uri)` to `(uri)`. Positional construction with two arguments breaks. The legacy `ResourceReference(String type, String uri)` constructor stays `@Deprecated`; it ignores `type` and logs a `WARN`. Use `new ResourceReference(uri)` or `ResourceReference.builder(uri)`. The `type()` accessor still returns `ref/resource` and Jackson serializes it via `@JsonProperty("type")` on the accessor.
|
||||
|
||||
### `ElicitRequest` is now an interface
|
||||
|
||||
To support URL-mode elicitation (see [New features](#new-features)), the elicitation request type was split:
|
||||
|
||||
- `ElicitRequest` changed from a `record` to an `interface`.
|
||||
- The original form-based request record is now `ElicitFormRequest`.
|
||||
- The `McpClient` builder `elicitation(...)` methods now accept a handler over `ElicitFormRequest` instead of `ElicitRequest`.
|
||||
|
||||
**Action:** Replace references to the old `ElicitRequest` record (construction, `instanceof`, handler signatures) with `ElicitFormRequest`. Code that only referred to `ElicitRequest` as a type continues to compile against the new interface.
|
||||
|
||||
### `ServerParameters` no longer carries Jackson annotations
|
||||
|
||||
`ServerParameters` (in `client/transport`) has had its `@JsonProperty` and `@JsonInclude` annotations removed. It was never a wire type and is not serialized to JSON in normal SDK usage. If your code serialized or deserialized `ServerParameters` using Jackson, switch to a plain map or a dedicated DTO.
|
||||
|
||||
---
|
||||
|
||||
## JSON serialization behaviour
|
||||
|
||||
### Unknown JSON fields are ignored
|
||||
|
||||
Wire-oriented `public record` types in `McpSchema` consistently use `@JsonInclude(JsonInclude.Include.NON_ABSENT)` and `@JsonIgnoreProperties(ignoreUnknown = true)`. Nested capability objects under `ClientCapabilities` / `ServerCapabilities` (for example `Sampling`, `Elicitation`, `CompletionCapabilities`, `LoggingCapabilities`, and the prompt/resource/tool capability records) also ignore unknown JSON properties. As a result:
|
||||
|
||||
- **Unknown fields** in incoming JSON are silently ignored, improving forward compatibility with newer server or client versions.
|
||||
- **Absent optional properties** are omitted from outgoing JSON where `NON_ABSENT` applies, and optional Java components deserialize as `null` when missing on the wire.
|
||||
|
||||
### `CompleteCompletion` field handling
|
||||
|
||||
- `CompleteResult.CompleteCompletion.total` and `CompleteCompletion.hasMore` are now omitted from serialized JSON when `null` (previously they were always emitted). Deserializers that required these fields to be present must treat their absence as `null`.
|
||||
- The compact constructor asserts that `values` is not `null`. **Action:** always pass a non-null list (for example `List.of()` when there are no suggestions).
|
||||
|
||||
### `LoggingLevel` deserialization is lenient
|
||||
|
||||
`LoggingLevel` now uses a `@JsonCreator` factory (`fromValue`) so JSON string values deserialize case-insensitively. **Unrecognized level strings deserialize to `null`** instead of failing.
|
||||
|
||||
**Impact:** `SetLevelRequest`, `LoggingMessageNotification`, and any other type embedding `LoggingLevel` can observe a `null` level when the wire value is unknown or misspelled. Downstream code must null-check or validate before use.
|
||||
|
||||
### `Content.type()` is ignored for Jackson serialization
|
||||
|
||||
The `Content` interface still exposes `type()` as a convenience for Java callers, but the method is annotated with `@JsonIgnore` so Jackson does not treat it as a duplicate `"type"` property alongside `@JsonTypeInfo` on the interface.
|
||||
|
||||
**Impact:** Custom serializers or `ObjectMapper` configuration that relied on serializing `Content` through the `type()` accessor alone should use the concrete content records (each of which carries a real `"type"` property) or the polymorphic setup on `Content`.
|
||||
|
||||
### JSON-RPC envelope ergonomics
|
||||
|
||||
In 1.x, every envelope was constructed via the canonical record constructor and the literal `"2.0"` `jsonrpc` string had to be threaded through every call site:
|
||||
|
|
@ -164,12 +184,58 @@ JSONRPCResponse.result(id, result);
|
|||
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.
|
||||
`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. The 1.x canonical 4-arg constructors continue to compile.
|
||||
|
||||
The 1.x canonical 4-arg constructors continue to compile.
|
||||
---
|
||||
|
||||
### Optional JSON Schema validation on `tools/call` (server)
|
||||
## Server-side validation
|
||||
|
||||
### Optional JSON Schema validation on `tools/call`
|
||||
|
||||
When a `JsonSchemaValidator` is available (including the default from `McpJsonDefaults.getSchemaValidator()` when you do not configure one explicitly) and `validateToolInputs` is left at its default of `true`, the server validates incoming tool arguments against `tool.inputSchema()` before invoking the tool. Failed validation produces a `CallToolResult` with `isError` set and a textual error in the content.
|
||||
|
||||
**Action:** Ensure `inputSchema` maps are valid for your validator, tighten client arguments, or disable validation with `validateToolInputs(false)` on the server builder if you must preserve pre-2.0 behaviour.
|
||||
|
||||
### Embedded JSON Schemas are validated against 2020-12 (SEP-1613)
|
||||
|
||||
The JSON Schema documents that MCP embeds — `Tool.inputSchema`, `Tool.outputSchema`, and `ElicitRequest.requestedSchema` — are now validated against the JSON Schema 2020-12 meta-schema by default. Servers reject malformed schemas at **build time** (`McpServer.build()`) and at **runtime** (`addTool()`) with an `IllegalArgumentException` that names the offending field and references SEP-1613. Elicitation requests whose `requestedSchema` violates the meta-schema are rejected before being sent to the client.
|
||||
|
||||
Schemas that explicitly declare a different dialect via `$schema` are accepted without meta-schema validation — 2020-12 is the default, not the only permitted dialect.
|
||||
|
||||
**Action:** Make embedded schemas valid 2020-12 documents, or set an explicit `$schema` to opt into a different dialect.
|
||||
|
||||
---
|
||||
|
||||
## Transport changes
|
||||
|
||||
### `customizeRequest()` removed from the HttpClient transport builders
|
||||
|
||||
The deprecated `Builder.customizeRequest(Consumer<HttpRequest.Builder>)` method on `HttpClientSseClientTransport` and `HttpClientStreamableHttpTransport` has been removed.
|
||||
|
||||
**Action:** Use `requestBuilder(HttpRequest.Builder)` for static request setup, or `httpRequestCustomizer(McpSyncHttpClientRequestCustomizer)` for per-request customization.
|
||||
|
||||
### SSE transports are deprecated
|
||||
|
||||
The HTTP+SSE client and server transports (and their supporting validator/exception types) are deprecated in favour of Streamable HTTP — `HttpClientStreamableHttpTransport` on the client, and `HttpServletStreamableServerTransportProvider` on the server. They still work; plan a move to Streamable HTTP.
|
||||
|
||||
---
|
||||
|
||||
## New features
|
||||
|
||||
These are additive and backward-compatible.
|
||||
|
||||
### URL elicitation (SEP-1036)
|
||||
|
||||
Servers can request out-of-band URL input from users (for example payment or API-key flows) during tool execution. Adds `ElicitUrlRequest`, `urlElicitation()` / `elicitationCompleteConsumer(s)()` builder methods on `McpClient`, `sendElicitationComplete()` on `McpAsyncServer`/`McpSyncServer`, the `ElicitationCompleteNotification` record, and the `URL_ELICITATION_REQUIRED` error code. See the [`ElicitRequest` interface change](#elicitrequest-is-now-an-interface) for the related breaking change.
|
||||
|
||||
### Client-side elicitation defaults (SEP-1034)
|
||||
|
||||
A new opt-in `McpClient` builder option `applyElicitationDefaults(boolean)` fills missing keys of an accepted `ElicitResult.content` with the `default` values declared in the request's `requestedSchema` before returning the result to the server. It is a local client config, not a wire capability.
|
||||
|
||||
### Icons and metadata (SEP-973)
|
||||
|
||||
A new `Icon` record and an optional `icons` field were added to `Implementation`, `Resource`, `ResourceTemplate`, `Prompt`, and `Tool`. `Implementation` also gains optional `description` and `websiteUrl` fields. All fields are optional; existing constructors and builders are unchanged.
|
||||
|
||||
### `_meta` on paginated list queries
|
||||
|
||||
The client list operations accept an optional `_meta` map alongside the pagination cursor: `listResources(String cursor, Map<String, Object> meta)`, `listResourceTemplates(...)`, `listPrompts(...)`, and `listTools(...)`.
|
||||
|
|
|
|||
|
|
@ -47,20 +47,21 @@ The client provides both synchronous and asynchronous APIs for flexibility in di
|
|||
|
||||
// Call a tool
|
||||
CallToolResult result = client.callTool(
|
||||
new CallToolRequest("calculator",
|
||||
Map.of("operation", "add", "a", 2, "b", 3))
|
||||
CallToolRequest.builder("calculator")
|
||||
.arguments(Map.of("operation", "add", "a", 2, "b", 3))
|
||||
.build()
|
||||
);
|
||||
|
||||
// List and read resources
|
||||
ListResourcesResult resources = client.listResources();
|
||||
ReadResourceResult resource = client.readResource(
|
||||
new ReadResourceRequest("resource://uri")
|
||||
ReadResourceRequest.builder("resource://uri").build()
|
||||
);
|
||||
|
||||
// List and use prompts
|
||||
ListPromptsResult prompts = client.listPrompts();
|
||||
GetPromptResult prompt = client.getPrompt(
|
||||
new GetPromptRequest("greeting", Map.of("name", "Spring"))
|
||||
GetPromptRequest.builder("greeting").arguments(Map.of("name", "Spring")).build()
|
||||
);
|
||||
|
||||
// Add/remove roots
|
||||
|
|
@ -102,24 +103,22 @@ The client provides both synchronous and asynchronous APIs for flexibility in di
|
|||
client.initialize()
|
||||
.flatMap(initResult -> client.listTools())
|
||||
.flatMap(tools -> {
|
||||
return client.callTool(new CallToolRequest(
|
||||
"calculator",
|
||||
Map.of("operation", "add", "a", 2, "b", 3)
|
||||
));
|
||||
return client.callTool(CallToolRequest.builder("calculator")
|
||||
.arguments(Map.of("operation", "add", "a", 2, "b", 3))
|
||||
.build());
|
||||
})
|
||||
.flatMap(result -> {
|
||||
return client.listResources()
|
||||
.flatMap(resources ->
|
||||
client.readResource(new ReadResourceRequest("resource://uri"))
|
||||
client.readResource(ReadResourceRequest.builder("resource://uri").build())
|
||||
);
|
||||
})
|
||||
.flatMap(resource -> {
|
||||
return client.listPrompts()
|
||||
.flatMap(prompts ->
|
||||
client.getPrompt(new GetPromptRequest(
|
||||
"greeting",
|
||||
Map.of("name", "Spring")
|
||||
))
|
||||
client.getPrompt(GetPromptRequest.builder("greeting")
|
||||
.arguments(Map.of("name", "Spring"))
|
||||
.build())
|
||||
);
|
||||
})
|
||||
.flatMap(prompt -> {
|
||||
|
|
@ -144,7 +143,7 @@ Creates transport for process-based communication using stdin/stdout:
|
|||
ServerParameters params = ServerParameters.builder("npx")
|
||||
.args("-y", "@modelcontextprotocol/server-everything", "dir")
|
||||
.build();
|
||||
McpTransport transport = new StdioClientTransport(params);
|
||||
McpTransport transport = new StdioClientTransport(params, McpJsonDefaults.getMapper());
|
||||
```
|
||||
|
||||
### Streamable HTTP
|
||||
|
|
@ -184,7 +183,7 @@ McpTransport transport = new StdioClientTransport(params);
|
|||
Creates a framework-agnostic (pure Java API) SSE client transport. Included in the core `mcp` module:
|
||||
|
||||
```java
|
||||
McpTransport transport = new HttpClientSseClientTransport("http://your-mcp-server");
|
||||
McpTransport transport = HttpClientSseClientTransport.builder("http://your-mcp-server").build();
|
||||
```
|
||||
=== "SSE WebClient (external)"
|
||||
|
||||
|
|
@ -301,6 +300,17 @@ The `ElicitResult` supports three actions:
|
|||
- `DECLINE` - The user declined to provide the information
|
||||
- `CANCEL` - The operation was cancelled
|
||||
|
||||
You can optionally have the client fill in missing values from the schema's `default` declarations before returning an accepted result to the server:
|
||||
|
||||
```java
|
||||
var client = McpClient.sync(transport)
|
||||
.applyElicitationDefaults(true) // default is false
|
||||
.elicitation(formElicitationHandler)
|
||||
.build();
|
||||
```
|
||||
|
||||
When enabled, any keys absent from an accepted `ElicitResult.content` are populated with the `default` values declared in the request's `requestedSchema`.
|
||||
|
||||
#### URL Elicitation Required Handling
|
||||
|
||||
When a server requires out-of-band URL elicitation but the client has not negotiated support for it (or the server strictly requires out-of-band handling), the server may return a `URL_ELICITATION_REQUIRED` error during tool execution or prompt retrieval.
|
||||
|
|
@ -339,7 +349,7 @@ mcpClient.initialize();
|
|||
mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO);
|
||||
|
||||
// Call the tool that sends logging notifications
|
||||
CallToolResult result = mcpClient.callTool(new CallToolRequest("logging-test", Map.of()));
|
||||
CallToolResult result = mcpClient.callTool(CallToolRequest.builder("logging-test").build());
|
||||
```
|
||||
|
||||
Clients can control the minimum logging level they receive through the `mcpClient.setLoggingLevel(level)` request. Messages below the set level will be filtered out.
|
||||
|
|
@ -371,11 +381,13 @@ Tools are server-side functions that clients can discover and execute. The MCP c
|
|||
|
||||
// Call a tool with a CallToolRequest
|
||||
CallToolResult result = client.callTool(
|
||||
new CallToolRequest("calculator", Map.of(
|
||||
"operation", "add",
|
||||
"a", 1,
|
||||
"b", 2
|
||||
))
|
||||
CallToolRequest.builder("calculator")
|
||||
.arguments(Map.of(
|
||||
"operation", "add",
|
||||
"a", 1,
|
||||
"b", 2
|
||||
))
|
||||
.build()
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -389,11 +401,13 @@ Tools are server-side functions that clients can discover and execute. The MCP c
|
|||
.subscribe();
|
||||
|
||||
// Call a tool asynchronously
|
||||
client.callTool(new CallToolRequest("calculator", Map.of(
|
||||
"operation", "add",
|
||||
"a", 1,
|
||||
"b", 2
|
||||
)))
|
||||
client.callTool(CallToolRequest.builder("calculator")
|
||||
.arguments(Map.of(
|
||||
"operation", "add",
|
||||
"a", 1,
|
||||
"b", 2
|
||||
))
|
||||
.build())
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
|
|
@ -420,7 +434,7 @@ Resources represent server-side data sources that clients can access using URI t
|
|||
|
||||
// Read a resource
|
||||
ReadResourceResult resource = client.readResource(
|
||||
new ReadResourceRequest("resource://uri")
|
||||
ReadResourceRequest.builder("resource://uri").build()
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -434,7 +448,7 @@ Resources represent server-side data sources that clients can access using URI t
|
|||
.subscribe();
|
||||
|
||||
// Read a resource asynchronously
|
||||
client.readResource(new ReadResourceRequest("resource://uri"))
|
||||
client.readResource(ReadResourceRequest.builder("resource://uri").build())
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
|
|
@ -457,10 +471,10 @@ Register a consumer on the client builder, then subscribe/unsubscribe at any tim
|
|||
client.initialize();
|
||||
|
||||
// Subscribe to a specific resource URI
|
||||
client.subscribeResource(new McpSchema.SubscribeRequest("custom://resource"));
|
||||
client.subscribeResource(McpSchema.SubscribeRequest.builder("custom://resource").build());
|
||||
|
||||
// ... later, stop receiving updates
|
||||
client.unsubscribeResource(new McpSchema.UnsubscribeRequest("custom://resource"));
|
||||
client.unsubscribeResource(McpSchema.UnsubscribeRequest.builder("custom://resource").build());
|
||||
```
|
||||
|
||||
=== "Async API"
|
||||
|
|
@ -473,11 +487,11 @@ Register a consumer on the client builder, then subscribe/unsubscribe at any tim
|
|||
.build();
|
||||
|
||||
client.initialize()
|
||||
.then(client.subscribeResource(new McpSchema.SubscribeRequest("custom://resource")))
|
||||
.then(client.subscribeResource(McpSchema.SubscribeRequest.builder("custom://resource").build()))
|
||||
.subscribe();
|
||||
|
||||
// ... later, stop receiving updates
|
||||
client.unsubscribeResource(new McpSchema.UnsubscribeRequest("custom://resource"))
|
||||
client.unsubscribeResource(McpSchema.UnsubscribeRequest.builder("custom://resource").build())
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
|
|
@ -493,7 +507,7 @@ The prompt system enables interaction with server-side prompt templates. These t
|
|||
|
||||
// Get a prompt with parameters
|
||||
GetPromptResult prompt = client.getPrompt(
|
||||
new GetPromptRequest("greeting", Map.of("name", "World"))
|
||||
GetPromptRequest.builder("greeting").arguments(Map.of("name", "World")).build()
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -507,6 +521,6 @@ The prompt system enables interaction with server-side prompt templates. These t
|
|||
.subscribe();
|
||||
|
||||
// Get a prompt asynchronously
|
||||
client.getPrompt(new GetPromptRequest("greeting", Map.of("name", "World")))
|
||||
client.getPrompt(GetPromptRequest.builder("greeting").arguments(Map.of("name", "World")).build())
|
||||
.subscribe();
|
||||
```
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ Add the BOM to your project:
|
|||
<dependency>
|
||||
<groupId>io.modelcontextprotocol.sdk</groupId>
|
||||
<artifactId>mcp-bom</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>2.0.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
|
@ -135,7 +135,7 @@ Add the BOM to your project:
|
|||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation platform("io.modelcontextprotocol.sdk:mcp-bom:1.0.0")
|
||||
implementation platform("io.modelcontextprotocol.sdk:mcp-bom:2.0.0")
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
|
|
|||
134
docs/server.md
134
docs/server.md
|
|
@ -111,7 +111,7 @@ Create process-based transport using stdin/stdout:
|
|||
|
||||
```java
|
||||
StdioServerTransportProvider transportProvider =
|
||||
new StdioServerTransportProvider(new ObjectMapper());
|
||||
new StdioServerTransportProvider(McpJsonDefaults.getMapper());
|
||||
```
|
||||
|
||||
Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support.
|
||||
|
|
@ -237,7 +237,9 @@ Key features:
|
|||
|
||||
@Bean
|
||||
public HttpServletSseServerTransportProvider servletSseServerTransportProvider() {
|
||||
return new HttpServletSseServerTransportProvider(new ObjectMapper(), "/mcp/message");
|
||||
return HttpServletSseServerTransportProvider.builder()
|
||||
.messageEndpoint("/mcp/message")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -340,10 +342,8 @@ The recommended approach is to use the builder pattern and `CallToolRequest` as
|
|||
```java
|
||||
// Sync tool specification using builder
|
||||
var syncToolSpecification = SyncToolSpecification.builder()
|
||||
.tool(Tool.builder()
|
||||
.name("calculator")
|
||||
.tool(Tool.builder("calculator", schema)
|
||||
.description("Basic calculator")
|
||||
.inputSchema(schema)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// Access arguments via request.arguments()
|
||||
|
|
@ -363,10 +363,8 @@ The recommended approach is to use the builder pattern and `CallToolRequest` as
|
|||
```java
|
||||
// Async tool specification using builder
|
||||
var asyncToolSpecification = AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder()
|
||||
.name("calculator")
|
||||
.tool(Tool.builder("calculator", schema)
|
||||
.description("Basic calculator")
|
||||
.inputSchema(schema)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// Access arguments via request.arguments()
|
||||
|
|
@ -389,7 +387,7 @@ You can also register tools directly on the server builder using the `toolCall`
|
|||
```java
|
||||
var server = McpServer.sync(transportProvider)
|
||||
.toolCall(
|
||||
Tool.builder().name("echo").description("Echoes input").inputSchema(schema).build(),
|
||||
Tool.builder("echo", schema).description("Echoes input").build(),
|
||||
(exchange, request) -> CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent(request.arguments().get("text").toString())))
|
||||
.build()
|
||||
|
|
@ -397,6 +395,18 @@ var server = McpServer.sync(transportProvider)
|
|||
.build();
|
||||
```
|
||||
|
||||
#### Tool Input Validation
|
||||
|
||||
By default the server validates incoming tool arguments against the tool's `inputSchema` before invoking the handler. When validation fails, the call returns a `CallToolResult` with `isError` set and a textual error, rather than reaching your handler. Validation uses the configured `JsonSchemaValidator` (or the default from `McpJsonDefaults.getSchemaValidator()`), and can be turned off on the server builder:
|
||||
|
||||
```java
|
||||
var server = McpServer.sync(transportProvider)
|
||||
.validateToolInputs(false) // default is true
|
||||
.build();
|
||||
```
|
||||
|
||||
The embedded JSON Schema documents themselves (`Tool.inputSchema`, `Tool.outputSchema`, and elicitation `requestedSchema`) are validated against the JSON Schema 2020-12 meta-schema (SEP-1613). Malformed schemas are rejected at build time (`McpServer.build()`) and when calling `addTool()`, throwing an `IllegalArgumentException` that names the offending field. A schema that declares a different dialect via `$schema` is accepted without meta-schema validation.
|
||||
|
||||
### Resource Specification
|
||||
|
||||
Specification of a resource with its handler function.
|
||||
|
|
@ -407,15 +417,13 @@ Resources provide context to AI models by exposing data such as: File contents,
|
|||
```java
|
||||
// Sync resource specification
|
||||
var syncResourceSpecification = new McpServerFeatures.SyncResourceSpecification(
|
||||
Resource.builder()
|
||||
.uri("custom://resource")
|
||||
.name("name")
|
||||
Resource.builder("custom://resource", "name")
|
||||
.description("description")
|
||||
.mimeType("text/plain")
|
||||
.build(),
|
||||
(exchange, request) -> {
|
||||
// Resource read implementation
|
||||
return new ReadResourceResult(contents);
|
||||
return ReadResourceResult.builder(contents).build();
|
||||
}
|
||||
);
|
||||
```
|
||||
|
|
@ -425,15 +433,13 @@ Resources provide context to AI models by exposing data such as: File contents,
|
|||
```java
|
||||
// Async resource specification
|
||||
var asyncResourceSpecification = new McpServerFeatures.AsyncResourceSpecification(
|
||||
Resource.builder()
|
||||
.uri("custom://resource")
|
||||
.name("name")
|
||||
Resource.builder("custom://resource", "name")
|
||||
.description("description")
|
||||
.mimeType("text/plain")
|
||||
.build(),
|
||||
(exchange, request) -> {
|
||||
// Resource read implementation
|
||||
return Mono.just(new ReadResourceResult(contents));
|
||||
return Mono.just(ReadResourceResult.builder(contents).build());
|
||||
}
|
||||
);
|
||||
```
|
||||
|
|
@ -481,15 +487,13 @@ Resource templates allow servers to expose parameterized resources using URI tem
|
|||
```java
|
||||
// Resource template specification
|
||||
var resourceTemplateSpec = new McpServerFeatures.SyncResourceTemplateSpecification(
|
||||
ResourceTemplate.builder()
|
||||
.uriTemplate("file://{path}")
|
||||
.name("File Resource")
|
||||
ResourceTemplate.builder("file://{path}", "File Resource")
|
||||
.description("Access files by path")
|
||||
.mimeType("application/octet-stream")
|
||||
.build(),
|
||||
(exchange, request) -> {
|
||||
// Read the file at the requested URI
|
||||
return new ReadResourceResult(contents);
|
||||
return ReadResourceResult.builder(contents).build();
|
||||
}
|
||||
);
|
||||
```
|
||||
|
|
@ -504,12 +508,18 @@ The Prompt Specification is a structured template for AI model interactions that
|
|||
```java
|
||||
// Sync prompt specification
|
||||
var syncPromptSpecification = new McpServerFeatures.SyncPromptSpecification(
|
||||
new Prompt("greeting", "description", List.of(
|
||||
new PromptArgument("name", "description", true)
|
||||
)),
|
||||
Prompt.builder("greeting")
|
||||
.description("description")
|
||||
.arguments(List.of(
|
||||
PromptArgument.builder("name")
|
||||
.description("description")
|
||||
.required(true)
|
||||
.build()
|
||||
))
|
||||
.build(),
|
||||
(exchange, request) -> {
|
||||
// Prompt implementation
|
||||
return new GetPromptResult(description, messages);
|
||||
return GetPromptResult.builder(messages).description(description).build();
|
||||
}
|
||||
);
|
||||
```
|
||||
|
|
@ -519,12 +529,18 @@ The Prompt Specification is a structured template for AI model interactions that
|
|||
```java
|
||||
// Async prompt specification
|
||||
var asyncPromptSpecification = new McpServerFeatures.AsyncPromptSpecification(
|
||||
new Prompt("greeting", "description", List.of(
|
||||
new PromptArgument("name", "description", true)
|
||||
)),
|
||||
Prompt.builder("greeting")
|
||||
.description("description")
|
||||
.arguments(List.of(
|
||||
PromptArgument.builder("name")
|
||||
.description("description")
|
||||
.required(true)
|
||||
.build()
|
||||
))
|
||||
.build(),
|
||||
(exchange, request) -> {
|
||||
// Prompt implementation
|
||||
return Mono.just(new GetPromptResult(description, messages));
|
||||
return Mono.just(GetPromptResult.builder(messages).description(description).build());
|
||||
}
|
||||
);
|
||||
```
|
||||
|
|
@ -592,10 +608,8 @@ Once connected to a compatible client, the server can request language model gen
|
|||
|
||||
// Define a tool that uses sampling
|
||||
var calculatorTool = SyncToolSpecification.builder()
|
||||
.tool(Tool.builder()
|
||||
.name("ai-calculator")
|
||||
.tool(Tool.builder("ai-calculator", schema)
|
||||
.description("Performs calculations using AI")
|
||||
.inputSchema(schema)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// Check if client supports sampling
|
||||
|
|
@ -606,9 +620,10 @@ Once connected to a compatible client, the server can request language model gen
|
|||
}
|
||||
|
||||
// Create a sampling request
|
||||
CreateMessageRequest samplingRequest = CreateMessageRequest.builder()
|
||||
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.TextContent("Calculate: " + request.arguments().get("expression")))))
|
||||
CreateMessageRequest samplingRequest = CreateMessageRequest.builder(
|
||||
List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.TextContent("Calculate: " + request.arguments().get("expression")))),
|
||||
100)
|
||||
.modelPreferences(McpSchema.ModelPreferences.builder()
|
||||
.hints(List.of(
|
||||
McpSchema.ModelHint.of("claude-3-sonnet"),
|
||||
|
|
@ -618,7 +633,6 @@ Once connected to a compatible client, the server can request language model gen
|
|||
.speedPriority(0.5)
|
||||
.build())
|
||||
.systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.")
|
||||
.maxTokens(100)
|
||||
.build();
|
||||
|
||||
// Request sampling from the client
|
||||
|
|
@ -646,10 +660,8 @@ Once connected to a compatible client, the server can request language model gen
|
|||
|
||||
// Define a tool that uses sampling
|
||||
var calculatorTool = AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder()
|
||||
.name("ai-calculator")
|
||||
.tool(Tool.builder("ai-calculator", schema)
|
||||
.description("Performs calculations using AI")
|
||||
.inputSchema(schema)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// Check if client supports sampling
|
||||
|
|
@ -660,9 +672,10 @@ Once connected to a compatible client, the server can request language model gen
|
|||
}
|
||||
|
||||
// Create a sampling request
|
||||
CreateMessageRequest samplingRequest = CreateMessageRequest.builder()
|
||||
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.TextContent("Calculate: " + request.arguments().get("expression")))))
|
||||
CreateMessageRequest samplingRequest = CreateMessageRequest.builder(
|
||||
List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.TextContent("Calculate: " + request.arguments().get("expression")))),
|
||||
100)
|
||||
.modelPreferences(McpSchema.ModelPreferences.builder()
|
||||
.hints(List.of(
|
||||
McpSchema.ModelHint.of("claude-3-sonnet"),
|
||||
|
|
@ -672,7 +685,6 @@ Once connected to a compatible client, the server can request language model gen
|
|||
.speedPriority(0.5)
|
||||
.build())
|
||||
.systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.")
|
||||
.maxTokens(100)
|
||||
.build();
|
||||
|
||||
// Request sampling from the client
|
||||
|
|
@ -701,10 +713,8 @@ Servers can request user input from connected clients that support elicitation:
|
|||
|
||||
```java
|
||||
var tool = SyncToolSpecification.builder()
|
||||
.tool(Tool.builder()
|
||||
.name("confirm-action")
|
||||
.tool(Tool.builder("confirm-action", schema)
|
||||
.description("Confirms an action with the user")
|
||||
.inputSchema(schema)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// Check if client supports elicitation
|
||||
|
|
@ -741,10 +751,8 @@ To request out-of-band URL elicitation, such as a user authorizing an OAuth flow
|
|||
|
||||
```java
|
||||
var urlTool = SyncToolSpecification.builder()
|
||||
.tool(Tool.builder()
|
||||
.name("oauth-auth")
|
||||
.tool(Tool.builder("oauth-auth", schema)
|
||||
.description("Authenticates via OAuth")
|
||||
.inputSchema(schema)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// Request URL elicitation from client
|
||||
|
|
@ -779,23 +787,17 @@ Log notifications can only be sent from within an existing client session, such
|
|||
The server can send log messages using the `McpAsyncServerExchange`/`McpSyncServerExchange` object in the tool/resource/prompt handler function:
|
||||
|
||||
```java
|
||||
var tool = new McpServerFeatures.AsyncToolSpecification(
|
||||
Tool.builder().name("logging-test").description("Test logging notifications").inputSchema(emptyJsonSchema).build(),
|
||||
null,
|
||||
(exchange, request) -> {
|
||||
|
||||
exchange.loggingNotification( // Use the exchange to send log messages
|
||||
McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.DEBUG)
|
||||
.logger("test-logger")
|
||||
.data("Debug message")
|
||||
.build())
|
||||
.block();
|
||||
|
||||
return Mono.just(CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent("Logging test completed")))
|
||||
.build());
|
||||
});
|
||||
var tool = AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder("logging-test", emptyJsonSchema).description("Test logging notifications").build())
|
||||
.callHandler((exchange, request) ->
|
||||
exchange.loggingNotification( // Use the exchange to send log messages
|
||||
McpSchema.LoggingMessageNotification.builder(McpSchema.LoggingLevel.DEBUG, "Debug message")
|
||||
.logger("test-logger")
|
||||
.build())
|
||||
.then(Mono.just(CallToolResult.builder()
|
||||
.content(List.of(new McpSchema.TextContent("Logging test completed")))
|
||||
.build())))
|
||||
.build();
|
||||
|
||||
var mcpServer = McpServer.async(mcpServerTransportProvider)
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
|
|
|
|||
Loading…
Reference in a new issue