MCP Resources, Prompts, Sampling and Elicitation Explained (2026)

Nov 10, 2026 · 11 min · Ajay Kumar

Most MCP servers I review expose tools and nothing else, which is a bit like shipping a REST API with only POST. The Model Context Protocol has always had more in it: resources, prompts, elicitation, sampling and roots, and since this year two official extensions, Tasks and MCP Apps. The current revision, 2026-07-28, also deprecated two of them (Sampling and Roots, plus Logging) and changed how the rest of them travel over the wire. This post explains what each MCP primitive beyond tools is for, what the 2026-07-28 spec changed, which clients actually support what, and when I would use each one. If you want the hands-on companion about running servers in containers, that is in How to Run MCP Servers in Docker Securely.

What are MCP resources, prompts and tools, and who controls each?

The spec answers this with a control hierarchy, and it is the single most useful idea for designing a server. The server features overview puts it in three rows: prompts are user-controlled, resources are application-controlled, tools are model-controlled.

That sounds academic until you design a server. A tool is something the model decides to call. A resource is data the client application decides to attach, usually because a person picked it from a menu or typed an @ mention. A prompt is a template the person invokes on purpose, typically as a slash command. Each primitive puts a different party in charge of when it fires, and that is the design decision, not the payload format.

The practical consequence: if your "tool" is really get_schema_for_table and nobody needs the model to decide when to call it, it probably wants to be a resource. Making everything a tool means every capability competes for the model's attention in the tool list, burns context on schemas, and is triggered by the least predictable party in the system. I covered the cost side of that in MCP tool design best practices.

Feature Who triggers it Direction Status in 2026-07-28 Source
Tools Model Client calls server Active, core Server overview
Resources Application Client reads from server Active, core Server overview
Prompts User Client fetches template Active, core Server overview
Elicitation Server asks the user Via MRTR Active, core Elicitation
Sampling Server asks the client's model Via MRTR Deprecated Deprecated registry
Roots Server asks for directories Via MRTR Deprecated Deprecated registry
Tasks Server returns a handle Extension io.modelcontextprotocol/tasks Beta, intended for core Extensions overview
MCP Apps Tool references a UI Extension io.modelcontextprotocol/ui Stable Extensions overview

What changed in MCP spec 2026-07-28?

The headline of the July release was a stateless core. The changelog removes the initialize handshake and the Mcp-Session-Id header; every request now carries its protocol version and client capabilities in _meta, and a new server/discover call lets a client ask for capabilities up front. I wrote about the transport side in remote vs local MCP servers. For this post, three other changes matter.

First, server-initiated requests are gone as a mechanism. Under the old model a server mid-tool-call could send elicitation/create, sampling/createMessage or roots/list back to the client, which needed a held-open bidirectional stream. The 2026-07-28 revision replaces that with Multi Round-Trip Requests (MRTR, SEP-2322): the server returns a result with resultType: "input_required" and a map of inputRequests, and the client retries the original call with inputResponses attached.

Second, Roots, Sampling and Logging are deprecated by SEP-2577. The deprecated features registry says they stay fully functional, new implementations should not adopt them, and the earliest removal is the first revision released on or after 2027-07-28. The suggested migrations are blunt: pass directories via tool parameters or config instead of Roots, call the LLM provider API directly instead of Sampling, and log to stderr or OpenTelemetry instead of Logging.

Third, extensions became a formal mechanism. Tasks moved out of the core protocol into an extension, and MCP Apps, which launched on 26 January 2026 as the first official extension, sits alongside it. Extensions are negotiated through an extensions field in capabilities and are always off by default.

Here is what an MRTR exchange looks like; the first half is trimmed from the spec's own example and the retry is my illustration of the schema's inputResponses and requestState params. It is the shape you need to recognise when you debug elicitation on a 2026-07-28 connection:

// 1. Server answers a tools/call with an interim result instead of a final one
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "input_required",          // required field on every result in 2026-07-28
    "inputRequests": {
      "github_login": {                        // key chosen by the server
        "method": "elicitation/create",
        "params": {
          "mode": "form",                      // "form" or "url"
          "message": "Please provide your GitHub username",
          "requestedSchema": {
            "type": "object",
            "properties": { "name": { "type": "string" } },
            "required": ["name"]
          }
        }
      }
    },
    "requestState": "opaque-server-string"   // clients MUST NOT parse this
  }
}

