MCP 2025-11-25 Spec

View Source

Purpose

Map every feature in the MCP 2025-11-25 specification against what wymcp currently implements, to guide planning.

1. Base Protocol

1.1 JSON-RPC 2.0 Message Layer

FeatureSpec requirementwymcp status
Request / Response / Notification framingMUST✅ Wymcp.JsonRpc (internal)
jsonrpc: "2.0" on every messageMUST
Standard error codes (-32700 … -32603)MUSTerror_response/3
_meta reserved property on all requestsMUST support✅ Parsed and passed via Context.meta
Message classification (req/notif/resp)MUSTPlugs.Classify tags conn.assigns.wymcp_message_type

1.2 Lifecycle

FeatureSpec requirementwymcp status
initialize — version + capability negotiationMUSTMethods.Initialize accepts 2025-11-25 only (earlier revisions 2025-03-26 and 2025-06-18 were dropped pre-1.0; see Wymcp.ProtocolVersion)
notifications/initializedMUSTMethods.Initialized
Version negotiation (echo or counter-propose)MUST✅ Echoes the client's requested version when supported; counter-proposes latest/0 for unknown versions
Negotiated version returned in InitializeResult.protocolVersionMUST✅ Echoed (or counter-proposed) and pinned on the session
MCP-Protocol-Version HTTP header on subsequent requestsMUST (HTTP)Plugs.Session enforces equality on every session; Plugs.SingletonHeaders rejects a duplicated header on every session and every route
Store negotiated client capabilities for the sessionSHOULD✅ Stored in Session.State.client_capabilities
Capability negotiation for sampling/elicitationSHOULD✅ Server advertises only what client declares
serverInfo fields: title, description, icons, websiteUrlMAY✅ Via :server_info router option — always emitted (one legacy revision — nothing to gate)
instructions field in init responseMAY✅ Via :instructions router option

Out of scope: 2024-11-05. That revision predates Streamable HTTP and requires a split-endpoint HTTP+SSE transport that wymcp does not implement. See Wymcp.ProtocolVersion for the supported set.

1.3 Transports

FeatureSpec requirementwymcp status
Streamable HTTP (POST + optional SSE)Defined✅ POST + GET SSE via Transport.Stream
stdioDefinedN/A (library is HTTP-focused)
Session management / Mcp-Session-Id headerSHOULD (HTTP)✅ Full session lifecycle with idle timeout (the earlier sessionless fallback was removed)
SSE keepaliveSHOULD✅ Configurable keepalive timer
Stream reconnection via Last-Event-IdMAY⚠️ Header read but not used for replay

1.4 Authorization

FeatureSpec requirementwymcp status
OAuth 2.1 / Bearer token flowSHOULD (HTTP)⚠️ Wymcp.Auth behaviour with Bearer support; no OAuth discovery
WWW-Authenticate for incremental scope consentMAY✅ Returns WWW-Authenticate: Bearer on 401

2. Server Features

2.1 Tools (model-controlled)

Reference: https://modelcontextprotocol.io/specification/2025-11-25/server/tools/

FeatureSpec requirementwymcp status
tools/listMUST if capability declaredMethods.ToolsList
tools/callMUST if capability declaredMethods.ToolsCall
outputSchema + structuredContentMAY✅ Tools define output_schema/0, validated on return — always emitted (one legacy revision — nothing to gate)
Runtime tool registrationN/A (wymcp extension)Session.register_tool/2, unregister_tool/2
Pagination (cursor / nextCursor)SHOULD
listChanged capability + notificationMAY✅ Advertised in capabilities; sent on a change to the tool list tools/list would serve, and on stream attach when one is owed
Tool title fieldMAY✅ Optional callback — always emitted (one legacy revision — nothing to gate)
Tool icons field (array: src, mimeType, sizes)MAY
Tool annotations (audience, priority, etc.)MAY✅ Optional callback, included in definition()
audio content type in resultsMAY
resource_link content type in resultsMAY
Embedded resource content type in resultsMAY
execution.taskSupport fieldMAY (experimental)
Input validation against inputSchemaSHOULD✅ two layers: JSV checks structure in ToolsCall (types), dispatch gates check vocabulary (unknown keys, unknown action)

