Skip to main content

MCP Extensions - Tasks, MCP Apps, and Skills

This guide explains how opt-in Extensions, which live outside the core protocol, work, and the main extensions. Extensions are versioned separately from the core, and they do not work if the client does not support them. Before adopting one, you need to check support and design a fallback. For the big picture of the core protocol, see Introduction to MCP - What Is the Model Context Protocol; for the changes in the 2026-07-28 version, see MCP 2026-07-28 Spec Changes and Migration.

1. What This Document Covers​

  • The extension mechanism
  • The Tasks extension
  • MCP Apps
  • Skills over MCP
  • Authorization extensions
  • Server Cards
  • Checking support
  • Deciding whether to adopt

The extension mechanism​

An extension is an opt-in additional feature defined outside the core specification. There are modular ones (such as authentication), specialized ones (industry-specific logic), and experimental ones (features being incubated for possible inclusion in the core).

An extension is identified by an extension identifier of the form {vendor-prefix}/{extension-name}. Official extensions use io.modelcontextprotocol as the prefix. A third-party extension uses a reversed domain name that you own as the prefix (for example, com.example/my-extension) to avoid collisions.

Both sides opt in explicitly by declaring support in capabilities.extensions (SEP-2133). Because the 2026-07-28 version is stateless, the place of the declaration is fixed for each direction.

  • Client: put extensions in _meta["io.modelcontextprotocol/clientCapabilities"] of each request
  • Server: put it in capabilities.extensions of the server/discover response
{
"_meta": {
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
}

Each extension defines the schema of its own settings object. An empty object means there are no settings. Extensions are always disabled by default and require the developer's explicit opt-in. An SDK can conform to the protocol without supporting extensions, and it is recommended that the SDK documentation list the extensions it supports.

Extensions evolve independently of the core. Updates are managed by the maintainers of the extension repository and do not need review by the core maintainers. Breaking changes (removing or renaming fields, changing types, adding required fields) are absorbed with flags or versions inside the settings, and a new identifier (for example, io.modelcontextprotocol/my-extension-v2) is used only when unavoidable.

When one side does not support an extension, the supporting side either falls back to core behavior or, if the extension is mandatory, rejects with an error. For example, a server that offers tools with UI also returns a meaningful text result to clients that do not support the UI extension.

Official extensions live in repositories whose names start with ext- in the MCP GitHub organization. Experimental extensions live in repositories that start with experimental-ext-, are incubated under working groups and the like, and become official through a SEP (Extensions Track).

The Tasks extension​

Tasks is an extension for handling long-running work with a durable handle. Its identifier is io.modelcontextprotocol/tasks. In the 2025-11-25 version it was an experimental feature of the core, and the 2026-07-28 version moved it into an extension (SEP-2663; the specification is at ext-tasks).

Tasks addresses problems that holding a connection open cannot solve.

  • No long-lived connection is needed. Many clients and intermediaries set timeouts on connections beyond a few seconds
  • Resilience to crashes. The task ID is a durable handle, and even if the client disconnects or restarts, it can resume with the same ID
  • Visible progress. A task has a status and an optional status message
  • Input can be requested mid-flight. The task becomes input_required, and the client responds with tasks/update
  • Server-directed. The server decides per request whether to create a task. The client opts in to the extension once and is ready to handle either shape of result

The flow is as follows.

  1. The client puts io.modelcontextprotocol/tasks in its per-request capabilities. The server also declares the same extension in server/discover
  2. For a request it judges to be long-running, the server returns a CreateTaskResult (resultType: "task") instead of the final result. It includes taskId, the initial status, ttlMs, and the suggested polling interval (pollIntervalMs). The task is created durably before the response is returned
  3. The client gets the status with tasks/get. In a terminal status, result or error is included
  4. If input is needed midway, the status becomes input_required, and the tasks/get response contains inputRequests. The client returns inputResponses with tasks/update
  5. For completed, result contains what would have been returned synchronously. For failed, error contains the JSON-RPC error
  6. The client can request cancellation at any time with tasks/cancel. Cancellation is cooperative, and the server does not necessarily stop
StatusMeaning
workingIn progress
input_requiredThe server is waiting for client input. See inputRequests
completedDone. result holds the final output
failedA JSON-RPC error occurred during execution. error has details
cancelledCancelled (not always honored)

completed, failed, and cancelled are terminal statuses and do not change afterward. tasks/result and tasks/list were removed; you get the result with tasks/get. Polling is the basic approach, but if the server supports it, the client can subscribe to notifications/tasks through subscriptions/listen and receive status notifications. A notification contains the complete task state, so no extra tasks/get is needed.

A server must not return a CreateTaskResult when the client has not declared the extension. The client persists task IDs (to resume after a restart) and polls while respecting pollIntervalMs.

Tasks suit scenarios such as CI pipelines, batch processing, human approval, wrapping external systems that have job IDs, and unreliable connections. You do not need them for work that finishes in a few seconds.

MCP Apps​

MCP Apps is an extension that renders an interactive UI inside the conversation as the result of a tool, such as a chart, form, dashboard, or video player (identifier io.modelcontextprotocol/ui). Instead of returning a link to a separate web app, it lets users operate it within the flow of the conversation, the app can call the server's tools, and the host can push fresh results to the app.

It works by combining two MCP primitives.

  1. The tool definition's _meta.ui.resourceUri points to a ui:// resource. The host can preload this resource before calling the tool
  2. The host fetches the UI resource (normally HTML bundled with JavaScript and CSS) from the server. The origins from which external scripts and the like may be loaded are specified in _meta.ui.csp
  3. A web host renders the HTML inside a sandboxed iframe. permissions in _meta.ui can request additional permissions such as the microphone or camera
  4. The app and host communicate with JSON-RPC over postMessage (a dialect of MCP that includes its own ui/-prefixed methods such as ui/initialize). The app can request tool calls, send messages, and update the model's context

Security rests on the sandboxed iframe. The app cannot touch the parent page's DOM, cookies, or local storage, and cannot navigate the parent or run scripts in it. The host can restrict the tools the app can call and the ability to open links. The design aim is that third-party apps can be rendered even without fully trusting the server's author.

It fits exploring data (drill-down in maps and charts), forms with many options, displaying rich media (PDF, 3D), real-time monitoring, and multi-step workflows. If you do not need the benefits UI brings, a regular web app is simpler.

For clients that do not support MCP Apps, design tools with UI to return a meaningful text result as well. For the detailed API, see the official MCP Apps documentation.

Skills over MCP​

Skills over MCP (identifier io.modelcontextprotocol/skills) is an extension for discovering and retrieving, from an MCP server, skills that follow the Agent Skills specification (a directory containing SKILL.md and optional supporting files). Reusable procedures that combine several tools, such as code review or document processing, can live in the same place as the service they describe. A client discovers usable skills from their metadata and reads the instructions and supporting files only when needed. SEP-2640 is Final, and the Skills over MCP Working Group maintains it. SDK and host support, on the other hand, is still being implemented.

A server has the resources capability and the declaration of this extension (including the optional directoryRead setting), and implements the following methods.

MethodPurpose
skills/listList of skills. Each entry has uri, frontmatter, and a file manifest (resources)
skills/getFetch one skill entry by specifying a URI (also used to refresh)
resources/readFetch the contents of SKILL.md and supporting files (the existing Resources feature)
resources/directory/readOptional. List the direct children of a directory (when directoryRead: true)

The skill URI recommends skill://, as in skill://code-review/SKILL.md, but other schemes can be used. The manifest has the URI, SHA-256 digest, and byte size for SKILL.md and every supporting file. Before using a file, the host must verify its byte size and digest, and must compare the frontmatter of SKILL.md with the entry's frontmatter. It does not use content that fails verification and refreshes the entry. A digest only shows consistency with the server's manifest and does not mean the content is trustworthy. Treat skill content as untrusted input. Host-side code execution and granting permissions such as allowed-tools require the user's explicit approval per skill. Approval is bound to the set of file URIs and digests, and is revoked if a file changes.

A skill's identity is the combination of the server's identity and the URI. A name is merely a label that is not necessarily unique, so the host prevents skills with the same name from silently replacing one another.

Authorization extensions​

The authorization extensions are mechanisms managed in the ext-auth repository that supplement the core authorization.

ExtensionIdentifierUse
OAuth Client Credentialsio.modelcontextprotocol/oauth-client-credentialsMachine-to-machine (M2M) authentication without interactive login
Enterprise-Managed Authorizationio.modelcontextprotocol/enterprise-managed-authorizationCentralized access control by an enterprise IdP

The mechanisms and implementations are covered in detail in MCP Authorization - OAuth 2.1 and Metadata Discovery and MCP Authorization Extensions and Enterprise Adoption.

Server Cards​

A Server Card is a static metadata document for discovering an MCP server before connecting (SEP-2127). server/discover is live discovery after you know where to connect and have connected. A Server Card fills the stage before that, letting clients and registries learn the server's location, transports, and supported protocol versions without connecting. The main uses are autoconfiguration in IDEs and indexing by crawling domains.

  • The document carries the identity of a remote MCP server (name, version, description, and so on), its remote endpoints (URLs, headers, variable templates, supported protocol versions), and namespaced _meta
  • For domain-level discovery, an AI Catalog (/.well-known/ai-catalog.json) links to or embeds Server Cards. The card itself can be placed at any URI, and <streamable-http-url>/server-card is the recommended location
  • It deliberately does not include definitions of tools, resources, and prompts, nor capabilities and extension support. These vary by user and session and cannot be represented in a static document, so you confirm them at runtime with */list
  • It is a public, read-only document. It must not contain credentials, internal network topology, or user-specific data. Serve it over HTTPS, and if a card disagrees with server/discover, prefer the live server/discover values

The details of the specification are kept in the experimental extension's repository (experimental-ext-server-card) and may change. It is separate from the Registry's server.json, which also covers locally installed packages (see MCP Registry and Distributing Servers).

Checking support​

An extension works only when both the client and the server support it. Check each client's support in the official Extension Support Matrix. It is a community-maintained list, and at the time of writing the situation is roughly as follows.

ExtensionOverview of support
MCP AppsSupported by Claude (web), Claude Desktop, GitHub Copilot in VS Code, Microsoft 365 Copilot, Goose, Postman, ChatGPT, Cursor, and others
SkillsPartial (ChatGPT, fast-agent, and MCP Inspector have partial support). mcpc supports it
OAuth Client CredentialsIn the list, mcpc supports it
Enterprise-Managed AuthorizationIn the list, Archestra.AI and mcpc support it

Tasks is not in this list. Because the table changes, always check the latest list right before deciding to adopt. Support for the authorization extensions is tracked separately from the core authorization features (DCR, CIMD).

Deciding whether to adopt​

An extension does not work if the client does not support it. When deciding to adopt one, check the following.

  1. Whether the clients you use support the extension (the support list, plus confirmation on real clients)
  2. Whether there is a fallback when the feature is lost in unsupported clients
  3. The maturity of the extension's specification: official or experimental. Experimental ones may change

2. Main Extensions​

ExtensionUseNotes
TasksAsynchronous long-running work, mid-flight input, durable handlesExperimental in 2025-11-25. Moved to an extension in 2026-07-28
MCP AppsEmbedding interactive UIRendered in hosts such as Claude Desktop
SkillsDistributing agent workflow proceduresSEP-2640 is Final. Maintained by the Skills over MCP Working Group. SDK and host support is in progress
OAuth Client CredentialsMachine-to-machine (M2M) authenticationAuthorization extension
Enterprise-Managed AuthorizationCentralized access control by an IdPAuthorization extension
Server CardsDiscovering a server before connectingDefined in an experimental extension repository (SEP-2127)

3. Common Pitfalls for Developers​

3.1 Assuming an extension without client support​

Extensions are disabled by default and need declarations from both sides. Check that the extension is in the client's _meta before using it. For an extension that changes the shape of the result, as Tasks does, you must not return it to a client that has not declared it.

3.2 Returning the result only as UI​

In clients that do not support MCP Apps, the UI is not shown. Have the tool return meaningful text or a structured result even without the UI.

3.3 Treating Tasks like a synchronous call​

A CreateTaskResult is not the final result. Check resultType and poll with tasks/get until a terminal status. Avoid high-frequency polling that ignores pollIntervalMs. Persist task IDs so you can resume after a restart. tasks/result and tasks/list have been removed.

3.4 Mixing up where to answer input_required​

In ordinary MRTR, you attach inputResponses to the retried tools/call. In Tasks, you return it with tasks/update. Decide which to use from the shape of the result you received.

3.5 Getting the extension identifier and settings wrong​

The identifier is the full string (such as io.modelcontextprotocol/tasks), and an empty object means no settings. A breaking change becomes a new identifier, so versioned identifiers can appear.

3.6 Trusting skill content​

A SKILL.md distributed through Skills is untrusted input. Like the description of a tool, it can be a route for prompt injection. Ask for approval for host-side code execution and permission grants, and detect content changes with digests. See AI Agent-Specific Threats - Prompt Injection and Tool Poisoning for details.

3.7 Putting tool lists or secrets in a Server Card​

A Server Card is a public document. Tools and capabilities are confirmed at runtime with */list and server/discover, and are not written in the card. Do not put credentials in it either.

3.8 Treating the support list as a fixed fact​

The list is community-maintained and changes often. Do not copy the table into documents or code; link to it and check at the time of adoption.

4. Code Examples​

A client request that declares an extension​

{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "run_pipeline",
"arguments": { "repo": "example/app" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
},
"io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" }
}
}
}

