An MCP extension is an optional addition to the Model Context Protocol specification that defines capabilities beyond the core protocol. An extension has a unique identifier, is specified in its own document and works only when both the client and the server declare support for it. The specification names three kinds of purpose: modular features such as authentication, specialized ones such as industry-specific logic, and experimental ones incubated for possible inclusion in the core.
The framework for extensions was defined by SEP-2133, and the extensions field of capabilities became part of the protocol schema in specification version 2026-07-28. The first official extension, MCP Apps, was announced on 26 January 2026. Official extensions are developed in repositories of the Model Context Protocol GitHub organization. The general structure of the protocol is described in What is MCP; the per-request capability model that extensions rely on is described in Stateless MCP.
Overview
| Property | Value |
|---|---|
| Identifier format | {vendor-prefix}/{extension-name}, prefix in reverse DNS notation |
| Official prefix | io.modelcontextprotocol |
| Negotiation | extensions field of client and server capabilities |
| Stages | Experimental, Beta, Stable; promotion to the core is possible |
| Official repositories | ext- repositories of the modelcontextprotocol GitHub organization |
| Governing documents | SEP-2133 (framework), SEP-3392 (lifecycle) |
| Official extensions | MCP Apps, Tasks, OAuth Client Credentials, Enterprise-Managed Authorization, Skills over MCP, Server Cards |
| Default state in SDKs | Disabled, explicit opt-in required |
The problem extensions solve
Every feature of the core specification has to be implemented by every SDK. Some features are needed only in certain environments, such as enterprise identity management, and some are not yet settled.
Extensions separate these features from the core. An extension evolves under its own maintainers, and implementations adopt it only when they need it.
The experimental tasks of version 2025-11-25 lived in the core, and implementation experience revealed problems with their handshake, the blocking tasks/result method and the scope of tasks/list. SEP-2663 moved them into an extension to give the feature time to incubate independently of the core. MCP Apps took the opposite route: MCP-UI and the OpenAI Apps SDK had developed separate approaches to interactive results, and the extension unified them.
History
The table lists the events that shaped the extension mechanism and the official extensions.
| Date | Event |
|---|---|
| 4 June 2025 | SEP-990, the basis of Enterprise-Managed Authorization, is created |
| 21 November 2025 | SEP-1865 "MCP Apps - Interactive User Interfaces for MCP" is created |
| 25 November 2025 | Specification 2025-11-25 with experimental tasks in the core |
| 26 January 2026 | MCP Apps, the first official extension: revision 2026-01-26 of its specification |
| 23 April 2026 | SEP-2640 "Skills Extension" is created |
| 27 April 2026 | SEP-2663 "Tasks Extension" is created |
| 28 July 2026 | Specification 2026-07-28: the extensions field in capabilities, tasks moved to io.modelcontextprotocol/tasks, revisions of the authorization extensions keyed to 2026-07-28 |
| 25 September 2026 | SEP-3392 "Extension Lifecycle" is created: stages Experimental, Beta and Stable |
Extension identifiers
An extension is identified by a string of the form {vendor-prefix}/{extension-name}. The identifier follows the rules for _meta keys, except that the prefix is mandatory. The prefix is a series of labels separated by dots and should be a reversed domain name that the author owns: a company with example.com uses com.example/my-extension. Any prefix whose second label is modelcontextprotocol or mcp is reserved for MCP.
The table lists the identifiers of official extensions.
| Extension | Identifier | Repository |
|---|---|---|
| MCP Apps | io.modelcontextprotocol/ui |
ext-apps |
| Tasks | io.modelcontextprotocol/tasks |
ext-tasks |
| OAuth Client Credentials | io.modelcontextprotocol/oauth-client-credentials |
ext-auth |
| Enterprise-Managed Authorization | io.modelcontextprotocol/enterprise-managed-authorization |
ext-auth |
| Skills over MCP | io.modelcontextprotocol/skills |
ext-skills |
The identifier does not change when the extension moves from one stage to another. A breaking change also keeps the identifier. A new identifier, such as io.modelcontextprotocol/my-extension-v2, is used only when the old and new behavior must coexist under one protocol revision. SEP-2133 originally required a new identifier for every breaking change; SEP-3392 replaced that rule.
Negotiation
Declaring support
Support is declared in the extensions field of capabilities: a map from identifiers to settings objects. An empty object means support without settings. Each extension defines the schema of its own settings. In a stateless protocol the client declares extensions in every request, inside _meta["io.modelcontextprotocol/clientCapabilities"]:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "location": "New York" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/ui": { "mimeTypes": ["text/html;profile=mcp-app"] },
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}The server declares its extensions in the capabilities returned by server/discover:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": {
"tools": {},
"resources": {},
"extensions": {
"io.modelcontextprotocol/ui": {},
"io.modelcontextprotocol/skills": { "directoryRead": true }
}
},
"ttlMs": 3600000,
"cacheScope": "public"
}
}A server must not assume support declared in an earlier request.
Versions with the handshake
Before 2026-07-28 many SDKs already exchanged extensions in capabilities.extensions of the initialize request and response. The field is not part of the published schemas of those versions; if the other side omits it, the extension is treated as unsupported.
What extensions may add
An extension may add methods, notifications, _meta keys, settings and values of resultType. The set of resultType values a client accepts consists of the core values plus those of extensions advertised in capabilities. Tasks, for example, reserve the method prefix tasks/, the notification prefix notifications/tasks/ and the resultType value "task".
Official, experimental and third-party extensions
Official extensions live in repositories of the modelcontextprotocol GitHub organization with the ext- prefix and use the io.modelcontextprotocol prefix. Each repository lists its Extension Maintainers in MAINTAINERS.md, and each extension is associated with a Working Group or an Interest Group. Core Maintainers keep the authority to change, deprecate or remove any official extension.
Experimental extensions are an incubation path for Working Groups and Interest Groups. They live under specification/experimental/ of an ext- repository or in a repository with the experimental-ext- prefix. Published packages must show their experimental status, and implementers should pin a tag or commit, because two revisions need not be compatible.
Extensions developed outside the MCP organization are not recognized by MCP governance; SEP-2133 calls them unofficial. They should use a reversed domain of their author as the prefix and follow the same negotiation rules.
Lifecycle and governance
Stages
SEP-3392 defines the stages of an extension. The directory that holds its specification shows the stage: specification/experimental/, specification/beta/ or specification/stable/.
| Stage | Allowed changes | Entry | What implementers can expect |
|---|---|---|---|
| Experimental | Any change at any time | Added by an Extension Maintainer | Nothing; the design may disappear |
| Beta | Any change, breaking changes rare and documented | Extensions Track SEP | Real use; documented breaking changes |
| Stable | Only with a new core protocol revision | Second Extensions Track SEP | Fixed behavior within a protocol revision |
| Core | As for the core specification | Standards Track SEP | As for the core specification |
Changes are approved by Extension Maintainers without Core Maintainer review. A Beta extension that makes a breaking change should add or increment a revision string in its settings object, so that peers can detect a mismatch. A Stable extension is published as immutable revisions keyed to core protocol revisions, for example specification/stable/<name>/2026-07-28/.
Creation and promotion
An extension starts as Experimental. To enter Beta, its authors submit an Extensions Track SEP and state whether the extension is meant to become Stable or to move into the core. At least one reference implementation in an official SDK is required before review, and Core Maintainers decide on acceptance. To become Stable, a second SEP needs two independent, interoperating implementations, one on the client side and one on the server side, and a conformance scenario.
A Standards Track SEP can promote an extension into the core in protocol revision R. For requests that declare R or later the feature is core behavior; for earlier revisions the extension still applies.
SDK support
Supporting extensions is not required for protocol conformance, and SDK maintainers decide which ones to implement. Extensions are disabled by default and require explicit opt-in by the developer.
Current stages
The table lists the stages of official extensions on the extensions overview page.
| Extension | Stage |
|---|---|
| OAuth Client Credentials | Stable |
| Enterprise-Managed Authorization | Stable |
| MCP Apps | Stable |
| Tasks | Beta, intended for the core |
| Skills over MCP | Beta, destination not stated |
| Server Cards | Beta, destination not stated |
Tasks
Purpose
The Tasks extension lets a server answer tools/call with a task handle instead of the final result. A task is a durable state machine that carries the execution state of a request and is identified by a taskId generated by the server. The client polls the task and retrieves the result later, also after reconnecting. Only tools/call can create tasks in the current revision.
Creation is decided by the server. The client declares the extension in the capabilities of its requests and must handle both result shapes; there is no separate flag for task creation. A server must not return a task to a client that did not declare the extension.
Creating and polling a task
The examples omit _meta for brevity. A long-running call returns a result with resultType: "task":
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "task",
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
"status": "working",
"statusMessage": "Build started.",
"createdAt": "2026-08-10T10:30:00Z",
"lastUpdatedAt": "2026-08-10T10:30:00Z",
"ttlMs": 3600000,
"pollIntervalMs": 5000
}
}The server must not send this result until the task is durably created, so that a tasks/get for the returned ID succeeds. ttlMs is the lifetime from creation, or null for unlimited. pollIntervalMs is the suggested polling interval, and servers may rate-limit clients that poll more often. The client polls with tasks/get:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tasks/get",
"params": { "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" }
}On Streamable HTTP the client sets the Mcp-Name header of tasks/get, tasks/update and tasks/cancel to the task ID, so that intermediaries can route the request to the instance that holds the task.
Statuses
The table lists the statuses of a task.
| Status | Meaning | Additional field |
|---|---|---|
working |
The request is being processed | None |
input_required |
The server needs input from the client | inputRequests |
completed |
The request finished; terminal | result |
failed |
A JSON-RPC error occurred during execution; terminal | error |
cancelled |
The task was cancelled; terminal | None |
A tool result with isError: true is a completed task with the error inside result. The failed status is reserved for protocol errors.
Input during execution
When the task needs input, tasks/get returns status: "input_required" and an inputRequests map in the format of Multi Round-Trip Requests. The client answers with tasks/update:
{
"jsonrpc": "2.0",
"id": 6,
"method": "tasks/update",
"params": {
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
"inputResponses": {
"approve_deploy": { "action": "accept", "content": { "approved": true } }
}
}
}The server acknowledges with an empty result that has resultType: "complete". The acknowledgement is eventually consistent: the status seen by tasks/get may change later. Each key of inputRequests is unique for the lifetime of the task, and the server ignores answers to unknown or already answered keys. A completed task returns the result the original request would have returned:
{
"jsonrpc": "2.0",
"id": 9,
"result": {
"resultType": "complete",
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
"status": "completed",
"createdAt": "2026-08-10T10:30:00Z",
"lastUpdatedAt": "2026-08-10T10:52:00Z",
"ttlMs": 3600000,
"pollIntervalMs": 5000,
"result": {
"content": [{ "type": "text", "text": "Deployed build 1842 to staging." }],
"isError": false
}
}
}Cancellation and notifications
tasks/cancel signals the intent to cancel. The server must acknowledge it but is not obliged to stop the work, and the task may still reach completed. Instead of polling, a client can open subscriptions/listen with a taskIds filter and receive notifications/tasks with the full task state.
Differences from 2025-11-25
The experimental tasks of 2025-11-25 are not wire-compatible with the extension. The blocking tasks/result method and tasks/list are removed, the task parameter of tools/call is ignored, and the capability declarations tasks.requests.*, tasks.cancel and tasks.list are replaced by the extension identifier. Task IDs must be hard to guess, and every task request is checked for authorization.
MCP Apps
Components
MCP Apps (io.modelcontextprotocol/ui) let a server deliver an interactive HTML interface that the host renders inside the conversation. The extension combines two primitives: a UI resource with the ui:// URI scheme and the MIME type text/html;profile=mcp-app, and a tool that refers to that resource in _meta.ui.resourceUri:
{
"name": "get_weather",
"description": "Get current weather for a location",
"inputSchema": {
"type": "object",
"properties": { "location": { "type": "string" } }
},
"_meta": {
"ui": {
"resourceUri": "ui://weather-server/dashboard",
"visibility": ["model", "app"]
}
}
}The host fetches the interface with resources/read, possibly before the tool is called. The resource content may carry _meta.ui.csp with the external origins the interface may contact:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"resultType": "complete",
"contents": [
{
"uri": "ui://weather-server/dashboard",
"mimeType": "text/html;profile=mcp-app",
"text": "<!DOCTYPE html><html><body>...</body></html>",
"_meta": { "ui": { "csp": { "connectDomains": ["https://api.example.com"] } } }
}
],
"ttlMs": 3600000,
"cacheScope": "public"
}
}visibility controls who can call the tool. The value "model" makes it available to the model, "app" to the interface of the same server. A tool with ["app"] only is excluded from the model's tool list. Resources and tools are covered in MCP resources and MCP tools.
Sandbox
The interface runs in a sandboxed iframe without access to the DOM, cookies or storage of the host. A web host places it behind a sandbox proxy on a different origin. If the resource declares no CSP, the host applies a restrictive default that blocks external connections, and it must not allow domains that were not declared.
Communication between the interface and the host
The interface and the host exchange JSON-RPC 2.0 messages over postMessage. The specification describes this as a dialect of MCP in which the interface acts as a client of the host.
Host (AI application)
|
+-- MCP client ------ tools/call, resources/read ------ MCP server
|
+-- sandbox proxy (separate origin)
|
+-- iframe with the interface --- postMessage, JSON-RPCThe exchange starts with ui/initialize and ui/notifications/initialized. The host sends the tool arguments in ui/notifications/tool-input and the result in ui/notifications/tool-result. The interface can call tools of its server with tools/call, which the host forwards and may subject to user approval. It can also send a message to the chat with ui/message, add data to the model's context with ui/update-model-context and ask the host to open a link with ui/open-link. Before removing the interface the host sends ui/resource-teardown.
Fallback
A host without MCP Apps support treats the tool as an ordinary tool and shows its text result. For this reason the tool must return meaningful content even when an interface is available, and servers should provide a text-only behavior for every tool with an interface.
Enterprise-Managed Authorization
The Enterprise-Managed Authorization extension (io.modelcontextprotocol/enterprise-managed-authorization) makes the organization's identity provider (IdP) the point where access to MCP servers is decided. Without it, every user authorizes every client on every server separately. With it, administrators define in the IdP which users may use which clients with which servers and scopes. The basics of MCP authorization with OAuth are described in MCP authorization.
The extension is a profile of the IETF draft Identity Assertion JWT Authorization Grant. The flow has three steps:
- The user signs in to the MCP client through the enterprise IdP with OpenID Connect or SAML, and the client keeps the identity assertion.
- The client sends a token exchange request (RFC 8693) to the IdP with
requested_token_typeequal tourn:ietf:params:oauth:token-type:id-jag. The IdP evaluates its policies and returns an Identity Assertion JWT Authorization Grant (ID-JAG). - The client presents the ID-JAG to the authorization server of the MCP server as a JWT bearer grant (RFC 7523) and receives an access token.
The client does not redirect the user to the authorization server of the MCP server. It learns that the server supports the profile from the value urn:ietf:params:oauth:grant-profile:id-jag in authorization_grant_profiles_supported of the authorization server metadata. The IdP sees only the issuance of the token, not the MCP traffic.
The same repository contains OAuth Client Credentials (io.modelcontextprotocol/oauth-client-credentials), an extension for machine-to-machine access without user interaction, such as CI pipelines and background services.
Skills over MCP
Skills over MCP (io.modelcontextprotocol/skills) let a server publish Agent Skills: directories with a SKILL.md file and supporting files. SEP-2640 has the status Final, and the extensions overview lists the extension as Beta. A server that declares it must also declare resources and implement skills/list and skills/get; the files are read with resources/read under skill:// URIs.
Reading SKILL.md does not activate a skill. The host loads it through its own skill path, which verifies the content and asks for user approval where required. The extension requires hosts to treat skill content as untrusted input; risks of instructions in such content are covered in prompt injection.
Behavior without extension support
If one side supports an extension and the other does not, the supporting side must either fall back to core behavior or reject the request with an error. Extensions should document their fallback. The table lists the behavior defined by each official extension.
| Extension | Peer without support | Defined behavior |
|---|---|---|
| Tasks | Client | Server returns the ordinary result; task methods return -32021 |
| MCP Apps | Host | Tool works as a text-only tool |
| Enterprise-Managed Authorization | Client | Server falls back to core authorization or rejects the client |
| Skills over MCP | Server | Client does not send skills/list or skills/get |
| Any | Client sees an unknown resultType |
The result is invalid for the client |
The specification does not define one error code for all extensions.
Extension, core feature and experimental capability
Three mechanisms add optional behavior to MCP. The table compares them.
| Core feature | Extension | Experimental capability | |
|---|---|---|---|
| Where it is specified | Core specification | Extension document in an ext- repository |
Not specified |
| Declared in | Named capability, such as tools |
capabilities.extensions |
capabilities.experimental |
| Identifier | Field name in the schema | vendor-prefix/name |
Arbitrary key |
| Who approves changes | Core Maintainers | Extension Maintainers | Not defined |
| Change points | Protocol revisions | Depends on the stage | Any time |
| Required for conformance | Per the specification | No | Not defined |
Changes to a core feature go through SEPs approved by Core Maintainers, and its deprecation follows the feature lifecycle policy described in Stateless MCP.
The experimental field of ClientCapabilities and ServerCapabilities holds non-standard capabilities as a map of names to objects. The schema defines no process, prefix or document for them, while an experimental extension has a Working Group, a repository and a path to Beta.
Terms
| Term | Meaning |
|---|---|
| MCP extension | Optional addition to the specification outside the core protocol, declared in capabilities |
| Extension identifier | String of the form vendor-prefix/name that identifies an extension in capabilities |
| Official extension | Extension in an ext- repository of the MCP organization with the io.modelcontextprotocol prefix |
| Experimental extension | Extension in incubation with no stability promise, linked to a Working or Interest Group |
| Extension stage | Maturity level of an extension: Experimental, Beta or Stable |
| Task | Durable state of a long-running request that the client polls by taskId |
| UI resource | Resource with a ui:// URI that contains an HTML interface for MCP Apps |
| ID-JAG | Identity Assertion JWT Authorization Grant: token from the enterprise IdP exchanged for an access token |
| Skill | Directory with SKILL.md and supporting files served by the Skills extension |
Sources
- Extensions Overview(opens in a new tab)
- Versioning and Compatibility: extension negotiation, specification 2026-07-28(opens in a new tab)
- Key Changes, specification 2026-07-28(opens in a new tab)
- TypeScript schema, version 2026-07-28(opens in a new tab)
- SEP-2133: Extensions(opens in a new tab)
- SEP-3392: Extension Lifecycle(opens in a new tab)
- Tasks(opens in a new tab)
- SEP-2663: Tasks Extension(opens in a new tab)
- MCP Tasks Extension schema, version 2026-07-28, GitHub(opens in a new tab)
- MCP Apps(opens in a new tab)
- MCP Apps specification, revision 2026-01-26, GitHub(opens in a new tab)
- MCP Apps - Bringing UI Capabilities To MCP Clients, MCP blog, 26 January 2026(opens in a new tab)
- Enterprise-Managed Authorization(opens in a new tab)
- Enterprise-Managed Authorization specification, revision 2026-07-28, GitHub(opens in a new tab)
- Skills(opens in a new tab)
- The 2026-07-28 Specification, MCP blog, 28 July 2026(opens in a new tab)