2.2 Resources (application-driven context)

FeatureSpec requirementwymcp status
resources/listMUST if capability declared❌ Not implemented
resources/readMUST if capability declared
resources/templates/list (URI templates)MUST if capability declared
resources/subscribe + update notificationsMAY
listChanged notificationMAY
Resource annotations (audience, priority, lastModified)MAY
PaginationSHOULD

2.3 Prompts (user-controlled templates)

FeatureSpec requirementwymcp status
prompts/listMUST if capability declared❌ Not implemented
prompts/get with argument substitutionMUST if capability declared
Prompt icons fieldMAY
listChanged notificationMAY
PaginationSHOULD

3. Client Features (server → client requests)

These are requests the server sends to the client via the SSE channel. The session's server-request round trip (Session.await_client_response/4) pushes a JSON-RPC request via SSE, holds the caller, and unblocks it when the client POSTs back a response. Plugs.Classify tags incoming responses so they bypass validation and route to Methods.DeliverResponse.

3.1 Sampling (server asks client to run LLM)

FeatureSpecwymcp status
sampling/createMessageClient capabilityContext.sample/3 — blocks until client responds
Capability negotiationPart of init✅ Only advertised when client declares sampling
Model preferences (hints, priorities)Part of request✅ Passed through via opts
Tool use within samplingClient declares sampling.tools❌ Not implemented (client-side concern)
Multi-turn tool loopPart of sampling❌ Single-turn only

3.2 Elicitation (server asks client for user input)

FeatureSpecwymcp status
elicitation/create — form modeClient capability elicitation.formContext.elicit/4 — sends JSON Schema, blocks for response
Capability negotiationPart of init⚠️ See note below
mode field in requestDefaults to "form" if omitted⚠️ See note below
Sensitive-info constraintMUST NOT use form mode⚠️ Not enforced or documented for tool authors
elicitation/create — URL modeClient capability elicitation.url❌ Deferred
notifications/elicitation/completeServer → Client notification❌ (needed for URL mode — carries elicitationId)
URLElicitationRequiredError (-32042)Error response❌ (structured: data.elicitations[] with mode, elicitationId, url, message)

Implementation notes (form mode):

  1. Capability sub-keys not checked. The spec defines elicitation.form and elicitation.url as distinct client sub-capabilities. Our check_capability/2 only tests Map.has_key?(client_capabilities, "elicitation") — it does not verify the client declared form specifically. Likewise, Initialize advertises "elicitation" => %{} without declaring which modes the server supports. A spec-strict client could reasonably interpret the empty map as "no modes supported."

  2. mode field omitted from request. Context.elicit/4 builds params as %{"message" => …, "requestedSchema" => …} without "mode" => "form". The spec says omitting mode defaults to "form" for backwards compatibility, so this works today but is implicit. Adding the field explicitly would be more robust.

  3. Sensitive-information constraint. The spec states: "Servers MUST NOT use form mode for sensitive information. URL mode MUST be used for sensitive interactions like credentials." This is not enforced in code (nor could it easily be), but should be documented as guidance for tool authors using Context.elicit/4.

3.3 Roots (server asks client for filesystem boundaries)

FeatureSpecNotes
roots/listClient capability❌ Not implemented
notifications/roots/list_changedClient → Server

4. Utilities (cross-cutting)

4.1 Ping

FeatureSpecwymcp status
ping{} responseMUSTMethods.Ping

4.2 Progress Tracking

FeatureSpecwymcp status
_meta.progressToken in requestsMAYContext.progress_token/1
notifications/progress with progress, total, messageMAYContext.report_progress/4
Progress must monotonically increaseMUST (if sent)⚠️ Caller responsibility (not enforced)

4.3 Cancellation

FeatureSpecwymcp status
notifications/cancelled with requestId + reasonMAYMethods.Cancelled
Receiver SHOULD stop work and return error -32800SHOULD⚠️ Tracked but no in-flight abort

