17 KiB
MCP Specification — 2025-11-25 (Latest Stable)
Status: Latest Stable (current
Latestrelease) Released: 2025-11-25 · repo tags2025-11-25(stable),2025-11-25-RC(pre-release) Spec site: https://modelcontextprotocol.io/specification/2025-11-25 Authoritative schema: schema/2025-11-25/schema.ts
This is the current stable revision of the Model Context Protocol. It keeps the stateful, JSON-RPC 2.0 core of 2025-06-18 and refines authorization (adding OpenID Connect Discovery and OAuth Client ID metadata documents), introduces optional icons metadata across server primitives, URL-mode elicitation, incremental scope consent (step-up authorization), tool-calling in sampling, and ships an experimental Tasks mechanism for long-running, pollable operations.
At a glance
- Protocol style: stateful connections
- Capability negotiation: connection-level (
initialize) - Transports: stdio; Streamable HTTP
- Server features: Resources, Prompts, Tools, Logging
- Client features: Sampling, Roots, Elicitation
- Authorization: OAuth 2.1 + Resource Indicators (RFC 8707) + OIDC Discovery + Protected Resource Metadata (RFC 9728) + Client ID metadata documents + incremental scope consent
- Experimental: Tasks
Architecture
Client-host-server topology built on JSON-RPC, focused on context exchange and sampling coordination.
- Host: creates/manages multiple clients, enforces consent and security policy, aggregates context. Each client has a 1:1 relationship with one server and maintains isolation between servers.
- Client: establishes one stateful session per server, negotiates capabilities, routes messages, owns subscriptions/notifications.
- Server: exposes resources/tools/prompts, may request sampling/roots/elicitation through client interfaces; can be a local process or remote service.
Design principles: servers should be easy to build, highly composable, must not read the whole conversation or "see into" other servers, and features can be added progressively. Capability negotiation is the contract: server features (resources, prompts, tools, logging) and client features (sampling, roots, elicitation, tasks) must be advertised in capabilities to be usable.
Base Protocol
Messages
All messages MUST be JSON-RPC 2.0, UTF-8 encoded. Three kinds:
- Requests —
id(string|integer) REQUIRED; MUST NOT benull; the requestor MUST NOT reuse anidwithin a session. - Responses —
result(any object) on success;error(codeinteger +message) on failure. TheidMUST match the originating request (except when the requestidwas unreadable due to malformation). - Notifications — one-way; MUST NOT carry an
id; the receiver MUST NOT respond.
The _meta field is reserved for protocol-level and extension metadata; certain keys are reserved by MCP and implementers MUST NOT assume meaning for reserved keys. JSON Schema dialect: schemas without an explicit $schema default to JSON Schema 2020-12; implementations MUST support at least 2020-12 and SHOULD document any additional dialects. The icons property is a standardized optional visual-identifier array (src, mimeType, sizes) usable on implementations/resources/tools/prompts; icon payloads MAY contain executable content (e.g. scripted SVG) so consumers MUST fetch them without credentials and treat them as untrusted.
Lifecycle
Three phases: Initialization → Operation → Shutdown.
- Client sends
initializewith itsprotocolVersion(this SHOULD be the latest the client supports), clientcapabilities, andclientInfo. - Server responds with the negotiated
protocolVersion, itscapabilities,serverInfo(name/title/version/description/icons/websiteUrl), and optionalinstructions. - Client sends an
notifications/initializednotification; only then does normal operation begin. Beforeinitializeis answered the client SHOULD NOT send non-pingrequests; beforeinitializedthe server SHOULD NOT send non-ping/non-logging requests.
Version negotiation: if the server supports the requested version it echoes it; otherwise it returns the latest version it supports. If the client cannot accept that version it SHOULD disconnect. Over HTTP the client MUST send MCP-Protocol-Version: <version> on every subsequent request.
Transports
- stdio — client launches the server as a subprocess; newline-delimited JSON-RPC on stdin/stdout; messages MUST NOT contain embedded newlines; stdout is reserved exclusively for valid MCP messages;
stderris for optional logging. Clients SHOULD support stdio whenever possible. - Streamable HTTP — the server exposes a single MCP endpoint supporting POST and GET. The client POSTs each JSON-RPC message with
Accept: application/json, text/event-stream; the server answers synchronously (application/json) or opens an SSE stream. Session management: the server MAY assign anMcp-Session-Idon theInitializeResultresponse; once issued, the client MUST send that header on subsequent requests and the server MAY reject mismatches with 404. Resumability: servers MAY attach SSEids; clients resume after disconnect via HTTP GET withLast-Event-ID. Servers MUST validate theOriginheader (403 on mismatch) and SHOULD bind to localhost locally to prevent DNS-rebinding. Replaces the deprecated HTTP+SSE transport from 2024-11-05.
Authorization
Authorization is OPTIONAL. HTTP-based implementations SHOULD conform; stdio SHOULD NOT (use environment credentials instead). The model: the MCP server is an OAuth 2.1 resource server; the MCP client is the OAuth 2.1 client; a separate authorization server issues tokens.
- Built on OAuth 2.1 (draft-ietf-oauth-v2-1-13) with PKCE and Resource Indicators (RFC 8707).
- Protected Resource Metadata (RFC 9728) — MCP servers MUST implement it; clients MUST use it for authorization-server discovery (via
WWW-Authenticate: resource_metadata=…on 401, or a.well-known/oauth-protected-resourceURI). Servers SHOULD advertise requiredscopein the 401 challenge. - Authorization-server metadata — the authorization server MUST expose either OAuth 2.0 Authorization Server Metadata (RFC 8414) or OpenID Connect Discovery 1.0 (
.well-known/openid-configuration); clients MUST support both, trying RFC 8414 first. - OAuth Client ID Metadata Documents (draft-ietf-oauth-client-id-metadata-document-00) — clients/servers SHOULD support this public-client identification mechanism; RFC 7591 Dynamic Client Registration MAY be supported.
- Incremental scope consent — the step-up / scope-challenge flow: clients start with the minimal
scopes_supported(or the 401scope) and request additional scopes only when the server returns an insufficient-scope error (WWW-Authenticatewith a narrowed challenge), satisfying least-privilege progressively.
Versioning
There is no dedicated versioning page in this revision; protocol-version agreement is defined within Lifecycle (the initialize handshake and the MCP-Protocol-Version header). The version string for this revision is 2025-11-25. The spec site's /basic/versioning URL does not resolve for 2025-11-25.
Server Features
Resources
Application-driven context exposed by URI. Capability resources with optional subscribe and listChanged. Operations: resources/list (paginated), resources/read, resources/templates/list (RFC 6570 URI templates, auto-completable). Resources carry uri, name, optional title/description/mimeType/icons/annotations (audience, priority, lastModified). Changes are signalled via notifications/resources/*; resource links let any text content embed typed links to other resources through their URI. Clients MUST obtain user consent before exposing resource data.
Prompts
User-controlled templates (e.g. slash commands). Capability prompts with optional listChanged. Operations: prompts/list (paginated), prompts/get (with arguments). A prompt has name, optional title/description/arguments/icons; messages are role + typed content (text/image/audio/embedded-resource), each supporting annotations.
Tools
Model-controlled functions. Capability tools with optional listChanged. Operations: tools/list (paginated) and tools/call. A tool defines name, optional title/description/icons, inputSchema (defaults to JSON Schema 2020-12), optional outputSchema, and annotations (e.g. readOnlyHint, destructiveHint, idempotentHint, openWorldHint). Structured output: when outputSchema is present, the server MUST return conforming results as a JSON object in structuredContent, and SHOULD also mirror it in a TextContent block for backwards compatibility; clients SHOULD validate against the schema. Results return content[], an isError flag, and the optional structuredContent. A human SHOULD remain in the loop to authorize invocations.
Utilities / Logging
Cross-cutting utilities: pagination (cursor/nextCursor on list ops), completion (completion/complete for argument auto-complete), ping (ping), cancellation (notifications/cancelled), progress (notifications/progress), and logging — capability logging; clients configure level via logging/setLevel, servers emit notifications/message with severity, logger name, and structured data.
Client Features
Sampling
Server-initiated LLM completions via sampling/createMessage. Clients MUST declare sampling; human-in-the-loop review of the prompt and result is RECOMMENDED. Requests carry messages, optional systemPrompt, modelPreferences (hints, intelligencePriority/speedPriority), maxTokens, and (when the client advertises sampling.tools) a tools array plus optional toolChoice. Tool-calling support: the client's LLM may emit tool_use; the server executes the tool and re-issues sampling/createMessage with the tool results, looping until stopReason: "endTurn". The legacy includeContext: "thisServer"|"allServers" values (gated behind sampling.context) are soft-deprecated.
Roots
Server-initiated discovery of filesystem boundaries. Capability roots (optional listChanged). Server sends roots/list; client returns uri (currently MUST be a file:// URI) plus optional name. Clients emit notifications/roots/list_changed when the set changes. Clients MUST validate root URIs against path traversal and SHOULD prompt for consent before exposing them.
Elicitation
Server-initiated requests for user information via elicitation/create. Capability elicitation with sub-modes form and url (an empty object is equivalent to form-only; at least one mode MUST be supported). Two modes:
- Form mode — in-band structured input.
requestedSchemais restricted to a flat object of primitive properties (string/number/integer/boolean/enum, includingoneOf/multi-select arrays); formatsemail,uri,date,date-time. Responseactionisaccept(withcontent),decline, orcancel. - URL mode (new in 2025-11-25) — out-of-band interaction for sensitive data (secrets, OAuth, payments) that must not transit the client. Parameters:
mode: "url",url,elicitationId,message. The client only collects consent and opens the URL; the response isaction: "accept"with no content. Servers MAY firenotifications/elicitation/completeand MAY return error-32042(URLElicitationRequiredError) carrying required elicitations. Servers MUST use URL mode (never form mode) for passwords/API keys/tokens/credentials.
Privacy: clients MUST show which server is asking, provide clear decline/cancel controls, let users review form responses before sending, and never log/retain secrets.
Experimental: Tasks
Introduced in 2025-11-25 as experimental. Tasks are durable state machines that wrap a request for pollable, deferred-result execution (expensive computation, batch jobs, external job APIs). Either party may be a requestor or receiver. A task is created by adding a task field (with optional ttl) to an augmentable request; the receiver returns a CreateTaskResult with a taskId, status (working→completed/failed/cancelled, plus input_required), pollInterval, and timestamps. The real result is fetched later via tasks/result; status via tasks/get, optional notifications/tasks/status, listing via tasks/list, termination via tasks/cancel. Capability tasks is structured per request category (e.g. tasks.requests.tools.call server-side; tasks.requests.sampling.createMessage / tasks.requests.elicitation.create client-side), with tool-level negotiation through execution.taskSupport (required/optional/forbidden). The design may be formalized or modified in future revisions.
Changes vs 2025-06-18
- OpenID Connect Discovery 1.0 added as a first-class authorization-server metadata mechanism (clients must support it alongside RFC 8414).
- Icons metadata — optional
icons[]on implementations, resources, tools, and prompts. - Incremental scope consent — step-up authorization via scope challenges (request additional scopes only on insufficient-scope errors).
- URL-mode elicitation (
mode: "url",URLElicitationRequiredError-32042,notifications/elicitation/complete) for sensitive out-of-band interactions. - Sampling tool-calling —
sampling.toolscapability; servers may passtools/toolChoiceand run a multi-turn tool loop. - OAuth Client ID metadata documents (draft-ietf-oauth-client-id-metadata-document-00) for public-client identification.
- Experimental Tasks — durable, pollable, deferred-result request augmentation.
- Unchanged: stateful JSON-RPC 2.0 core (batching already removed in 2025-06-18), connection-level capability negotiation, stdio + Streamable HTTP transports, structured tool output (
outputSchema/structuredContent, from 2025-06-18), elicitation form mode, resource links.
Skald relevance
This is the revision Skald's MCP client targets. The client lives in crates/mcp-client/ (stdio + Streamable HTTP, with an elicitation integration test); runtime connection management is src/core/mcp/mod.rs (McpManager); server-initiated elicitation/create is handled by src/core/elicitation/mod.rs (ElicitationManager + bridge), surfaced in the unified Inbox, with secrets never logged or persisted. Non-text tool-result content (image/audio/resource/resource_link) is now preserved rather than dropped: it is persisted under data/mcp_media/ and surfaced as a /api/mcp-media/ URL in a markdown result (see ../../mcp.md → Media tool results).
Cancellation (notifications/cancelled) is implemented: an abandoned tools/call (a /stop that drops the work future, or the 120 s timeout) now tells the server to stop via a per-transport drop-guard, instead of leaving it working on abandoned output. Tasks are polled synchronously (block-and-poll): call_tool opts a task-capable tool in with the task field, then poll_task polls tasks/get until terminal and fetches the real result via tasks/result — so a task-mode call no longer hits the 120 s wall — sending tasks/cancel cooperatively if the call is dropped. Both are documented in ../../mcp.md → Cancellation & Tasks. The block-and-poll variant holds the session for the task's duration and doesn't survive a self-restart; the detached/durable poller (DB-persisted tasks + background delivery via inject_async_result/resume_turn) is the tracked follow-up. Still unimplemented from this revision: sampling, roots, URL-mode elicitation, mid-task input_required, progress/completion/logging utilities, and OAuth authorization (HTTP transport).