# MCP 2026-07-28 Protocol Brief The protocol source of truth for every MCPA lesson, quiz, and assessment question in this curriculum. Every fact below was read from the primary specification pages on 2026-09-24. If a lesson disagrees with this brief, the lesson is wrong. If this brief disagrees with the specification, the specification wins and this brief must be corrected with a new date. - Specification: https://modelcontextprotocol.io/specification/2026-07-28 - TypeScript schema (source of truth for every message): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2026-07-28/schema.ts - Changelog since 2025-11-25: https://modelcontextprotocol.io/specification/2026-07-28/changelog - Deprecated features registry: https://modelcontextprotocol.io/specification/2026-07-28/deprecated - MCP Apps extension specification (stable 2026-01-26): https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx - The MCPA exam page says to prepare against the 2026-07-28 release and that the exam focuses on "MCP internals, including architecture, message flow, client-server interactions, capabilities, tools, resources, prompts, transports, and security considerations." Exam facts (fee, duration, domains, weights) live in `source-verification-ledger.md`, not here. ## 1. The era model | Term | Meaning | |---|---| | Modern | 2026-07-28 and later. Version, identity, and capabilities travel as per-request metadata. | | Legacy | 2025-11-25 and earlier. A connection starts with an `initialize` handshake that creates a session. | | Dual-era | An implementation that supports both, with an explicit era decision before parsing. | Revision identifiers are dates in YYYY-MM-DD form marking the last day backwards incompatible changes were made. A revision is Draft (in progress), Current (in use and still able to take backwards compatible changes; 2026-07-28 today), or Final (complete and frozen). Revision history, newest first: | Revision | Headline changes | |---|---| | 2026-07-28 | Stateless core; `initialize` and sessions removed; `server/discover`; Multi Round-Trip Requests (MRTR); `resultType`; `subscriptions/listen`; tasks moved to an extension; roots, sampling, logging, and Dynamic Client Registration deprecated; cacheable results; required HTTP headers; error-code allocation policy; feature lifecycle policy. | | 2025-11-25 | Icons; incremental scope consent via `WWW-Authenticate`; tool-name guidance; URL-mode elicitation; tool calling inside sampling; Client ID Metadata Documents recommended; experimental tasks; input validation errors become tool execution errors (SEP-1303); JSON Schema 2020-12 default dialect; governance and SDK tiering. | | 2025-06-18 | Structured tool output (`outputSchema`, `structuredContent`); resource links in tool results; elicitation; OAuth resource-server classification with Protected Resource Metadata; resource indicators required; `MCP-Protocol-Version` header on HTTP; JSON-RPC batching removed. | | 2025-03-26 | Streamable HTTP replaces HTTP+SSE; OAuth 2.1 authorization framework; tool annotations; audio content; completions capability. | | 2024-11-05 | First public revision: stdio and HTTP+SSE transports, `initialize` handshake. | What 2026-07-28 removed (never teach these as current): - `initialize` request and `notifications/initialized` (SEP-2575). - Protocol sessions and the `Mcp-Session-Id` header (SEP-2567). - The Streamable HTTP GET stream endpoint and session `DELETE`. - `resources/subscribe` and `resources/unsubscribe` (replaced by `subscriptions/listen`). - `ping`, `logging/setLevel`, `notifications/roots/list_changed`. - SSE resumability and redelivery (`Last-Event-ID`, SSE event IDs). - Server-initiated requests on any stream (replaced by MRTR). - Tasks in the core protocol (moved to `io.modelcontextprotocol/tasks`, redesigned: no `tasks/result`, no `tasks/list`). - `notifications/elicitation/complete` and the `elicitationId` field of URL-mode elicitation. - Error codes `-32002` (resource not found, now `-32602`) and `-32042` (URL elicitation required). ## 2. Messages All messages follow JSON-RPC 2.0 with these MCP rules: - **Request**: `{"jsonrpc":"2.0","id":,"method":...,"params":{...}}`. The id MUST NOT be null and MUST NOT repeat an id still awaiting a response. - **Result response**: same id as the request; `result` MUST include `resultType`. - `"complete"`: the final content. - `"input_required"`: an `InputRequiredResult` (MRTR, section 7). - Extensions MAY add values only when advertised through capabilities (the tasks extension adds `"task"`). - A client MUST treat an unrecognized `resultType` as invalid, and MUST treat an absent `resultType` (legacy server) as `"complete"`. - **Error response**: `{"jsonrpc":"2.0","id":...,"error":{"code":,"message":...,"data":...}}`. The id is omitted only when it could not be read from a malformed request. - **Notification**: a `method` and optional `params`, and MUST NOT include an id. The receiver MUST NOT respond. - No JSON-RPC batching (removed in 2025-06-18). On Streamable HTTP each POST body is exactly one request or notification. ## 3. `_meta` and per-request protocol fields Key names: an optional prefix (dot-separated labels plus `/`, reverse DNS recommended) and a name that begins and ends with an alphanumeric character. Any prefix whose second label is `modelcontextprotocol` or `mcp` is reserved for MCP (`io.modelcontextprotocol/`, `dev.mcp/`); `com.example.mcp/` is not reserved. Required on every client request (missing ones make the request malformed: JSON-RPC `-32602`, and HTTP `400 Bad Request`): | Key | Required | Meaning | |---|---|---| | `io.modelcontextprotocol/protocolVersion` | yes | e.g. `"2026-07-28"` | | `io.modelcontextprotocol/clientCapabilities` | yes | capabilities relevant to this request (may be `{}`) | | `io.modelcontextprotocol/clientInfo` | no (SHOULD) | `Implementation`: `name`, `version`, optional `title`, `description`, `websiteUrl`, `icons` | | `io.modelcontextprotocol/logLevel` | no | minimum log level for this request (deprecated logging feature) | | `progressToken` | no | opts the request into `notifications/progress` | On results, servers SHOULD include `io.modelcontextprotocol/serverInfo` in `result._meta`. `clientInfo` and `serverInfo` are self-reported: use them for display, logging, and debugging, never for security decisions. Other reserved keys: `io.modelcontextprotocol/subscriptionId` (on every message of a `subscriptions/listen` stream), and `traceparent`, `tracestate`, `baggage` (OpenTelemetry context, W3C Trace Context and W3C Baggage formats; the one exception to the prefix rule, SEP-414). Minimal modern request: ```json { "jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": { "name": "get_weather", "arguments": {"location": "Pune"}, "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}, "io.modelcontextprotocol/clientInfo": {"name": "ExampleClient", "version": "1.0.0"} } } } ``` ## 4. Statelessness - All information needed to process a request is in the request. A server MUST NOT rely on prior requests on the same connection for capabilities, version, or identity. - A stdio process or HTTP connection is not a session. Clients may interleave unrelated requests on one transport. - State that spans requests (long-running work, carts, browser contexts) MUST be referenced by an explicit identifier the client passes on each request: a server-minted handle returned in a tool result and passed back as an ordinary argument (SEP-2567). Handles are names, not capabilities: authorize the caller against the handle on every call, keep handles opaque, state their lifetime in the tool description, and report an expired handle as a tool execution error. - `tools/list`, `resources/list`, and `prompts/list` MUST NOT vary per connection or as a side effect of other requests. They MAY vary by the authorization presented on the request. - Why: any replica can serve any request, retries are safe to route anywhere, and load balancers need no stickiness. ## 5. Error codes | Code | Name | Use | |---|---|---| | -32700 | Parse error | invalid JSON | | -32600 | Invalid Request | not a valid request object | | -32601 | Method not found | unknown method (on HTTP: `404 Not Found`) | | -32602 | Invalid params | unknown tool; missing required `_meta`; resource not found; invalid prompt name or missing prompt argument; invalid cursor; invalid log level | | -32603 | Internal error | server failure | | -32020 | HeaderMismatch | HTTP header disagrees with body, or a required header is missing (HTTP `400`) | | -32021 | MissingRequiredClientCapability | `data.requiredCapabilities` lists what the client must declare (HTTP `400`) | | -32022 | UnsupportedProtocolVersion | `data.supported` (list) and `data.requested` (HTTP `400`) | Allocation policy (2026-07-28): - `-32000` to `-32019`: legacy. New codes MUST NOT be allocated there and new implementations SHOULD NOT use it at all. - `-32020` to `-32099`: reserved for the MCP specification. Emitting an undefined code in this range is forbidden. - Implementations of this revision MUST NOT emit `-32002` or `-32042`. Clients SHOULD still accept `-32002` from older servers as "resource not found". - Application-defined error codes SHOULD sit outside `-32768` to `-32000`. Two error channels for tools (the most tested distinction): 1. **Protocol errors** (JSON-RPC `error`): unknown tool (`-32602`), a request that fails the `CallToolRequest` schema, server errors. 2. **Tool execution errors** (a normal result with `isError: true` and explanatory `content`): API failures, input validation errors such as a wrong date format or an out-of-range value, business-logic refusals, expired handles. These go back to the model so it can self-correct (SEP-1303, 2025-11-25). An unknown tool is `-32602`, not `-32601`. Arguments that violate the tool's `inputSchema` are a tool execution error, not `-32602`. ## 6. `server/discover` and version negotiation - Servers MUST implement `server/discover`. The request carries only the standard `_meta`. - `DiscoverResult` (a cacheable result): `resultType`, `supportedVersions`, `capabilities`, optional `instructions` (natural-language guidance for the model), `_meta["io.modelcontextprotocol/serverInfo"]`, `ttlMs`, `cacheScope`. - Calling it is optional. A client may send any request directly and handle `UnsupportedProtocolVersionError` by retrying with a version from `data.supported`. - On stdio, a dual-era client SHOULD probe with `server/discover` first: a `DiscoverResult` means modern; a recognized modern error such as `-32022` means modern with a different version (do not fall back); any other error or a timeout means legacy (fall back to `initialize`). The fallback MUST NOT key on one specific error code. - On HTTP, a dual-era client attempts a modern request and inspects a `400` body: a recognized modern JSON-RPC error means modern; anything else means legacy. - Cache the era decision per server process (stdio) or origin (HTTP). - A modern-only server SHOULD name its supported versions in any error it returns to `initialize`. Server capabilities: `tools {listChanged}`, `resources {listChanged, subscribe}`, `prompts {listChanged}`, `completions {}`, `logging {}` (deprecated), `extensions {...}`, `experimental {...}`. Client capabilities: `elicitation {form, url}`, `sampling {context, tools}` (deprecated), `roots {}` (deprecated), `extensions {...}`, `experimental {...}`. A server MUST NOT rely on a capability the client did not declare on that request; if it needs one, it returns `-32021`. ## 7. Multi Round-Trip Requests (MRTR) Replaces every server-initiated request (SEP-2322). Breaking change. 1. The client sends a request. 2. The server returns `{"resultType":"input_required","inputRequests":{...},"requestState":"..."}`. It MUST include at least one of `inputRequests` or `requestState`. 3. `inputRequests` is a map from server-chosen keys to request objects, each exactly one of `elicitation/create`, `sampling/createMessage`, or `roots/list`. The server MUST NOT include a request type the client has not declared a capability for. 4. The client gathers the answers and **retries the original request with a new JSON-RPC id**, adding `params.inputResponses` (same keys) and echoing `params.requestState` byte for byte. It MUST NOT include `requestState` if none was given. 5. The server completes with a final result, or asks again with another `InputRequiredResult`. Only `tools/call`, `prompts/get`, and `resources/read` may return `InputRequiredResult`. `requestState` is attacker-controlled input: if it influences authorization, resource access, or business logic, protect it with an HMAC or AEAD, and bind it to the authenticated principal, a short expiry, and a digest of the originating request. Single-use must be enforced server-side. `input_required` results and MRTR retries are never cached. ## 8. Notifications, subscriptions, progress, cancellation - `subscriptions/listen` opens a long-lived stream. `params.notifications` is a filter: `toolsListChanged`, `promptsListChanged`, `resourcesListChanged` (booleans) and `resourceSubscriptions` (URI list). The server MUST NOT send unrequested types. - The first message MUST be `notifications/subscriptions/acknowledged`, echoing the subset the server will honor. Every message on the stream carries `_meta["io.modelcontextprotocol/subscriptionId"]`, whose value is the JSON-RPC id of the `subscriptions/listen` request. - Stream notifications: `notifications/tools/list_changed`, `notifications/prompts/list_changed`, `notifications/resources/list_changed`, `notifications/resources/updated` (with `uri`). - Request-scoped notifications (`notifications/progress`, `notifications/message`) flow only on the response stream of the request they relate to, never on a listen stream. - Progress: the client sets `_meta.progressToken` (string or integer, unique among active requests); `notifications/progress` carries `progressToken`, a strictly increasing `progress`, optional `total` and `message`; notifications stop when the request completes. - Cancellation: on Streamable HTTP, closing the request's SSE response stream is the cancellation (no message). On stdio, the client sends `notifications/cancelled` with `requestId` and optional `reason`. The server sends `notifications/cancelled` only to tear down a `subscriptions/listen` stream. Late responses to cancelled requests are ignored; races are normal. - Timeouts SHOULD exist for every request, with a hard maximum even when progress arrives. - A server ending a subscription gracefully SHOULD send a `subscriptions/listen` result with `resultType: "complete"` and the subscription id in `_meta`. ## 9. Transports **stdio** - The client launches the server as a subprocess. Newline-delimited JSON-RPC, no embedded newlines. - `stdout` carries only MCP messages; `stderr` carries logs (not necessarily errors). - The server never writes requests to `stdout`. All metadata is inline in `_meta` (no header layer). - Shutdown: the client closes `stdin`, waits, then escalates (SIGTERM, SIGKILL). On unexpected exit the client restarts the server, retries lost requests, and re-sends `subscriptions/listen`. - Authorization: stdio implementations SHOULD NOT use the OAuth flow and instead read credentials from the environment. **Streamable HTTP** - One MCP endpoint that accepts POST (e.g. `https://example.com/mcp`). Every client message is its own POST. - `Accept` lists both `application/json` and `text/event-stream`. The response to a request is either one JSON object or an SSE stream scoped to that request; the client MUST support both. - A notification POST returns `202 Accepted` with no body. - SSE streams carry request-related notifications, then the final response. The server MUST NOT send requests on any stream. - No GET endpoint, no `Mcp-Session-Id`, no `Last-Event-ID`. A modern-only server answers GET or DELETE with `405` and ignores session and event-id headers. - Security: validate `Origin` (reply `403` if present and invalid) to stop DNS rebinding; bind to `127.0.0.1` when local; authenticate. - Required headers: `MCP-Protocol-Version` on every POST (must equal the `_meta` version, else `400` plus `-32020`); `Mcp-Method` on every request; `Mcp-Name` on `tools/call`, `resources/read`, and `prompts/get` (from `params.name` or `params.uri`). - `x-mcp-header` in a tool's input schema mirrors a primitive argument (integer, string, or boolean, never `number`, and only properties statically reachable from the schema root) into `Mcp-Param-{Name}`. Server developers SHOULD NOT mark sensitive parameters (passwords, API keys, tokens, PII), because header values are visible to intermediaries. Values that are not plain ASCII use the sentinel `=?base64?...?=`. Clients on HTTP MUST reject a tool whose `x-mcp-header` values violate the constraints by excluding it from their `tools/list` result. Servers MUST reject a header that disagrees with the body (`-32020`). - Status codes: unsupported version `400` plus `-32022`; missing capability `400` plus `-32021`; unknown method `404` plus `-32601`; missing or invalid token `401`; insufficient scope `403`. - HTTP+SSE, the 2024-11-05 transport, has been Deprecated since 2025-03-26 (SEP-2596); its earliest removal is three months after SEP-2596 reaches Final. ## 10. Server primitives Control model: tools are **model-controlled**, resources are **application-driven**, prompts are **user-controlled**. **Tools** - `tools/list` supports pagination and caching, and SHOULD be deterministically ordered (client caching, LLM prompt-cache hits). - Tool definition: `name`, optional `title`, `description`, optional `icons`, `inputSchema` (a JSON Schema object, never null; for no parameters use `{"type":"object","additionalProperties":false}`), optional `outputSchema`, optional `annotations`, optional `_meta`. - Names SHOULD be 1 to 128 characters of `A-Za-z0-9_-.`, case-sensitive, unique within a server. Aggregators SHOULD prefix names with a server identifier; `serverInfo.name` is not unique. - Annotations are hints and MUST be treated as untrusted unless the server is trusted: `title`, `readOnlyHint` (default false), `destructiveHint` (default true, meaningful only when not read-only), `idempotentHint` (default false), `openWorldHint` (default true). - `tools/call` params: `name`, `arguments`, and on retry `inputResponses` and `requestState`. - `CallToolResult`: `content` (list of content blocks), optional `structuredContent` (any JSON value; must conform to `outputSchema` when one exists; SHOULD also be serialized into a text block), optional `isError`. - Content blocks: `text`; `image` (`data`, `mimeType`); `audio`; `resource_link` (`uri`, `name`, optional `description` and `mimeType`); `resource` (embedded contents). All accept `annotations` (`audience`: `user` or `assistant`; `priority` 0 to 1; `lastModified`). - A human SHOULD be able to deny invocations. Servers MUST validate inputs, enforce access control, rate limit, and sanitize outputs. Clients SHOULD confirm sensitive operations, show inputs before calling, validate results, set timeouts, and log usage. **Resources** - `resources/list`, `resources/read`, `resources/templates/list` (RFC 6570 URI templates). Contents are `{uri, mimeType, text}` or `{uri, mimeType, blob}`. - Capability `resources {listChanged, subscribe}`; updates arrive through `subscriptions/listen` with `resourceSubscriptions`. - Not found: `-32602` (never an empty `contents` array). Internal: `-32603`. Accept legacy `-32002`. - URI schemes: `https://` only when the client can fetch it directly; `file://` (need not be a real filesystem; sanitize paths against traversal); `git://`; custom schemes per RFC 3986. **Prompts** - `prompts/list`, `prompts/get` with `arguments`; results carry `messages` with a `role` (`user` or `assistant`) and a content block. - Errors: invalid name or missing required argument is `-32602`; internal is `-32603`. **Completion**: `completion/complete` with `ref` (`ref/prompt` by name, or `ref/resource` by URI or template), `argument {name, value}`, and `context.arguments`; returns at most 100 `values` plus `total` and `hasMore`. Capability `completions {}`. **Pagination**: opaque `cursor` and `nextCursor`; the server picks page size; an empty string is a valid cursor; an invalid cursor is `-32602`. **Caching** (SEP-2549): `server/discover`, `tools/list`, `prompts/list`, `resources/list`, `resources/templates/list`, and `resources/read` results with `resultType: "complete"` MUST carry `ttlMs` (an integer, 0 or more) and `cacheScope` (`"public"` or `"private"`). `ttlMs` is a freshness hint, not a polling interval; a list-changed notification invalidates the cache immediately. `"private"` caches never cross authorization contexts. `cacheScope` is never an access control. ## 11. Client features **Elicitation** (via MRTR `inputRequests`, method `elicitation/create`) - Form mode: `mode: "form"` (optional), `message`, and `requestedSchema` (a flat object of primitive properties). URL mode: `mode: "url"`, `message`, `url` (out-of-band pages such as OAuth consent or payment, where sensitive data should not pass through the client). - `ElicitResult.action`: `accept` (with `content` for form mode), `decline`, or `cancel`. - Client capability: `elicitation {form, url}`. **Deprecated features** (the specification's deprecated registry; every one is still functional until removed): | Feature | Deprecated by | Deprecated in | Migration | Earliest removal | |---|---|---|---|---| | Roots | SEP-2577 | 2026-07-28 | pass directories via tool parameters, resource URIs, or server configuration | first revision released on or after 2027-07-28 | | Sampling | SEP-2577 | 2026-07-28 | call LLM provider APIs directly | first revision released on or after 2027-07-28 | | Logging | SEP-2577 | 2026-07-28 | log to `stderr` (stdio) or use OpenTelemetry | first revision released on or after 2027-07-28 | | Dynamic Client Registration | PR #2858 | 2026-07-28 | Client ID Metadata Documents | first revision released on or after 2027-07-28 | | `includeContext: "thisServer" \| "allServers"` | SEP-2596 | 2025-11-25 | omit, or use `"none"` | follows Sampling | | HTTP+SSE transport | SEP-2596 | 2025-03-26 | Streamable HTTP | three months after SEP-2596 reaches Final | Logging still works per request: a request with `_meta["io.modelcontextprotocol/logLevel"]` may receive `notifications/message` (syslog levels from RFC 5424) on its own response stream. Without that key, the server MUST NOT emit them. ## 12. Authorization (HTTP transports) - Optional overall. HTTP implementations SHOULD conform; stdio SHOULD NOT (use environment credentials). - Roles: the MCP server is an OAuth 2.1 resource server, the MCP client is an OAuth 2.1 client, and the authorization server issues tokens. - MCP servers MUST implement Protected Resource Metadata (RFC 9728), listing at least one entry in `authorization_servers`. Discovery goes through `WWW-Authenticate: Bearer resource_metadata="..."` on a `401`, or the well-known URIs: first the path-specific `/.well-known/oauth-protected-resource/`, then the root. - Authorization server metadata: RFC 8414 or OpenID Connect Discovery. With a path issuer, the client tries in order `/.well-known/oauth-authorization-server/`, then `/.well-known/openid-configuration/`, then `/.well-known/openid-configuration`. The metadata `issuer` MUST equal the issuer used to build the URL. - PKCE is mandatory, with `S256` when capable. If `code_challenge_methods_supported` is absent, the client MUST refuse to proceed. - Resource Indicators (RFC 8707): the `resource` parameter goes on both the authorization request and the token request, holding the canonical server URI (scheme plus host, no fragment, no trailing slash by convention). Clients send it even if the authorization server ignores it. - Tokens travel in `Authorization: Bearer ` on every HTTP request, never in the query string. - Servers MUST validate the audience (the token was issued for this server) and MUST NOT accept or pass through other tokens. Upstream API calls use a separate token (token passthrough is forbidden). - Issuer validation (RFC 9207): the client records the expected issuer before redirecting and compares the returned `iss` exactly (no normalization); mismatches are rejected, including on error responses. This defends against mix-up attacks. - Scopes: use the `scope` from the first `WWW-Authenticate` challenge, otherwise `scopes_supported`. At runtime, an insufficient scope returns `403` with `error="insufficient_scope"` and the full set of required scopes in one challenge. The client steps up with the union of old and new scopes and a limited number of retries. - Refresh tokens: never assume one is issued; public clients' refresh tokens are rotated; servers should not demand `offline_access`. - Status codes: `401` (no token or invalid token), `403` (insufficient scope), `400` (malformed authorization request). **Client registration priority**: (1) pre-registered credentials; (2) Client ID Metadata Documents when the authorization server advertises `client_id_metadata_document_supported`; (3) Dynamic Client Registration (deprecated) when a `registration_endpoint` exists; (4) ask the user. - CIMD: `client_id` is an HTTPS URL with a path, pointing at JSON containing at least `client_id` (exactly equal to the URL), `client_name`, and `redirect_uris`. The authorization server fetches it, validates it, validates redirect URIs, and guards against SSRF. It shows extra warnings for localhost-only redirect URIs and always displays the redirect hostname. CIMD ids are portable across authorization servers. - DCR: the client MUST send `application_type` (`native` for desktop, CLI, and localhost apps; `web` for remote browser apps). - Authorization-server binding: persisted credentials are keyed by the issuer, never reused with a different authorization server, and re-registered when the authorization server changes. - Confused deputy: an MCP proxy using a static client id MUST get user consent for each dynamically registered client before forwarding to a third-party authorization server. - Communication: every authorization-server endpoint uses HTTPS; redirect URIs are localhost or HTTPS and registered exactly; use and verify `state`. **Authorization extensions** (never active by default, negotiated through `extensions`): OAuth Client Credentials for machine-to-machine clients (CI, daemons) and Enterprise-Managed Authorization for central IdP policy. ## 13. Security checklist beyond authorization - Tool descriptions, annotations, icons, and results are untrusted input to the model (poisoning, prompt injection, rug pulls through changed definitions). - Icons: only `https:` or `data:` URIs, no credentials when fetching, same origin as the server, treat SVG as potentially executable. - JSON Schema: never auto-dereference a network `$ref` (opt-in only, with a host allowlist, no private addresses, timeouts); bound composition keywords against denial of service. - Streamable HTTP: `Origin` validation, localhost binding, authentication. - MRTR `requestState`: integrity protection, principal binding, expiry, request binding. - Logs never contain credentials, secrets, or personal data. - Caching: `"public"` responses can be shared across users and tokens; per-primitive access control is still required. ## 14. Extensions - Identifier: `{vendor-prefix}/{name}` under the `_meta` naming rules. Official extensions use `io.modelcontextprotocol/`; third parties use a reversed domain they own (`com.example/my-extension`). - Negotiation: clients declare support in `io.modelcontextprotocol/clientCapabilities.extensions` on each request; servers declare theirs in `server/discover` `capabilities.extensions`. The value is the extension's settings object (`{}` means supported with no settings). - If only one side supports an extension, the supporting side falls back to core behavior or rejects the request with an appropriate error. Extensions are disabled by default and need explicit opt-in. A breaking change needs a new identifier (e.g. `-v2`). - Process: SEP-2133 (Extensions Track SEP, a reference implementation in an official SDK, review by the Core Maintainers). Experimental repositories use the `experimental-ext-` prefix and belong to a Working Group or Interest Group. - Official extensions: - **Tasks** `io.modelcontextprotocol/tasks` (SEP-2663): the server may answer a supported request with `resultType: "task"` (`CreateTaskResult` with `taskId`, status, TTL, suggested poll interval). The client polls `tasks/get`, supplies mid-flight input with `tasks/update` (`inputResponses`), and may send `tasks/cancel` (cooperative). Statuses: `working`, `input_required`, `completed` (carries `result`), `failed` (carries `error`), `cancelled`. There is no `tasks/result` and no `tasks/list`. - **MCP Apps** `io.modelcontextprotocol/ui` (SEP-1865): a tool's `_meta.ui` carries `resourceUri`, pointing to a `ui://` resource of type `text/html;profile=mcp-app` fetched with an ordinary `resources/read`, and `visibility` (default `["model", "app"]`; the host MUST NOT list a tool to the agent when it lacks `"model"` and MUST reject an app's `tools/call` for a tool lacking `"app"`). The UI resource's own `_meta.ui` (on its `resources/read` contents entry) carries `csp`, an object of optional `connectDomains`, `resourceDomains`, `frameDomains`, and `baseUriDomains` origin lists, and `permissions`, an object of optional `camera`, `microphone`, `geolocation`, and `clipboardWrite` flags set to `{}`. The host MUST build its CSP from the declared domains, MUST NOT allow undeclared domains, MAY restrict further, and MUST apply a restrictive default (`connect-src 'none'`) when `csp` is omitted; it MAY honor `permissions`, and apps SHOULD NOT assume they were granted. The view runs in a sandboxed iframe (web hosts wrap it in a sandbox proxy on a different origin); the app and host talk through a JSON-RPC dialect with `ui/` methods, starting with the app's `ui/initialize`, which is unrelated to the removed core `initialize`; tools still return meaningful text for hosts without the extension. - **Skills over MCP** `io.modelcontextprotocol/skills` (SEP-2640): `skills/list` and `skills/get`, with skill files served through `resources/read`; reading `SKILL.md` does not activate a skill. Skill content is untrusted input tagged with its origin server, explicit user policy governs whether a skill is loaded at all, hosts SHOULD let users inspect a skill before loading it, MUST NOT let skill content trigger host-side code execution without per-skill approval, and MUST ignore permission-widening frontmatter such as `allowed-tools` on MCP-served skills unless the user approved that grant. - **Authorization extensions** (`ext-auth`): OAuth Client Credentials and Enterprise-Managed Authorization. ## 15. Governance, lifecycle, and ecosystem - Feature lifecycle (SEP-2596): Active, Deprecated (still specified; a migration path is required; at least a 12-month window measured from the release of the revision that deprecates it), Removed. The earliest removal is the first revision released on or after the window ends; actual removal is a Core Maintainer decision. A deprecated feature can be restored by a superseding SEP. - SEPs: PR-based workflow with files in `seps/` (SEP-1850); governance (SEP-932); Working Groups and Interest Groups (SEP-1302); Final SEPs require conformance tests (SEP-2484). - SDK tiers (SEP-1730): Tier 1 passes 100% of conformance tests, ships features before a spec release, triages within 2 business days, and fixes critical bugs within 7 days. Tier 2 passes 80% and ships features within 6 months. Tier 3 is experimental. Extensions are not required for any tier. An SDK is relegated after 4 weeks of failures. - Registry (preview): metadata only (`server.json`) that points at packages (npm, PyPI, Docker, and others) or remote URLs; reverse-DNS namespaces verified through GitHub, DNS, or HTTP challenges (`io.github.user/server`, `com.example/server`); public servers only (run your own registry, implementing the OpenAPI spec, for private ones); meant for aggregators, not direct host consumption; security scanning is delegated to package registries and aggregators. The `server.json` schema version is independent of the MCP protocol version. - Stewardship: MCP is a project of the Agentic AI Foundation (Linux Foundation). - Scale (MCP blog, 2026-07-28 release post): close to half a billion downloads a month across Tier 1 SDKs; the TypeScript and Python SDKs each passed one billion total downloads. ## 16. Traps the exam can exploit | Wrong belief | Correct 2026-07-28 fact | |---|---| | A connection starts with `initialize`. | No handshake. Every request carries version and capabilities in `_meta`; `server/discover` is optional for clients and mandatory for servers. | | The session id keeps state between calls. | No sessions. Use explicit server-minted handles as arguments. | | An unknown tool returns `-32601`. | `-32602`. `-32601` means the method itself is unknown. | | A schema-invalid argument returns `-32602`. | A tool execution error: `isError: true` with feedback the model can act on. | | Servers push requests (sampling, elicitation) to the client. | MRTR: an `input_required` result, then the client retries with `inputResponses` and a new id. | | The retry reuses the original request id. | The retry MUST use a new id and echo `requestState` exactly. | | `resources/subscribe` watches a resource. | `subscriptions/listen` with `resourceSubscriptions`. | | Tool annotations are enforced guarantees. | Hints, untrusted unless the server is trusted. | | `cacheScope: "public"` protects data. | It permits sharing across users; access control is separate. | | Progress and log notifications arrive on the listen stream. | Only on the response stream of their own request. | | Closing a stdio process cancels a request. | stdio cancels with `notifications/cancelled`; HTTP cancels by closing that request's stream. | | New servers should use `-32001` for custom errors. | `-32000` to `-32019` is legacy; `-32020` to `-32099` is reserved; app codes go outside `-32768` to `-32000`. | | Resource not found is `-32002`. | `-32602` in 2026-07-28 (accept `-32002` from older servers). | | Roots, sampling, and logging were removed. | Deprecated, still functional, eligible for removal on or after 2027-07-28. | | Dynamic Client Registration is the recommended registration. | Deprecated; Client ID Metadata Documents are preferred after pre-registration. | | A server may forward the user's token upstream. | Token passthrough is forbidden; audience validation is mandatory. | | `clientInfo` can drive authorization decisions. | Self-reported, for display and logging only. | | Streams can resume with `Last-Event-ID`. | Removed. A broken stream loses the request; reissue with a new id. | ## 17. Source conflicts resolved during the build Where two primary pages disagree, the normative record wins: `schema.ts` for message shapes, the changelog and the current specification page for what exists in 2026-07-28, and never a historical SEP draft over the published revision. Questions must not test the points marked "unspecified" as a single rule. | Point | What the pages say | Resolution | |---|---|---| | `logging/setLevel` and `notifications/roots/list_changed` | SEP-2577 lists them inside the deprecated Logging and Roots features; changelog item 5 and SEP-2575 say both methods are removed | The methods are removed in 2026-07-28. The Roots, Sampling, and Logging features are deprecated, and their surviving surface is `roots/list` and `sampling/createMessage` as MRTR input requests plus per-request `logLevel` with `notifications/message`. | | Annotations on an embedded resource | The tools page example nests `annotations` inside `resource`; `schema.ts` defines `EmbeddedResource.annotations` beside `resource`, and `ResourceContents` has no `annotations` | Follow `schema.ts`: annotations sit beside `resource`. | | Headers on a notification POST | SEP-2243's table lists `Mcp-Method` for notifications; the 2026-07-28 Streamable HTTP page says header requirements for notification POSTs are not defined, and the core protocol defines no client-to-server notification over HTTP | Unspecified in 2026-07-28. | | Rejecting a `requestState` that fails verification | MRTR says the server MUST reject it but does not name the channel | Unspecified. A tool execution error or a fresh `input_required` result are both defensible. | | HTTP status for `-32700`, `-32600`, `-32603` | Streamable HTTP names `400` for missing `_meta`, `-32020`, `-32021`, and `-32022`, and `404` for `-32601`, but nothing for these three | Unspecified. | | A server ending a subscription | The subscriptions page says the server SHOULD answer the listen request with a `complete` result for a graceful end; the cancellation page says a server sends `notifications/cancelled` only to tear down a listen stream | Both hold. A graceful end is the `complete` result; tearing down a listen stream is the only purpose for which a server may send `notifications/cancelled`. | | Tool name format | SEP-986 proposed a different length and allowed `/` | The published tools page wins: 1 to 128 characters of `A-Za-z0-9_-.`, no `/`. |