4.4 Logging

FeatureSpecwymcp status
logging/setLevel (client → server)MAYMethods.LoggingSetLevel, stores level in session
notifications/message with level + logger + dataMAYContext.log/3 with level filtering
Levels: debug, info, notice, warning, error, critical, alert, emergencyDefined✅ All 8 syslog levels supported

4.5 Completion (autocompletion)

FeatureSpecwymcp status
completion/complete for prompt args and resource URI template paramsServer capability completions
Reference types: ref/prompt, ref/resourceDefined
Context-aware completions (previous arg values)SHOULD

4.6 Pagination

FeatureSpecwymcp status
Opaque cursor-based pagination on all list operationsSHOULD
nextCursor in responses, cursor in requestsDefined

4.7 Tasks (experimental — new in 2025-11-25)

FeatureSpecwymcp status
Task-augmented requests (durable state machines)Experimental
tasks/get — poll task statusDefined
tasks/cancel — cancel running taskDefined
tasks/list — list active tasksDefined
tasks/result — retrieve deferred resultDefined
Task statuses: working, completed, failed, cancelled, input_requiredDefined
execution.taskSupport on tool definitionsDefined
_meta with io.modelcontextprotocol/model-immediate-responseDefined

5. Summary: What wymcp has today

Implemented:

  • JSON-RPC 2.0 framing with message classification (request/notification/response)
  • Schema validation via JSV against the 2025-11-25 schema (priv/schema-2025-11-25.json)
  • Lifecycle: initialize with dynamic capability negotiation, notifications/initialized, ping
  • Version negotiation: always responds with latest supported version (counter-proposal ready)
  • Full session management: Mcp-Session-Id, GenServer-per-session, idle timeout, Registry lookup
  • SSE transport: Transport.Stream with keepalive, bidirectional messaging
  • Tools: tools/list, tools/call with outputSchema + structuredContent
  • Tool metadata: optional title/0 and annotations/0 callbacks on Wymcp.Tool
  • listChanged capability advertised; notifications/tools/list_changed sent on a change to the tool list tools/list would serve, and on stream attach when one is owed
  • Runtime tool registration/unregistration per session
  • Server callbacks: Wymcp.Server behaviour with init/2 and terminate/2
  • Context bridge: session assigns + conn.assigns merged into %Context{}
  • Auth behaviour with Bearer token support and WWW-Authenticate header
  • Cancellation: notifications/cancelled with request tracking
  • Sampling: Context.sample/3 — server asks client's LLM mid-tool-execution
  • Elicitation: Context.elicit/4 — server asks human for structured form input (see §3.2 notes for spec gaps)
  • Server-request round trip: Session.await_client_response/4 + deliver_response/3
  • Progress tracking: Context.progress_token/1 + Context.report_progress/4
  • Logging: logging/setLevel method + Context.log/3 with level filtering
  • Telemetry events for session lifecycle

6. Missing:

Tier 1: Low effort, high value (polish what exists)

  1. Pagination on tools/list
  2. In-flight cancellation — actually abort running tool tasks on notifications/cancelled
  3. Stream reconnection replay — use Last-Event-Id to replay missed events
  4. Elicitation spec alignment — add mode field to requests, check elicitation.form sub-capability, advertise supported modes in server capabilities (see §3.2 notes)

Tier 2: Medium effort, high value (new server features)

  1. ResourcesWymcp.Resource behaviour + resources/list, resources/read
  2. Resource templatesresources/templates/list with URI template expansion
  3. PromptsWymcp.Prompt behaviour + prompts/list, prompts/get
  4. Completioncompletion/complete for prompt and resource template args
  5. Additional content typesaudio, resource_link, embedded resource in tool results

Tier 3: Remaining client features

  1. Elicitation URL modeelicitation/create with URL redirect flow + elicitationId + notifications/elicitation/complete + URLElicitationRequiredError (-32042)
  2. Roots — server → client roots/list request
  3. Sampling tool use — multi-turn tool loop within sampling

Tier 4: Experimental / future

  1. Tasks — durable state machines for long-running operations