TL;DR: MCP defines three separate primitives, not one. Tools are functions the model can call on its own. Resources are data the host application decides to attach. Prompts are templates a person picks deliberately, even though the server writes what's inside them. Same protocol, three different parties in control.
Most explanations of the Model Context Protocol reach for one word — usually "tools" — and use it for everything a server can offer a model. That flattens a distinction the spec goes out of its way to make explicit: MCP's own reference (revision 2026-07-28, at modelcontextprotocol.io) gives tools, resources and prompts each a dedicated page, and every one of those pages opens with a section called "User Interaction Model" that answers exactly one question — who decides when this runs?
That question is the entire post. Get it right and the rest of MCP — why a server ships three kinds of things instead of one, why some clients only expose two of them, why a slash command and a tool call can look identical from the outside but behave differently — falls into place.
What is MCP, in one paragraph?
The Model Context Protocol is a JSON-RPC-based standard for connecting an AI application to external systems: databases, file stores, internal APIs, saved prompt libraries. A host is the AI application a person is using. A client is the connector inside that host talking to one server. A server is the process exposing capability — tools to call, resources to read, prompts to select. One host can hold many clients, each wired to a different server. Everything below describes what a server can offer through that connection, and who on the other end gets to decide when it happens.
Why does MCP split into three primitives instead of one?
Because "the model can access this" and "a person explicitly asked for this" are different trust boundaries, and collapsing them either over-triggers automation a user never approved, or forces a person to manually approve things that were always meant to run unattended. The spec resolves this by defining three primitives, each with its own capability flag the server declares up front, so a client knows before it connects which kinds of interaction it needs to support.
What are MCP tools, and who decides when they run?
Tools are functions. The spec's own wording: tools "can be invoked by language models," and are "designed to be model-controlled, meaning that the language model can discover and invoke tools automatically based on its contextual understanding and the user's prompts." A tool has a name, a schema, and a job — query a database, call an API, run a computation. The model reads the available tool list, decides mid-conversation that calling one would help, and calls it without asking first.
That autonomy is exactly why the spec pairs the tools page with a warning most summaries drop: implementations "SHOULD always" keep "a human in the loop with the ability to deny tool invocations," with visible indicators when a tool fires and confirmation prompts before anything consequential runs. Model-controlled does not mean unsupervised — it means the decision to call belongs to the model; the permission to proceed is still supposed to belong to a person.
What are MCP resources, and who decides what gets included?
Resources are data, not functions. Each one is identified by a URI and exists to "share data that provides context to language models, such as files, database schemas, or application-specific information." The spec's framing here is the opposite of tools: resources are "designed to be application-driven, with host applications determining how to incorporate context based on their needs."
In practice that means the host — the Claude, Cursor or VS Code UI, not the model — decides how resources reach the conversation. The spec lists three patterns explicitly: expose resources through a picker UI for explicit selection, let a person search and filter available resources, or implement automatic inclusion "based on heuristics or the AI model's selection." Note that even the automatic case still runs through application logic the host built — a resource never simply appears because the model asked for it the way a tool call does.
What are MCP prompts, and who decides when they're used?
Prompts are reusable message templates. The spec: prompts "allow servers to provide structured messages and instructions for interacting with language models," and clients "can discover available prompts, retrieve their contents, and provide arguments to customize them." The interaction model is the third distinct answer: prompts are "designed to be user-controlled, meaning they are exposed from servers to clients with the intention of the user being able to explicitly select them for use."
The spec adds a clarification worth keeping intact rather than paraphrasing away: "This refers to who decides when the prompt is used, not who authors its content. Prompt content is defined by the server." A prompt is authored upstream, on the server, and a person downstream chooses the moment it fires — typically as a slash command, which is the exact example the spec's own diagram shows. For the mechanics of building and invoking that kind of template, MCP prompt templates and slash commands goes deeper than the spec does.
Tools vs resources vs prompts, side by side
| Feature | Tools | Resources | Prompts |
|---|---|---|---|
| What it is | A callable function | A piece of context data | A reusable message template |
| Who decides it runs | The model | The host application | The user |
| Spec's own label | Model-controlled | Application-driven | User-controlled |
| Typical UI surface | Invisible — happens mid-turn | A picker, search, or filter list | A slash command or menu |
| Who authors the content | The server (the function's logic) | Whatever the URI points to | The server (even though the user triggers it) |
| Discovery request | tools/list | resources/list | prompts/list |
Which primitive should you reach for, as a server builder?
The spec doesn't hand you a flowchart, but the three "User Interaction Model" sections add up to one:
- Is this something the model should be free to decide on its own, mid-conversation, without the user picking it from a list? That's a tool. Database lookups, API calls, calculations — anything the model can reasonably judge it needs.
- Is this a piece of context a person (or the host's own logic) should attach deliberately, rather than the model reaching for it unprompted? That's a resource. A specific file, a specific record, a specific schema.
- Is this a piece of your wording — a template, a saved procedure, a house style — that only makes sense when a person deliberately invokes it? That's a prompt. The user picks the moment; you own what it says.
A single server routinely ships all three. A prompt library server, for example, might expose prompts/list for its saved templates while also shipping a search tool the model can call on its own to find one by topic.
How each client actually surfaces this
The spec is deliberately silent on user interface — it says implementations are "free to expose" each primitive "through any interface pattern that suits their needs." That freedom is why the same primitive looks different depending on what you're using:
- VS Code documents an exact convention on its own MCP page: type
/<MCP server>.<prompt>in the chat input to run a prompt, and a separate Add Context → MCP Resources menu (or the MCP: Browse Resources command) to attach a resource. - Claude Code uses a different slash-command shape entirely:
/mcp__servername__promptname. Using MCP inside Claude Code walks through connecting a server there in full, scopes and auth included. - Cursor's own MCP documentation lists Prompts as "Supported" in a feature table right alongside Tools and Resources — but nowhere on that page, or anywhere else we could find in Cursor's docs, does it say how you actually invoke one. If a Cursor server ships prompts, expect to go looking for the invocation method yourself.
Three real clients, three different answers to "how do I use a prompt" — and that's before you get to resources, where VS Code's picker-menu approach is one implementation choice among several the spec explicitly allows.
Does the transport — stdio or Streamable HTTP — change any of this?
No, and this is worth separating out because "MCP" gets used to describe both the protocol's meaning and its wire format, which are two different layers. The spec defines transport as a pure binding: it governs "how messages are framed and delivered, how request metadata is carried, and how cancellation and termination are signaled." It "does not define what the messages mean" — tools, resources and prompts behave identically no matter which binding carries them.
The two standard bindings:
stdio newline-delimited JSON-RPC over the standard streams
of a client-launched subprocess
Streamable HTTP each message is an HTTP POST to a single MCP endpoint;
replies arrive as a JSON object or a request-scoped
SSE stream
This matters in practice when a client only natively speaks one binding but the server you want is built on the other — a stdio-only client reaching an HTTP-only server (or vice versa) typically needs a small local bridge process in between, rather than a code change on either side.
Where the server config actually lives, client by client
This is where "just add it to your MCP config" stops meaning one thing. Four clients, four different files, verified against each vendor's own current docs:
| Client | Config file | Format |
|---|---|---|
| Codex | ~/.codex/config.toml (or project-scoped .codex/config.toml) | TOML, [mcp_servers.<name>] |
| Claude Desktop | claude_desktop_config.json | JSON |
| Claude Code | .mcp.json (project, committed) or ~/.claude.json (user scope) | JSON |
| Cursor | .cursor/mcp.json (project) or ~/.cursor/mcp.json (global) | JSON |
| VS Code | .vscode/mcp.json (workspace) or user profile | JSON |
Codex is the one most people get wrong, because everything else on this list is JSON with a similar mcpServers shape you can half-remember and mostly reuse. Codex's own MCP documentation is explicit that it "stores MCP configuration in config.toml alongside other Codex configuration settings," by default at ~/.codex/config.toml — a different format, in a different location, than the mcp.json-style files Claude Desktop, Claude Code, Cursor and VS Code all read. A JSON snippet copied straight from one of those into Codex's TOML file will not parse.
The distinction that actually matters
Every explanation above traces back to one question: who decided this should happen? A model decided — that's a tool. An application decided on the user's behalf — that's a resource. A person decided, explicitly — that's a prompt, even though the server wrote every word inside it. Once that question has an answer, "does this client support MCP" stops being useful, and "which of the three primitives does this client actually expose, and how do I trigger the one I want" is the question worth asking instead — client by client, since the spec, deliberately, won't answer it for you.
If you're building against a server that only exposes tools, or trying to figure out why a "supported" prompt feature won't fire in your editor, that's the diagnostic. Start with tools/list, resources/list, or prompts/list against the server directly, and reconcile whatever comes back with the client's own documented invocation method before assuming the server is broken. If the server isn't answering at all, that's a different problem — see why isn't my MCP server showing up? or the wider MCP troubleshooting guide for the full set of failure modes.
Stop rewriting prompts. Start shipping.
Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.
Create An Account