The server's declaration (server/discover)​

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": {
"tools": {},
"extensions": {
"io.modelcontextprotocol/tasks": {},
"io.modelcontextprotocol/ui": {}
}
},
"ttlMs": 3600000,
"cacheScope": "public"
}
}

A tool definition that references an MCP Apps UI​

The tool's _meta.ui.resourceUri points to the UI resource.

{
"name": "show_sales_dashboard",
"description": "Show sales by region. Returns a text summary as well as an interactive dashboard.",
"inputSchema": {
"type": "object",
"properties": { "year": { "type": "integer" } },
"required": ["year"]
},
"_meta": {
"ui": { "resourceUri": "ui://sales/dashboard" }
}
}

Polling by a Tasks client​

Branch on the shape of the result (resultType). This is TypeScript pseudocode. For the exact shape of the task entries, check the Tasks extension specification (ext-tasks).

async function callMaybeTask(client: McpClient, params: CallToolParams) {
const first = await client.request("tools/call", params);
if (first.resultType !== "task") return first; // An ordinary result

const taskId = first.task.taskId;
await persistTaskId(taskId); // So that you can resume after a restart
let delay = first.task.pollIntervalMs ?? 2000;

for (;;) {
await sleep(delay);
const t = await client.request("tasks/get", { taskId });
switch (t.status) {
case "completed":
return t.result;
case "failed":
throw new Error(t.error.message);
case "cancelled":
throw new Error("task cancelled");
case "input_required": {
const inputResponses = await collectInputs(t.inputRequests);
await client.request("tasks/update", { taskId, inputResponses });
break;
}
default:
delay = t.pollIntervalMs ?? delay; // working
}
}
}

5. Summary​

  • Extensions are opt-in features outside the core. Both sides declare them in capabilities.extensions, and the identifier is {vendor-prefix}/{extension-name}
  • Tasks handles long-running work with a taskId handle, advanced by polling tasks/get and by tasks/update. tasks/result and tasks/list were removed
  • MCP Apps renders interactive UI in a sandboxed iframe. Also return a text result for unsupported clients
  • Skills over MCP discovers and retrieves skills with skills/list, skills/get, and resources/read. The content is untrusted input and is verified with digests
  • The authorization extensions are OAuth Client Credentials and Enterprise-Managed Authorization
  • A Server Card is public metadata for discovery before connecting. It does not include tools or capabilities
  • Before adopting, check support in the Extension Support Matrix and design a fallback

References​