// 2. Client retries the ORIGINAL tools/call (new request id), with answers keyed the same way
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "open_pr",
    "arguments": { "repo": "acme/api" },
    "inputResponses": {
      "github_login": { "action": "accept", "content": { "name": "octocat" } }
    },
    "requestState": "opaque-server-string",
    "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28" }  // clientCapabilities etc. omitted
  }
}

The design is simpler to scale, because any server instance behind a round-robin load balancer can handle the retry. It also means the server has to be able to resume from requestState, which is where your own correlation ID lives now that the spec removed elicitationId for URL mode. The spec also says servers MUST treat a returned requestState as attacker-controlled input and protect its integrity (HMAC or AEAD) if it influences authorization or business logic. Put differently, requestState is a cookie, and every lesson about unsigned cookies applies.

MCP features and their status in spec revision 2026-07-28 Offered by the server Tools Model-controlled Active, core tools/call, actions Resources Application-controlled Active, core resources/read, @ mentions Prompts User-controlled Active, core Slash commands MCP Apps io.modelcontextprotocol/ui Extension, Stable ui:// HTML, sandboxed iframe Requested from the client, or added by extension Elicitation Server asks the user Active, via MRTR form or url mode Sampling Server asks client's LLM Deprecated 2026-07-28 Removal from 2027-07-28 Roots Client lists directories Deprecated 2026-07-28 Use tool args or config Tasks io.modelcontextprotocol/tasks Extension, Beta to Core tasks/get polling active core feature deprecated official extension, opt-in
Status of each MCP feature in the 2026-07-28 revision, from the spec changelog, the deprecated features registry and the extensions overview at modelcontextprotocol.io.

When should you use MCP resources instead of tools?

Use a resource when the content has a stable address and a person, or the host application, should decide when it enters the context. Database schemas, runbooks, a ticket by ID, the README of a service: these are things you want to reference, not actions you want the model to take. In Claude Code resources show up in the @ autocomplete next to files and are referenced as @server:protocol://resource/path, for example @github:issue://123; Claude Code also gives the model tools to list and read resources when a server offers them. In VS Code you attach them from Add Context, MCP Resources.

Two details from 2026-07-28 matter to server authors. resources/subscribe is gone, replaced by a single subscriptions/listen stream that clients opt into for change notifications. And list and read results now carry ttlMs and cacheScope hints through a new CacheableResult interface, so a client can cache your resource list instead of polling it. If you were relying on per-connection resource lists, that is also gone: list endpoints no longer vary per connection.

What are MCP prompts, and how do they differ from tools and skills?

A prompt is a named, parameterised message template the server publishes and the user invokes. Claude Code lists them as /servername:promptname (MCP) and also accepts /mcp__servername__promptname with space-separated arguments; VS Code uses /<server>.<prompt>. The result is injected into the conversation as if the user had typed it.

Prompts are underrated for operations work. A /mcp__pagerduty__incident_brief INC-123 prompt that pulls the incident, the last deploys and the relevant dashboard links into one message is more predictable than hoping the model chains four tools in the right order. The newer Skills over MCP extension (Beta) goes further, letting servers publish workflow instructions plus supporting files as resources. My rule of thumb: a prompt is a single message a human starts; a skill is a reusable procedure the agent can load; a tool is an action.

What is MCP elicitation, and is form mode safe for secrets?

Elicitation lets a server ask the user a question in the middle of a tool call. There are two modes. Form mode sends a JSON schema and the client renders a form. URL mode, added in 2025-11-25, sends a link for an out-of-band flow such as an OAuth sign-in or a payment page. The elicitation spec is explicit that servers MUST NOT use form mode for passwords, API keys, access tokens or payment credentials, and MUST use URL mode for those, so the secret never passes through the MCP client or the model's context.

Claude Code declares both modes, elicitation: {form: {}, url: {}}, on 2026-07-28 connections and on user-configured servers on older revisions from v2.1.287; on some other connections it declares the bare elicitation: {}, which means form only. It shows a terminal dialog for form mode and asks before opening a browser for URL mode, and an Elicitation hook can answer requests automatically. That hook is the place to be careful: auto-answering elicitations is exactly how a "human in the loop" quietly stops containing a human.

Should you still use MCP sampling and roots in 2026?

