Repository navigation
Add server support for the MCP 2026-07-28 ("modern") protocol revision - #7284
Merged
Merged
Conversation
dimitarproynov
approved these changes
Nov 2, 2026
Comment on lines
+39
to
+41
| public McpTransportContext extract(ServerRequest request) { | ||
| return McpTransportContexts.fromHeaders(request.headers().asHttpHeaders()::forEach); | ||
| } |
Contributor
There was a problem hiding this comment.
As mentioned in the meeting this will only generate headaches for us. Let users implement it, as we cannot ever have a full white or black list.
Contributor
Author
There was a problem hiding this comment.
Agreed, and done in 8d514d2: the extractor is gone, and so is the context-headers property that briefly replaced it. The transport context is empty unless the application defines a McpTransportContextExtractor bean. McpTransportContexts.fromHeaders(names, lookup) stays as a helper for such a bean, and the reference page shows one in the per-caller filtering example.
The 2026-07-28 ("modern") server API lives in the
`io.modelcontextprotocol.modern` package, which is only available in
the 2.2.0 line of the SDK. The snapshot must be installed locally from
the java-sdk checkout until the SDK is released.
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
`WebMvcModernServerTransport` serves the 2026-07-28 revision over a single POST endpoint: header consistency checks against the body, bounded body reading, JSON-RPC error to HTTP status mapping, server-sent event streaming with keep-alives and an early header commit, and `subscriptions/listen` over the servlet async context. The protocol helpers mirror the SDK's servlet transport in `ModernTransportSupport`. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Add WebFluxMcpTransport, a stateless POST-only transport serving the 2026-07-28 MCP revision through the SDK's McpRequestManager. Requests are resolved with the non-blocking handle(), so sync handlers run on boundedElastic and never on the event loop. Streaming responses and subscriptions/listen are sent as server-sent events with keep-alives, and a leading comment commits the response headers right away. Client disconnects cancel in-flight work. Headers are validated against the body, and JSON-RPC errors are mapped to HTTP statuses. Taken from chemicL/spring-ai branch modern-webflux-transport, commit 70afa59, without its module-local SDK version pin. Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com> Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Reject non-JSON request bodies with 415 as the SDK servlet transport does, add `no-transform` to the stream cache header, and tag the public API `@since 2.2.0` in line with the rest of the modern integration. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
The SDK repositories return a McpSyncTool from find and no longer have a call method, so the three test fixtures now return McpSyncTool.of(tool, this::call). The new types carry @SInCE 2.1.0. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
In-memory repositories for the four primitives on the SDK's McpSyncTool, McpSyncPrompt and McpSyncResource, with runtime add/remove, change broadcasts and key-cursor paging; SyncResourceEntry and SyncResourceTemplateEntry pair a resource or template definition with its handler, which McpSyncResource does not carry; a legacy/modern schema converter, ToolCallback adaptation, the transport context header convention and the modern server customizer. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Modern method callbacks for @mcptool, @McpResource, @McpPrompt and @McpComplete that implement the SDK handler interfaces, the providers that build McpSyncTool, McpSyncPrompt, SyncResourceEntry and SyncResourceTemplateEntry from annotated beans, and the McpModernSyncRequestContext parameter type with progress and MRTR support. Input responses are converted with the server's JSON mapper. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Activated by spring.ai.mcp.server.protocol=MODERN. Publishes the scanned primitives as list beans, builds an in-memory repository per primitive unless a repository bean is defined, wires the McpServer with the features, the change feed and the request state codec, and adds the WebMvc and WebFlux transports with a default header-based transport context extractor. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Design document, SDK review for the second iteration, migration guide and the reference page for the modern server starter. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
A method that declared McpModernSyncRequestContext was always served as a streaming response, so a parameter type decided the response shape. The SDK leaves that choice to the handler, which returns either McpSyncResponse.result or McpSyncResponse.streaming; multi round-trip methods that never report progress, and calls without a progress token, were streamed for nothing. The context now carries the request data and the MRTR fields only: progressEnabled(), progress(...) and notify(...) are removed, and DefaultMcpModernSyncRequestContext no longer holds a notifier. A tool, resource or prompt method streams if and only if it declares the SDK McpSyncNotifier, and reports progress through it. The reference page, the design documents and the migration guide describe the rule, and the tests cover a token-less streaming call and a context-only plain result. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
The MCP schema has long carried fields the annotations could not set, and the 2026-07-28 revision adds one more. `@McpIcon` and an `icons` attribute on `@McpTool`, `@McpResource` and `@McpPrompt` publish icons in the definitions; `@McpArg` gains `title`; `@McpResource` gains `size`. `@McpToolParam(header = true)` emits the `x-mcp-header` schema keyword so clients mirror a parameter into the `Mcp-Param-<name>` header. The keyword is allowed on parameters whose generated schema type is string, integer or boolean, so enums qualify, and other types are rejected at startup. The attributes are wired on the legacy, stateless and modern providers alike; the modern definitions receive them through the JSON round-trip of `McpSchemaConverter.toModern`, which also derives the modern icons from the legacy ones. The reference page documents the attributes. The design folder is reorganised: `MODERN.md` is now a one-page summary, the full design with its diagrams moves to `MODERN-detailed.md`, and the folder README is removed. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Annotated modern methods were built with ClientCapabilities.NONE, so a client lacking elicitation, sampling or roots was refused only after the method had run, one missing capability at a time, and after a streaming method had opened its stream. @mcptool, @McpPrompt and @McpResource now take requiredCapabilities, a list of McpClientCapability values, which the modern providers pass to McpSync*.of(definition, requiredCapabilities, handler). The SDK refuses such requests with -32021 before the method runs. SyncResourceEntry and SyncResourceTemplateEntry gain the matching of(...) overloads. The runtime check on InputRequiredResult stays as a safety net and warns once per callback. The modern provider warns when the return type cannot carry an InputRequiredResult; the legacy providers warn that the attribute is ignored. logback-classic joins the test classpath so the warnings can be asserted. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Rename SyncResourceEntry, SyncResourceTemplateEntry and SyncCompletionSpecification to SyncResourceRegistration, SyncResourceTemplateRegistration and SyncCompletionRegistration, and the completion provider methods to modernCompletions. McpModernSyncRequestContext.inputResponse and McpModernToolUtils.getMcpRequestContext return @nullable values instead of Optional. Pin the MCP Java SDK to 2.2.0-M2. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
tzolov
force-pushed
the
modern-mcp-server-2
branch
from
November 2, 2026 19:01
8a8c6b9 to
dca1b1b
Compare
The WebMvc and WebFlux auto-configurations stored every request header in the transport context, including Authorization and Cookie, in front of every handler, repository and ToolCallback. The SDK and the legacy Spring transports default to an empty context. spring.ai.mcp.server.modern.context-headers names the headers to keep; nothing is stored by default. McpTransportContexts.fromHeaders(names, lookup) builds the context from the named headers only and returns the empty context when none is present. Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
- Mirror the Mcp-Param-* headers into the transport context on every tools/call, where the SDK tools feature validates them, and put the bare parameter name in x-mcp-header as the SDK expects - Decode Mcp-Name with the SDK McpHeaderDecoder; the local copy failed on "=?base64?=" - Answer a malformed Content-Type with 415 and a failed notification with 500 - Require a request-state-secret of at least 32 bytes - Drop the built-in header extractor: the transport context is empty unless the application defines a McpTransportContextExtractor bean Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a server for the 2026-07-28 ("modern") MCP revision next to the existing STDIO, SSE, STREAMABLE and STATELESS servers, activated with
spring.ai.mcp.server.protocol=MODERN. The revision dropsinitializeand sessions:server/discoveris a cacheable request, every request carries the protocol version and the client capabilities in_meta, server-initiated input (elicitation, sampling, roots) is a multiround-trip request, and change notifications go oversubscriptions/listen.The integration builds on the modern API of MCP Java SDK 2.2.0 (
io.modelcontextprotocol.modern), in particular theMcpSyncTool/McpSyncPrompt/McpSyncResourcefeatures with repositories (modelcontextprotocol/java-sdk#1282) and required client capabilities (modelcontextprotocol/java-sdk#1283).What is in the change
WebMvcModernServerTransportandWebFluxMcpTransportserve the revision over a single POST endpoint: header/body consistency checks, JSON-RPC error to HTTP status mapping, bounded body reading, server-sent event streaming with keep-alives and an early header commit, andsubscriptions/listen. The WebFlux transport runs sync handlers onboundedElasticand cancels in-flight work on clientdisconnect.
spring-ai-mcp) - In-memory repositories for tools, prompts, resources and completions with runtimeadd/remove, change broadcasts and key-cursor paging.SyncResourceEntryandSyncResourceTemplateEntrypair a resource definition with its handler, which the SDK'sMcpSyncResourcedoes not carry. A legacy/modernMcpSchemaConverter,ToolCallbackadaptation and a header-based transport context convention complete the module.spring-ai-mcp-annotations) - Modern method callbacks for@McpTool,@McpResource,@McpPromptand@McpCompleteimplementing the SDK handler interfaces, and providers that build the SDK types from annotated beans. Methods may declareMcpModernSyncRequestContextfor the request data and the MRTR fields (inputResponses,requestState), and the SDKMcpSyncNotifierfor progress; a method streams if and only if it declares the notifier.requiredCapabilitieson@McpTool,@McpPromptand@McpResourcelists theMcpClientCapabilityvalues a method cannot work without; the server answers-32021before the method runs. A runtime check onInputRequiredResultremains as the safety net.@McpIconwithiconson the three annotations,@McpArg(title),@McpResource(size, ttlMs, cacheScope)and@McpToolParam(header = true), which emits thex-mcp-headerschema keyword. The attributes are wired on the legacy, stateless and modern providers alike; the modern-only ones are ignored with a warning elsewhere.List<McpSyncTool>etc.), builds an in-memory repository per primitive unless a repository bean of the SDK interface is defined, wiresMcpServerwith the features, the change feed and the request state codec, and registers the transports. New properties underspring.ai.mcp.server.modern.*(endpoint, list and discover TTLs, page size, request state secret and TTL).mcp-modern-server-boot-starter-docs.adoc) and the new attributes on the annotations page.Example
Annotated tools, resources and prompts of a STATELESS server migrate with the property change alone; the session-bound calls of a STREAMABLE server (ctx.elicit, ctx.sample, ctx.progress, client logging) are rewritten as above. Working samples for both migrations are in spring-projects/spring-ai-examples#151 (model-context-protocol/modern-migration).
Scope and follow-ups
Dependencies
mcp.sdk.version moves from 2.1.0 to 2.2.0-SNAPSHOT. The PR can be merged once MCP Java SDK 2.2.0 (or its first M/RC) is released and the version is updated.