# MCP Specification — 2026-07-28 Draft (RC)

> **Status:** Draft / Release Candidate (pre-release). Subject to change — not final.
> **Tag:** `2026-07-28-RC` · rendered at `/specification/draft`
> **Spec site:** https://modelcontextprotocol.io/specification/draft
> **Authoritative schema:** [schema/draft/schema.ts](https://github.com/modelcontextprotocol/specification/blob/main/schema/draft/schema.ts)

The `2026-07-28` draft is a **foundational redesign** of the Model Context Protocol, not an incremental revision. It moves from the prior stateful-connection model to a **stateless base protocol with self-contained requests**, replaces connection-level capability negotiation (the `initialize` handshake) with **per-request capability negotiation**, and introduces a formal **extensions framework** for opt-in modular functionality. Several older client/server features are deprecated under a new feature-lifecycle policy. This is the direction future Skald MCP work should anticipate; the per-request shift and the Tasks extension are the most impactful changes to plan for.

## At a glance
- **Protocol style:** stateless, self-contained requests (no session state inferred across requests)
- **Capability negotiation:** per-request, via `_meta.io.modelcontextprotocol/clientCapabilities`
- **Transports:** **stdio** (newline-delimited JSON-RPC over a client-launched subprocess) and **Streamable HTTP** (single MCP endpoint; `POST` per message, request-scoped SSE for replies). Custom transports **MAY** be defined. (HTTP+SSE was deprecated earlier, in `2025-03-26`.)
- **Server features:** Resources, Prompts, Tools
- **Client features (core):** **Elicitation only**. (Sampling and Roots are deprecated — see below.)
- **Extensions (opt-in):** Tasks (`io.modelcontextprotocol/tasks`), MCP Apps (`io.modelcontextprotocol/ui`), Skills over MCP (in Working Group / experimental), OAuth client-credentials, enterprise-managed authorization, …
- **Authorization:** OAuth 2.1 + PKCE, HTTP transports only; stdio uses environment credentials

## The redesign (why this matters)
Three structural changes define this draft:

1. **Stateless base protocol.** All information needed to process a request is contained in the request itself — protocol version, client identity, and capabilities travel in the `_meta` field on every request. Servers **MUST NOT** rely on prior requests to establish context, and **SHOULD** handle requests associated with multiple tasks/threads/conversations.
2. **Per-request capability negotiation.** The connection-level `initialize` handshake is gone for modern clients; capabilities are declared on each request, and the server's capabilities are discoverable via `server/discover`. This replaces session-scoped negotiation entirely.
3. **Extensions framework.** Optional, always opt-in, modular capabilities (Tasks, MCP Apps, …) negotiated through the `extensions` field of capabilities. Core client features are trimmed to Elicitation; Sampling, Roots, and Logging move to **deprecated** status under a formal lifecycle policy (not silent removal).

## Architecture
The client-host-server topology is retained. A **host** creates and manages multiple **client** instances; each client has a 1:1 relationship with exactly one **server** and enforces isolation (a server cannot see the whole conversation or other servers). The change is that clients now "attach protocol version and capabilities to every request" rather than establishing a session. Servers expose resources/tools/prompts and **MAY** request client input (sampling/elicitation/roots) by returning an `InputRequiredResult` within a reply. Design principles are unchanged: servers should be easy to build, highly composable, mutually isolated, and progressively extensible.

## Base Protocol

### Messages
All messages **MUST** be [JSON-RPC 2.0](https://www.jsonrpc.org/specification), UTF-8 encoded. Three kinds: **Requests** (with a string/integer `id`; the id **MUST NOT** be `null` and **MUST NOT** match any in-flight request id), **Responses**, and **Notifications** (no `id`; receiver **MUST NOT** reply). A notable addition is **polymorphic results**: every result **MUST** include a `resultType` field — `"complete"` (final content), `"input_required"` (an `InputRequiredResult`, driving a multi-round-trip request), or extension-defined values; an absent `resultType` **MUST** be treated as `"complete"` for backward compatibility. JSON-RPC's reserved server-error range is now partitioned: `-32000`–`-32019` is legacy (new codes **MUST NOT** be allocated there); `-32020`–`-32099` is reserved for the MCP specification. Defined codes: `-32020` `HeaderMismatch`, `-32021` `MissingRequiredClientCapability`, `-32022` `UnsupportedProtocolVersion`. Three message patterns are supported: **Request/Response**, **Multi Round-Trip Requests (MRTR)**, and **Subscribe/Notify** (`subscriptions/listen`).

### Lifecycle / Negotiation
There is **no negotiation handshake** in the modern protocol. Every request declares its protocol version in `_meta` (also mirrored in the `MCP-Protocol-Version` header on HTTP). If the server does not support the requested version it **MUST** respond `UnsupportedProtocolVersionError` (`-32022`) listing its `supported` versions; the client **SHOULD** pick a mutually supported version and retry. Servers **MUST** implement `server/discover`; clients **MAY** call it first to learn supported versions and capabilities up front, but are free to invoke any RPC inline and handle the error. Capability negotiation is therefore **per-request**: clients advertise capabilities in `_meta.io.modelcontextprotocol/clientCapabilities`; servers expose theirs through `server/discover`. Extensions are advertised the same way, via a `capabilities.extensions` map keyed by extension identifier.

### Transports
A transport is a binding (framing + metadata + cancellation signalling); message semantics are identical on every transport. Two standard bindings:
- **stdio** — newline-delimited JSON-RPC over a client-launched subprocess; `notifications/cancelled` abandons an in-flight request. Clients **SHOULD NOT** tie the subprocess lifetime to a single task/thread/conversation.
- **Streamable HTTP** — every client message is a `POST` to a single MCP endpoint; a reply arrives as a JSON object or a request-scoped SSE stream. Selected `_meta` fields are mirrored into HTTP headers so intermediaries can route without parsing the body (body remains source of truth). Cancellation closes the response stream.

Custom transports **MAY** be defined but **MUST** preserve JSON-RPC format, the message patterns, and the per-request metadata model; those over a reliable byte stream **SHOULD** reuse stdio framing.

### Authorization
Optional. For HTTP transports it is built on **OAuth 2.1** with PKCE; stdio **SHOULD NOT** use it and instead retrieves credentials from the environment. The MCP server is an OAuth 2.1 *resource server* and **MUST** implement OAuth 2.0 Protected Resource Metadata ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728.html)), signalling the metadata URL via `WWW-Authenticate` on `401`. Authorization servers **MUST** provide discovery via OAuth 2.0 Authorization Server Metadata ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)) or OIDC Discovery; clients **MUST** support both. Client registration priority: **Client ID Metadata Documents** (new, preferred) → pre-registered → **Dynamic Client Registration** ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591), now **deprecated**). Resource Indicators ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html)) and the `iss` parameter ([RFC 9207](https://datatracker.ietf.org/doc/html/rfc9207)) apply. Servers **SHOULD** advertise required `scope` in the `WWW-Authenticate` challenge; clients follow a least-privilege scope-selection strategy with step-up authorization.

### Versioning
Protocol versions are date strings (e.g. `2026-07-28`). Terminology: **Modern** = per-request metadata (this revision and later); **Legacy** = `initialize`-handshake versions (`2025-11-25` and earlier); **Dual-era** = implementations supporting both. A dual-era server selects behaviour from how the client opens: a request carrying modern `_meta` is served statelessly; an `initialize` request selects legacy semantics. Clients detect a server's era via transport-specific probing (stdio: `server/discover` and fall back on any non-modern error; HTTP: attempt a modern request and inspect a `400` body). The era is a property of the server and **SHOULD** be cached for the process/origin lifetime. A full compatibility matrix (Modern/Legacy/Dual-era × client/server) is specified.

## Server Features
Servers advertise implemented primitives in their capabilities. Control model unchanged: **Prompts** user-controlled, **Resources** application-controlled, **Tools** model-controlled.

### Resources
Context/data exposed by the server, addressed by URI. Discovery and reading are RPC-driven; clients open a `subscriptions/listen` stream to receive `notifications/*` (tagged with a `subscriptionId`) when resources change. The old `-32002` "resource not found" code is replaced by `-32602`; clients **SHOULD** still accept `-32002` from legacy servers.

### Prompts
Templated messages/workflows invoked by user choice (e.g. slash commands, menu options). Listed and fetched on demand; the model does not pick them autonomously.

### Tools
Functions exposed to the LLM (model-controlled). Tool invocation requires the server to declare tool capabilities. Tools **MAY** return structured output, and — via extensions such as MCP Apps — can reference interactive UI resources.

### Utilities
Cross-cutting concerns now consist of **Configuration**, **Progress tracking**, **Cancellation**, and **Error reporting**. **Logging is no longer a core utility: it is deprecated** (SEP-2577). The migration path is to log to `stderr` for stdio transports and to use [OpenTelemetry](https://opentelemetry.io/) for observability.

## Client Features
### Elicitation
The **only core client feature**. Servers **MAY** request user input during request processing by returning an `InputRequiredResult` containing an `elicitation/create` request. Two modes:
- **Form mode** — in-band structured data with optional JSON-Schema validation. Schemas are restricted to flat objects of primitive properties (string/number/integer/boolean/enum). Servers **MUST NOT** use form mode to request secrets (passwords, API keys, tokens, payment credentials).
- **URL mode** — out-of-band interaction via URL navigation; data (other than the URL) is **not** exposed to the client. Servers **MUST** use URL mode for sensitive information.

Clients supporting elicitation **MUST** declare `_meta.io.modelcontextprotocol/clientCapabilities.elicitation` (with `form` and/or `url` sub-capabilities; empty object ≡ `form` only) on each request. Clients **MUST** make clear which server is asking, and provide decline/cancel options.

## Extensions (new)
### Overview
Extensions are optional additions beyond the core protocol — modular, specialized, or experimental. They are identified by `{vendor-prefix}/{extension-name}` (official ones use `io.modelcontextprotocol/`), advertised in the `capabilities.extensions` map (per-request), and **always disabled by default** — requiring explicit opt-in from both sides. If one side supports an extension the other lacks, it **MUST** fall back to core behaviour or reject with an appropriate error. Extensions evolve independently and **SHOULD** use capability flags or in-extension versioning rather than new identifiers for breaking changes. Official extensions live in `ext-*` repositories; experimental ones (incubating in Working/Interest Groups) use the `experimental-ext-` prefix.

### Tasks
Extension `io.modelcontextprotocol/tasks` (repo `ext-tasks`). Lets a server return a durable handle instead of blocking on long-running operations (CI pipelines, batch jobs, human approvals). Flow: the client declares the extension per-request; when a request will be long-running the server returns a `CreateTaskResult` (`resultType: "task"`) carrying a `taskId`, initial status, TTL, and suggested `pollIntervalMs`; the client polls `tasks/get`; if status becomes `input_required`, the response carries `inputRequests` which the client fulfils via `tasks/update`; terminal states are `completed` / `failed` / `cancelled`. The client **MAY** send `tasks/cancel` (cooperative). Servers **MAY** push `notifications/tasks` via `subscriptions/listen`; polling remains the default. Task IDs are durable and survive client disconnect/restart.

### Skills over MCP
An emerging extension (Skills Over MCP Working Group; current direction is SEP-2640, a Resources-based Extensions-Track proposal; reference work in `experimental-ext-skills`). It defines how "agent skills" — rich, structured instructions for agent workflows — are discovered, distributed, and consumed through MCP. **Not yet a finalized official extension** at the time of this draft.

### MCP Apps
Extension `io.modelcontextprotocol/ui` (repo `ext-apps`). Lets a server return interactive HTML (charts, forms, video players, dashboards) rendered inline in the conversation. A tool declares a UI reference via `_meta.ui.resourceUri` pointing at a `ui://` resource; the host fetches and renders the HTML inside a **sandboxed iframe**, isolated from the parent page. Communication uses a JSON-RPC dialect over `postMessage` with a `ui/` method prefix (e.g. `ui/initialize`); `_meta.ui.csp` and `_meta.ui.permissions` control loading and capabilities. Already supported by several clients (Claude, Claude Desktop, VS Code Copilot, Microsoft 365 Copilot, Goose, Postman, and others).

## Changes vs 2025-11-25
- **Stateless base protocol**: requests are self-contained; servers **MUST NOT** rely on connection/session state.
- **Per-request capability negotiation** replaces the `initialize` handshake; `server/discover` provides up-front discovery.
- **Polymorphic results** (`resultType`: `complete` / `input_required` / extension values) and a formal MRTR pattern via `InputRequiredResult`.
- **New spec-reserved error range** (`-32020`–`-32099`) and codes (`HeaderMismatch`, `MissingRequiredClientCapability`, `UnsupportedProtocolVersion`).
- **Elicitation is the only core client feature**; **Sampling and Roots are deprecated** (SEP-2577), retained ≥12 months, eligible for removal in the first revision released on/after **2027-07-28**. Migration: Roots → tool parameters/resource URIs/server configuration; Sampling → integrate directly with LLM provider APIs.
- **Logging deprecated** as a utility (SEP-2577) → `stderr` / OpenTelemetry.
- **Dynamic Client Registration deprecated** (→ Client ID Metadata Documents).
- **Extensions framework** introduced; **Tasks** stabilized as an official extension (`io.modelcontextprotocol/tasks`), **MCP Apps** added (`io.modelcontextprotocol/ui`), **Skills over MCP** incubating.

## Skald relevance
Skald's MCP surface lives in `crates/mcp-client/` (the `McpServer` stdio wrapper, `McpHttpServer`, and the `McpServerClient` trait), `src/core/mcp/` (`McpManager`), and `src/core/elicitation/` (`ElicitationManager` + bridge, surfaced in the Inbox). Two draft changes matter most for future planning: (1) the **stateless / per-request** shift means capability and version metadata must be attached to every request rather than assumed from a session, and `server/discover` becomes the discovery primitive; (2) the **Tasks extension** maps closely onto Skald's async/background work and would let MCP servers return durable, pollable handles for long-running operations. Skald's existing elicitation bridge already corresponds to the one remaining core client feature, so that path forward is well aligned. Until the draft finalizes, treat Sampling/Roots/Logging as deprecated-but-present for interop only.

## References
- [Specification (draft)](https://modelcontextprotocol.io/specification/draft)
- [Architecture](https://modelcontextprotocol.io/specification/draft/architecture)
- [Base protocol](https://modelcontextprotocol.io/specification/draft/basic)
- [Versioning and compatibility](https://modelcontextprotocol.io/specification/draft/basic/versioning)
- [Transports](https://modelcontextprotocol.io/specification/draft/basic/transports)
- [Authorization](https://modelcontextprotocol.io/specification/draft/basic/authorization)
- [Server features](https://modelcontextprotocol.io/specification/draft/server)
- [Client features — Elicitation](https://modelcontextprotocol.io/specification/draft/client/elicitation)
- [Deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated)
- [Extensions overview](https://modelcontextprotocol.io/extensions/overview)
- [Tasks extension](https://modelcontextprotocol.io/extensions/tasks/overview)
- [MCP Apps extension](https://modelcontextprotocol.io/extensions/apps/overview)
- [Skills over MCP Working Group](https://modelcontextprotocol.io/community/working-groups/skills-over-mcp)
- [Schema (schema.ts)](https://github.com/modelcontextprotocol/specification/blob/main/schema/draft/schema.ts)
- [Release 2026-07-28-RC](https://github.com/modelcontextprotocol/modelcontextprotocol/releases/tag/2026-07-28-RC)
