Tools vs Resources vs Prompts in MCP
Understand the three core primitives of the Model Context Protocol — tools, resources, and prompts. Learn when to use each, how they differ, and see practical examples.
title: "Tools vs Resources vs Prompts in MCP" description: "Understand the three core primitives of the Model Context Protocol — tools, resources, and prompts. Learn when to use each, how they differ, and see practical examples." order: 3 keywords:
- MCP tools
- MCP resources
- MCP prompts
- MCP primitives
- tools vs resources
- MCP tool definition
- MCP resource URI
- MCP prompt template
- model context protocol primitives date: "2026-04-01"
MCP servers expose functionality through three core primitives: Tools (model-controlled functions the AI can invoke), Resources (application-controlled data the AI can read), and Prompts (user-controlled templates for structured interactions). Understanding when to use each primitive is essential for building effective MCP servers.
The Three Primitives
The Model Context Protocol defines three distinct ways for servers to expose functionality to clients. Each primitive serves a different purpose and is controlled by a different actor in the system.
MCP primitives are the three fundamental building blocks that MCP servers use to expose capabilities: tools (executable functions), resources (readable data), and prompts (reusable templates). Each primitive has a different control model — tools are model-controlled, resources are application-controlled, and prompts are user-controlled.
At a Glance
| Aspect | Tools | Resources | Prompts |
|---|---|---|---|
| Purpose | Execute actions and computations | Provide data and context | Define structured interactions |
| Controlled by | AI model decides when to invoke | Application decides when to fetch | User selects explicitly |
| Analogy | Functions / API endpoints | GET endpoints / file reads | Slash commands / templates |
| Side effects | Can have side effects | Should be read-only | No direct side effects |
| Discovery | tools/list | resources/list | prompts/list |
| Invocation | tools/call | resources/read | prompts/get |
| Input schema | JSON Schema for parameters | URI-based addressing | JSON Schema for arguments |
| Output | Content array (text, images) | Content with MIME type | Array of prompt messages |
Tools: Model-Controlled Functions
Tools are the most commonly used MCP primitive. They represent executable functions that the AI model can decide to invoke based on the user's request.
What Tools Are For
Tools are designed for operations that do something — they perform actions, run computations, interact with external systems, or transform data. Unlike resources, tools can have side effects.
Examples of MCP tools:
- query_database — Execute SQL queries against a database
- send_email — Send an email through an email provider
- create_file — Create a new file on the filesystem
- search_web — Perform a web search
- run_tests — Execute a test suite
- deploy_application — Trigger a deployment pipeline
How Tools Work
Each tool is defined with a name, description, and an input schema written in JSON Schema format. The AI model uses the description and schema to understand what the tool does and how to invoke it.
{
"name": "query_database",
"description": "Execute a read-only SQL query against the application database and return results",
"inputSchema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "The SQL query to execute"
}
},
"required": ["sql"]
}
}
When the AI model decides to use a tool, the client sends a tools/call request to the server. The server executes the tool logic and returns a result containing one or more content items.
Building Tools with mcp-framework
With mcp-framework, tools are defined as classes with typed input schemas:
import { MCPTool } from "mcp-framework";
import { z } from "zod";
class QueryDatabaseTool extends MCPTool<typeof inputSchema> {
name = "query_database";
description = "Execute a read-only SQL query against the application database";
schema = {
sql: {
type: z.string(),
description: "The SQL query to execute",
},
};
async execute({ sql }: { sql: string }) {
const results = await db.query(sql);
return JSON.stringify(results, null, 2);
}
}
Building Tools with the Official SDK
With the @modelcontextprotocol/sdk, tools are registered using a functional pattern:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "query_database") {
const { sql } = request.params.arguments;
const results = await db.query(sql);
return {
content: [{ type: "text", text: JSON.stringify(results) }],
};
}
});
Write clear, specific tool descriptions. The AI model relies on the description to decide when and how to use the tool. Include what the tool does, what it returns, and any important constraints. Avoid vague descriptions like "does database stuff" — instead write "Execute a read-only SQL query and return results as JSON."
Resources: Application-Controlled Data
Resources represent data that the server can provide to the client for context. Unlike tools, resources are read-only and should not have side effects.
What Resources Are For
Resources are designed for providing data and context — file contents, database records, API responses, configuration values, or any other information the AI model might need to understand the user's environment.
Examples of MCP resources:
- file:///project/src/index.ts — Contents of a source file
- db://users/schema — Database table schema
- config://app/settings — Application configuration
- git://repo/status — Current git repository status
- metrics://dashboard/today — Today's application metrics
How Resources Work
Resources are identified by URIs and can be either static (listed by the server) or dynamic (resolved via URI templates). The client reads a resource by sending a resources/read request with the resource URI.
{
"uri": "file:///project/src/index.ts",
"name": "index.ts",
"description": "Main application entry point",
"mimeType": "text/typescript"
}
Static vs Dynamic Resources
| Aspect | Static Resources | Dynamic Resources |
|---|---|---|
| Discovery | Listed via resources/list | Resolved via URI templates |
| URI | Fixed, known ahead of time | Constructed from template parameters |
| Example | config://app/settings | db://users/{userId} |
| Use case | Known files, fixed endpoints | User-specific data, parameterized queries |
Resource Subscriptions
Clients can subscribe to resource changes using resources/subscribe. When a subscribed resource changes, the server sends a notification, allowing the client to re-read the resource and update its context.
If you are unsure whether to expose something as a resource or a tool, ask: "Does this operation have side effects?" If yes, make it a tool. If it is purely reading data, make it a resource. That said, many simple read operations work fine as tools too — the distinction is about signaling intent to the client and the AI model.
Prompts: User-Controlled Templates
Prompts are reusable templates that define structured interactions. They are the least commonly used primitive but are powerful for creating consistent, repeatable workflows.
What Prompts Are For
Prompts define multi-step interaction patterns — predefined workflows with specific roles, context injection, and structured outputs. Users explicitly select prompts, typically through a UI element like a slash command or dropdown menu.
Examples of MCP prompts:
- code_review — A structured code review workflow that examines code for bugs, style, and performance
- summarize_document — A template for summarizing documents with specific focus areas
- debug_error — A guided debugging workflow that collects error details and suggests fixes
- generate_tests — A template for generating test cases from source code
How Prompts Work
Each prompt is defined with a name, description, and optional arguments. When a user selects a prompt, the client sends a prompts/get request and receives back an array of messages that define the interaction:
{
"name": "code_review",
"description": "Perform a structured code review",
"arguments": [
{
"name": "code",
"description": "The code to review",
"required": true
},
{
"name": "language",
"description": "Programming language",
"required": false
}
]
}
The server responds with structured messages:
{
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please review the following TypeScript code for bugs, performance issues, and style:\n\n```typescript\n// code here\n```"
}
}
]
}
Prompts with Embedded Resources
Prompts can embed resources directly, combining templates with dynamic data:
{
"messages": [
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "file:///project/src/index.ts",
"text": "// file contents...",
"mimeType": "text/typescript"
}
}
}
]
}
Choosing the Right Primitive
Use this guide to choose the right primitive:
- Use a Tool when: The AI model needs to perform an action, the operation may have side effects, or the operation requires computed input from the AI model.
- Use a Resource when: You are exposing read-only data, the data can be identified by a URI, or the application (not the AI) should control when the data is fetched.
- Use a Prompt when: You want to define a reusable interaction pattern, the workflow has a specific structure, or users should explicitly choose when to invoke it.
Common Patterns
| Scenario | Recommended Primitive | Reason |
|---|---|---|
| Search a database | Tool | Requires computed input (search query) from the AI model |
| Read a config file | Resource | Static, read-only data identified by a path |
| Send an email | Tool | Has side effects (sends a real email) |
| Code review workflow | Prompt | Structured interaction pattern selected by the user |
| List git branches | Tool or Resource | No side effects, but may benefit from AI-driven filtering |
| Application logs | Resource | Read-only data stream that the application controls |
| Deploy to production | Tool | Critical side effect requiring explicit invocation |
| Generate test cases | Prompt | Structured workflow with specific template format |
Combining Primitives
The most powerful MCP servers combine all three primitives. For example, a database MCP server might expose:
- Tools:
query_database,insert_record,update_record - Resources:
db://schema,db://tables/{table}/sample - Prompts:
analyze_query_performance,generate_migration
This gives the AI model tools for taking action, resources for understanding context, and prompts for structured workflows — all through a single MCP server.
If you are building your first MCP server, start with tools. They are the most commonly used primitive and provide the most immediate value. You can always add resources and prompts later as your server matures. Both mcp-framework and the @modelcontextprotocol/sdk make it easy to add primitives incrementally.