When developers start working with Model Context Protocol, the first thing that trips them up is the same question every time: what's the difference between Tools, Resources, and Prompts? They all live in the same server. They all get declared the same way. And in some cases, they can do the exact same thing.
But they serve completely different purposes. And if you pick the wrong one, your AI integration will feel awkward at best and broken at worst.
One Question That Separates All Three
The entire distinction comes down to a single question: who decides when it runs?
That's it. Tools are model-controlled — the AI decides when to call them. Resources are application-controlled — the host app decides when to load them. Prompts are user-controlled — the user explicitly selects them. Get this one distinction right and the rest of your MCP server design falls into place.
Tools: The AI Takes Action
Tools are functions the AI model can discover and invoke automatically during a conversation. When a user says "deploy the latest build to staging," the model evaluates available tools, picks the right one, constructs the arguments, and calls it.
The key trait: tools are called, they do something, they return a result. Use tools when:
- Actions with side effects — creating records, sending messages, triggering deployments, executing transactions. Anything that changes state.
- Computations requiring external processing — running database queries, calling APIs, performing calculations the model can't do natively.
- Dynamic data retrieval — when the query depends on conversation context, like "search for tickets assigned to me."
// MCP Tool definition
server.tool("deploy_to_staging", {
description: "Deploy a branch to the staging environment",
inputSchema: {
branch: { type: "string" },
service: { type: "string" }
}
}, async ({ branch, service }) => {
const result = await deploy(branch, service);
return { text: `Deployed ${branch} to staging` };
});
Resources: Read-Only Context
Resources are structured, read-only data that the host application loads as context. They're identified by URIs and they never perform actions — they just provide information.
This is the #1 confusion developers have: "Why can't I just use a tool to fetch data?" You can. But here's the difference — the model cannot autonomously request a resource the way it can call a tool. The user or the application loads it. If you need the model to dynamically fetch data based on conversation, use a tool. If you need to pre-load static context, use a resource.
Use resources for:
- Database schemas — semi-static metadata the model needs as context
- Configuration files — app settings, environment details
- Documentation — API specs, README content, coding standards
- File contents — source code, templates, data files
// MCP Resource definition
server.resource(
"db-schema",
"db://schema",
{ description: "Database table schemas" },
async () => ({
contents: [{
uri: "db://schema",
mimeType: "application/json",
text: JSON.stringify(await getDbSchema())
}]
})
);
Prompts: User-Selected Templates
Prompts are predefined templates that the user explicitly selects. Think slash commands — /code-review, /weekly-report, /explain-error. The model never invokes a prompt on its own.
Prompts solve a specific problem: when teams embed complex instructions in tool descriptions or system prompts instead of defining reusable templates. This leads to inconsistent behavior across different hosts and makes it hard to update interaction patterns without redeploying.
Use prompts for:
- Code review workflows — standardized review checklist
- Report generation — weekly sales report, sprint summary
- Onboarding — explain this codebase to a new developer
- Domain-specific helpers — any repeatable workflow worth naming
// MCP Prompt definition
server.prompt(
"code-review",
{ file: { type: "string" } },
async ({ file }) => ({
messages: [{
role: "user",
content: `Review this code for bugs, security
issues, and performance. Be specific with line
numbers.\n\nFile: ${file}`
}]
})
);
The Decision Framework
When you're building an MCP server, ask two questions for every capability:
Question 1: Who should control when this runs? If the AI should decide autonomously → Tool. If the app should load background data → Resource. If the user should explicitly choose → Prompt.
Question 2: Does it perform an action or provide data? Actions (write, create, send, execute) → almost always a Tool. Static or semi-static data → usually a Resource. Structured conversation starters → Prompts.
Real-World Example: PostgreSQL MCP Server
Here's how you'd split capabilities for a database MCP server:
The schema is a Resource — semi-static, loaded at session start. Query execution is a Tool — the model decides when and what to query based on conversation. Explaining a query or auditing a table is a Prompt — the user explicitly triggers it.
Three Mistakes Everyone Makes
- Using a tool when you need a resource — If data is static and the model doesn't need to decide when to fetch it, don't waste a tool call. Load it as a resource. Cheaper, faster, cleaner.
- Skipping prompts entirely — Teams embed complex instructions in tool descriptions or system prompts instead of defining reusable templates. This makes behavior inconsistent across different hosts.
- Using a resource for dynamic data — If the data retrieval depends on conversation context (like searching a knowledge base), it needs to be a tool. Resources can't accept runtime parameters from the model.
A Note on Host Support
Not all hosts support all three primitives equally. Tools and Resources work across Claude Desktop, Cursor, VS Code, and Windsurf. Prompt support varies — Claude Desktop shows them as slash commands, but some hosts don't expose prompts in their UI at all. Check your target host before building heavy prompt workflows.
How All Three Work Together
In a real workflow, all three primitives complement each other:
The user clicks /weekly-report (Prompt). The host loads the database schema (Resource). The model runs the queries it needs (Tool). Each primitive does what it does best. Modular, clean, and predictable.
The Cheat Sheet
| Tools | Resources | Prompts | |
|---|---|---|---|
| Controlled by | Model | Application | User |
| Purpose | Perform actions | Provide data | Structure interaction |
| Side effects? | Yes | No (read-only) | No |
| Dynamic? | Yes | Static / semi-static | Template with params |
| Think of it as | API endpoint | Database view | Slash command |
| Method | tools/call | resources/read | prompts/get |
Final Thoughts
MCP's three primitives exist for a reason. Tools handle actions and dynamic data retrieval. Resources provide static context. Prompts encode reusable interaction patterns.
The single most important design decision when building an MCP server is choosing the right primitive for each piece of functionality. Ask who controls it, ask whether it acts or reads, and the answer becomes obvious.
Tools do. Resources know. Prompts guide.
Related
- Loop Engineering Explained — how loops are replacing prompting
- Context Engineering Explained — why what the model sees matters more than what you type