MCP Tools, Resources and Prompts
Three server primitives, divided not by what they do but by who pulls the trigger: the model picks tools, the application supplies resources, the user invokes prompts. Choose by blast radius, not by data shape.
Most explanations of MCP stop at it's USB-C for AI tools.
That metaphor tells you MCP is a standard connector and nothing else — not what travels through it, and certainly not why a server author has three different ways to expose the same underlying data.
There are three server primitives, and the difference between them is not technical. It is who decides when they get used.
| Primitive | Who controls it | What it is |
|---|---|---|
| Tools | The model | Executable functions the model may invoke to act |
| Resources | The application | Data the host application pulls in as context |
| Prompts | The user | Templates a person deliberately selects |
Every design question about MCP servers resolves to picking the right row. Get it wrong and you build a server that either does nothing useful or does something alarming.
Tools: the model chooses
A tool is an executable function. The server advertises it through tools/list with a name, a human-readable title, a description, and an inputSchema — a JSON Schema describing its arguments. The model reads that list, decides a tool is relevant, and the client issues tools/call.
The word doing the work is decides. Nobody approves each call in advance. You wrote the tool, you wrote its description, and then a language model chose to run it based on a conversation you were not present for.
This is why tool design is the part most teams get wrong first. The description field is not documentation for a human reading your API reference — it is a prompt, read by a model, at the moment it is deciding whether to act. The spec's own example names its tools calculator_arithmetic and weather_current rather than calculate and weather, because a model federating tools from nine servers needs to tell yours apart from everyone else's.
Tool results come back as a content array rather than a bare string, which is what lets a single call return text, images, and embedded resources together.
Resources: the application chooses
A resource is data, identified by a URI, that the host application decides to put in front of the model. File contents, a database schema, an API response. The client discovers them with resources/list and reads them with resources/read.
The distinction people miss: a resource is not something the model fetches when it feels like it. That would be a tool. A resource is something the application decides is relevant — because you opened that file, or selected that record, or because the server told the application this schema matters.
If you find yourself building a resource the model is supposed to request on its own initiative, you have built a tool with extra steps. Expose it as a tool.
The practical consequence is a context-budget one. Resources are pulled in by the application, so the application controls how much of your window they consume — which is the whole argument in what is a context window and the reason RAG, tools and long context is a real architectural choice rather than a preference.
Prompts: the user chooses
A prompt is a reusable template — a slash command, a menu item, a saved workflow. Discovered with prompts/list, retrieved with prompts/get. The user picks it.
This is the least-used primitive and the most misunderstood, largely because prompt
already means six other things. In MCP it does not mean the system prompt, and it does not mean the text you type. It means a server-supplied template the user explicitly invokes.
The canonical example from the spec: a database server exposes tools for querying, a resource holding the schema, and a prompt carrying few-shot examples of good queries against that schema. Three primitives, one server, one domain — and each one enters the conversation through a different door.
If you have written prompt templates with variables before, this is that idea moved to the server, so the template ships with the integration instead of living in someone's notes.
Why the control model is the whole point
Read the three rows again as a security model rather than an API surface.
Tools are the dangerous primitive, because the model triggers them and the model can be talked into things. Anything a tool can do, a sufficiently well-crafted piece of text in a web page or an email can eventually cause it to do — which is the argument in prompt injection, and it is not a bug you patch out of your server.
Resources are safer by construction, because the application decided. Prompts are safest, because a person clicked.
So when you are deciding how to expose a capability, the question is not what shape is this data.
It is: what is the worst thing that happens if this fires at the wrong moment, and who should be holding the trigger? A read-only lookup can be a tool. Anything that spends money, sends a message, or deletes a row should be asking whether the user ought to be picking it instead.
That reasoning is the same one behind guardrails and safety for AI agents: scope to least privilege, and require a human for anything irreversible.
Discovery is dynamic, and that matters
Each primitive has a */list method, and listings are allowed to change at runtime. A server can expose different tools depending on who authenticated, what state the workspace is in, or which features are enabled.
Under protocol version 2026-07-28, change notifications are opt-in. A client that wants to hear about them opens a long-lived stream with subscriptions/listen, naming the notification types it cares about; the server acknowledges the subset it will honour and delivers matching notifications on that stream. The spec is explicit that this is best-effort and that clients should still poll — notifications can be lost across transport reconnects.
Listings also carry caching hints now: a ttlMs freshness window and a cacheScope saying who may reuse the response. If you are writing a client, honour them; a five-minute tool-list cache is the difference between one tools/list per session and one per turn.
What about the client primitives?
Servers can also call back into the client. As of protocol version 2026-07-28 there is exactly one live client primitive — elicitation, where a server asks the user for information mid-request.
Sampling and roots, which appear in almost every MCP explainer written before now, are both deprecated and scheduled for removal. That is covered in what changed in MCP 2026-07-28, and it is worth reading before you build against a tutorial of uncertain vintage.
The short version
Three primitives, three owners: the model picks tools, the application supplies resources, the user invokes prompts. Choose by asking who should be pulling the trigger, not by asking what shape the data is.
Then go and read how to build an MCP server — and browse the MCP directory to see how the servers you already use have made these calls.