Not in new code. Sampling let a server borrow the client's model ("summarise this for me using whatever LLM you have"), which is clever and rarely deployed well: it gives a server indirect access to the user's model budget, and client support was always patchy. VS Code is one client that does support it, with a chat.mcp.serverSampling setting that controls which models are exposed to servers, per its configuration reference. Under SEP-2577 the advice is to call the provider API from your server instead, which also makes the cost show up on your bill rather than your user's.

Roots told a server which directories the client considered in scope. Claude Code still answers roots/list with the launch directory plus any --add-dir directories. The reference filesystem server shows why Roots is being retired: its README notes that roots sent by a 2025-era client replace the command-line directories unless the server is started with --confine-roots, and that a 2026-07-28 client is never asked for roots at all. A directory list in server config is easier to audit than one negotiated at runtime, which is the whole argument in one sentence.

What are the MCP Tasks extension and MCP Apps?

Tasks handle long-running work. A server answers tools/call with a task handle, the client polls tasks/get, and tasks/update carries input back mid-flight. The redesign in 2026-07-28 replaced the blocking tasks/result call with polling and removed tasks/list. It is Beta with core as the stated destination. Claude Code runs MCP tasks as a public beta in the CLI from v2.1.304, which is the most concrete client implementation I have found.

MCP Apps lets a tool declare a ui:// resource in _meta.ui.resourceUri; the host fetches that HTML (media type text/html;profile=mcp-app) and renders it in a sandboxed iframe that can call back into the server's tools. The Apps overview is candid that if you do not need conversation context or host integration, a normal web app may be simpler. The SDK repo, ext-apps, was at v2.0.4 on 27 October 2026. From a security review point of view, an MCP App is third-party HTML inside your chat client, so the host's CSP and sandbox are the control, and _meta.ui.csp is where the server asks for external origins. Read it like you would read a browser extension's manifest.

Which MCP clients support resources, prompts, elicitation, tasks and Apps?

The official MCP site no longer carries the old core-feature client table; what it maintains now is an extension support matrix. The rows below combine that matrix with each vendor's own docs. Blank means I could not find it documented, not that it is absent.

Client Resources Prompts Elicitation Sampling Tasks ext MCP Apps Source
Claude Code yes, @server:uri yes, /mcp__server__prompt form and url not documented yes, beta, v2.1.304+ no Claude Code docs, matrix
Claude (web) yes yes matrix
Claude Desktop yes matrix
VS Code GitHub Copilot yes, Add Context yes, /<server>.<prompt> yes, chat.mcp.serverSampling experimental VS Code docs, reference
Cursor yes matrix
ChatGPT yes matrix
Goose yes matrix

The pattern is that MCP Apps spread fast across chat-style hosts, while Tasks and the client-side features are thinner. If your server depends on elicitation or tasks, test against the specific clients your users run, and design a degraded path: a tool that fails with a clear message beats one that hangs waiting for a form nobody can see.

FAQ

What is the current MCP specification version?

The current stable revision is 2026-07-28, released on 28 July 2026. The previous one was 2025-11-25. Version negotiation now happens per request through _meta and the server/discover call rather than an initialize handshake.

Is MCP sampling deprecated?

Yes. The 2026-07-28 revision deprecated Sampling, Roots and Logging under SEP-2577. They keep working during the deprecation window, and the earliest removal is the first revision released on or after 28 July 2027.

What is the difference between MCP resources and tools?

Tools are model-controlled actions the model decides to call. Resources are addressable data the client application attaches, usually because a person selected it. Use resources for things you reference and tools for things you do.

Do MCP Apps work in Claude Code?

Not according to the official extension matrix, which lists MCP Apps for Claude on the web, Claude Desktop, VS Code, Cursor, ChatGPT and others but not Claude Code. Claude Code hides ui:// resources from its @ suggestions, though reading one by URI still works.

This quarter I would do three things on any MCP server I own: move read-only lookups from tools to resources, delete any sampling or roots dependency before the 2027 removal window, and add a URL-mode elicitation path for anything that touches a credential. Then I would put the server in a container with no network it does not need, which is the subject of the Docker guide, and re-read the threat model in MCP security in 2026.

Related: What Is MCP (Model Context Protocol)? A Plain-English Guide for 2026, How to Build an MCP Server in Python and TypeScript (2026) and Remote vs Local MCP Servers in 2026: stdio vs Streamable HTTP, No Sessions.

Spot an error or something out of date? Email me and I will fix it and log it on the corrections page.

I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.

Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.


Related