-
Notifications
You must be signed in to change notification settings - Fork 10
protocol versions
Verdict: Protocol SPI via ServiceLoader, two impls registered. Negotiation = highest versionString among protocols whose matches(request) is true. Mappers isolate wire shape; handlers see protocol-neutral domain types.
Protocol Protocol: endpoint, familyName, versionString (ISO date β lexical = chronological), priority, supportsSessions (Protocol#supportsSessions), matches(HttpRequest), responseMapper, requestMapper, createInteractionContext, requestHandlers(server) (Protocol#requestHandlers, must be @Sharable, must no-op for other versions).
Registry Protocols static ServiceLoader, fails if empty Protocols#PROTOCOLS; resolve = filter matches β max by version then priority Protocols#resolve. Services file tachyon-core/src/main/resources/META-INF/services/dev.tachyonmcp.core.protocol.Protocol (2025 first, 2026 second β getFirst() callers depend on this order, see findings).
| Aspect | 2025-11-25 | 2026-07-28 |
|---|---|---|
| Impl | protocol/mcp/v2025_11_25/McpProtocol.java |
protocol/mcp/v2026_07_28/McpProtocol.java |
matches POST |
header absent or in {2025-11-25, 2025-06-18, 2025-03-26} McpProtocol#matches
|
header == 2026-07-28 McpProtocol#matches
|
matches GET/DELETE/OPTIONS |
yes (endpoint only) | no |
supportsSessions |
true |
false McpProtocol#supportsSessions
|
| Handshake |
initialize + notifications/initialized
|
none; server/discover
|
| Extensions | once at initialize (InitializeHandler) |
per request _meta."io.modelcontextprotocol/clientCapabilities".extensions
|
| Pipeline handlers | none (requestHandlers default) |
RequestValidationHandler() + RequiredHeadersHandler(server) + ExtensionNegotiationHandler(extensions) McpProtocol#requestHandlers
|
| SEP-2243 mirrors | optional β but checked if sent |
required RequiredHeadersHandler#requireMirrors
|
| Logging threshold |
logging/setLevel per session |
_meta."io.modelcontextprotocol/logLevel" per request; absent β no logs NotificationsImpl#shouldEmit
|
| Resource updates |
resources/subscribe + GET SSE |
subscriptions/listen stream (SEP-2575) |
| Tasks | legacy tasks/list, tasks/result, task-augmented tools/call
|
`tasks/get |
| Error codes | JSON-RPC std + -32001..-32004, HTTP 200 | -32020/-32021/-32022, HTTP 400/404 β errors |
| Result envelope | plain | `resultType: complete |
Request mappers: shared logic in AbstractMcpRequestMapper AbstractMcpRequestMapper (all mapping methods + _meta helpers). Each version is final and only supplies convert(ObjectNode, Class) with its generated CodecRegistry:
- 2025-11-25: own codec, else
invalid_params("Unsupported params type β¦")McpRequestMapper.java. - 2026-07-28: own codec β fallback to 2025-11-25 codec β
invalid_params; plus overridescallTool,supportsLegacyTaskAugmentation=false,supportsSubscriptionsListen=true,subscriptionsListenMcpRequestMapper.java.
Params rules AbstractMcpRequestMapper: null β empty object; ObjectNode/Map accepted; any other JSON (array, scalar) β RequestMappingException(invalid_params "Params must be an object"); codec JacksonException β invalid_params with Jackson message. declaredExtensions, permittedLogLevel, hasMetaKey also throw on non-object params/_meta ProtocolRequestMapper#supportsLegacyTaskAugmentation.
Response mapper 2026 extends 2025 one McpResponseMapper.java.
v2026_07_28/transport/RequestValidationHandler.java (peeks via PeekedBody#peek, malformed JSON parses to null and passes through to the normal parse error). Only what 2026-07-28 alone demands:
- Removed methods β method not found:
initialize, ping, logging/setLevel, resources/subscribe, resources/unsubscribeRequestValidationHandler#REMOVED_METHODS. -
_metaobject required;io.modelcontextprotocol/protocolVersionstring;clientInfoshape if present;clientCapabilitiesobjectRequestValidationHandler#validate. - Header
MCP-Protocol-Version==_metaversion else header mismatchRequestValidationHandler#validate. π΄ Nothing here touches the SEP-2243 mirrors.
v2026_07_28/transport/RequiredHeadersHandler.java, same gate + peek. Mirrors present: Mcp-Method always (requests and notifications); Mcp-Name when the method addresses a target; Mcp-Param-<h> for every x-mcp-header property the call passed a mirrorable value for RequiredHeadersHandler#requireMirrors. Presence only β agreement was already settled upstream (below).
transport/netty/http/McpHeaderMatchHandler.java, ungated by version, runs before every protocol's own handlers (McpChannelInitializer#initChannel), so a mirror that lies is reported ahead of one that is merely missing. Rule: a mirror that is present must match the body; a mirror that is absent is this handler's business on no version.
Why version-free: a gateway routing on Mcp-Method/Mcp-Param-Region must never authorize an operation the body doesn't run β true whichever revision negotiated. It also lets SEP-2243's canonical example work: Mcp-Method: initialize on the handshake, which by construction carries no MCP-Protocol-Version and so negotiates a pre-SEP-2243 revision.
-
Mcp-Method== body method, requests and notificationsMcpHeaderMatchHandler#matchMethod. -
Mcp-Name==name(tools/call, prompts/get) oruri(resources/read)McpHeaderNames#mirroredNameField,McpHeaderMatchHandler#matchName. -
Mcp-Param-*chars: HTAB/space/visible ASCII only, checked pre-decodeMcpHeaderMatchHandler#findInvalidCharacterParamHeader. -
tools/call: eachx-mcp-headerproperty's header must match its argument; numbers compared byBigDecimal(so42==42.0), |n| β€ 2^53-1, NaN/Infinity rejected; header with no body value behind it rejectedMcpHeaderMatchHandler#matchArgument,MirroredArgument.
Wire format McpHeaderValue: =?base64?β¦?= sentinel else literal (OWS-stripped). decodeLenient (Mcp-Name) degrades a bad payload to a mismatch; decodeStrict (Mcp-Param-*) throws, because SEP-2243 makes invalid Base64/padding its own rejection reason.
Registration side: x-mcp-header only on top-level string|integer|boolean props, HTTP-token name, case-insensitively unique JsonSchemaUtils.
Header names McpHeaderNames McpHeaderNames#MCP_SESSION_ID: MCP-Session-Id, MCP-Protocol-Version, Last-Event-ID, Mcp-Method, Mcp-Name, Mcp-Param-, schema keyword x-mcp-header.
ServerError and ChannelHandlerUtils#rejectWithServerError maps it through the negotiated responseMapper (errors).
ProtocolRequestMapper ProtocolRequestMapper β page, callTool, getPrompt, readResource, complete, resourceUri, taskGet/Cancel/AwaitResult/Update, loggingLevel, initialize, declaredExtensions, permittedLogLevel, hasMetaKey, cancellation, taskStatus, subscriptionsListen. Records: PageRequest, ToolCallRequest(request, taskAugmented), CompletionReference sealed, SubscriptionListenRequest.
ProtocolResponseMapper ProtocolResponseMapper β encode, emptyResult, error, discoverResult, initializeResult, list*Result, callToolResult, readResourceResult, getPromptResult, inputRequiredResult, task results, notification params (loggingMessageParams, progressNotificationParams, taskStatusNotificationParams, subscriptions ack/list-changed/resource-updated/graceful).
- Wire models + streaming Jackson codecs generated from TypeScript schema:
tachyon-core/protocol/mcp-2025-11-25.ts,mcp-2026-07-28.ts+*_config.json. - Generator
tachyon-core/ts2java.py(records + Jackson streaming codecs,@Generated). - Bound in
pom.xmlviaexec-maven-pluginatgenerate-sources; outputtarget/generated-sources/ts2java/javaadded as source root (pom.xml). Generated packages:dev.tachyonmcp.core.protocol.mcp.v20xx.models,...codecs. β οΈ Generated code not in git. Handwritten mappers live next to generated codecs incodecs/.- Revapi ignores generated classes (commit
3cc96c5f).
- TS schema + config in
tachyon-core/protocol/, exec execution intachyon-core/pom.xml. -
McpProtocolimpl + request mapper (extends AbstractMcpRequestMapper, implementconvertwith fallback to older codecs) + response mapper (extend previous). - Register in services file.
- Pipeline handlers (
@Sharable, version-check first). - e2e package
e2e/src/test/java/dev/tachyonmcp/e2e/mcp/v<ver>/+ testkit client. See testing.
Related: netty-pipeline, extensions, errors.
π source .llm-wiki/concepts/protocol-versions.md Β· updated 2026-09-17 Β· verified at 1a4081f4 Β· tags [concept, protocol, mcp]
π§ Start
βοΈ Concepts (cross-cutting)
- request-lifecycle
- netty-pipeline
- protocol-versions
- sessions
- sse-streams
- feature-registries
- tasks
- extensions
- json-layer
- errors
- concurrency
- declarative-configuration
- configuration
- security-guards
- observability
- api-stability
π¦ Modules