Resources and Prompts: The Other Two Primitives
Master MCP Resources for exposing data and context, and MCP Prompts for creating reusable interaction templates. Learn when to use each primitive.
Premium Course Content
This lesson is part of a premium course. Upgrade to Pro to unlock all premium courses and content.
- Access all premium courses
- 1000+ AI skill templates included
- New content added weekly
Tools get the most attention, but MCP has three primitives for a reason. Resources provide data without executing code. Prompts give users structured starting points. Knowing when to use each one is what separates a competent MCP developer from a great one.
🔄 Quick Recall: In the previous lesson, you mastered Tools — functions the AI calls to perform actions. Now you’ll learn the other two primitives: Resources (for data) and Prompts (for templates).
Resources: Data Your AI Can Read
Resources expose data and context to AI assistants. Unlike Tools, which execute actions, Resources simply provide information.
When to Use Resources vs. Tools
| Use a Resource When | Use a Tool When |
|---|---|
| Providing static or slow-changing data | Performing an action or computation |
| Loading context at the start of a conversation | Getting real-time data on demand |
| Exposing configuration or documentation | Making API calls or database writes |
| The client should decide when to fetch the data | The AI model should decide when to invoke it |
Example: A database schema is a Resource (static context the AI needs). A database query is a Tool (an action the AI performs).
Defining Resources
Resources use URIs to identify themselves:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Data Server")
@mcp.resource("config://app-settings")
def get_app_settings() -> str:
"""Current application configuration."""
return """
Database: PostgreSQL 16
Cache: Redis 7
API Rate Limit: 100 req/min
Debug Mode: Off
"""
@mcp.resource("docs://api-reference")
def get_api_docs() -> str:
"""API endpoint documentation."""
return open("api-docs.md").read()
The URI scheme (config://, docs://) tells the client what kind of data this is. Common schemes:
| Scheme | Use Case | Example |
|---|---|---|
file:// | Local files | file:///var/log/app.log |
db:// | Database records | db://users/schema |
config:// | Configuration | config://app-settings |
docs:// | Documentation | docs://api-reference |
✅ Quick Check: You have a database table schema that rarely changes and an API that returns live user counts. Which should be a Resource and which a Tool? (Answer: The table schema is a Resource — it’s static context the AI needs for writing queries. The live user count is a Tool — it requires executing a query to get current data.)
Dynamic Resources with Templates
For resources that depend on parameters, use URI templates:
@mcp.resource("db://users/{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""User profile data."""
user = database.get_user(user_id)
return f"Name: {user.name}\nEmail: {user.email}\nRole: {user.role}"
The {user_id} placeholder makes this a template — the client can request any user’s profile by filling in the ID.
Resource Lists
Servers can provide a list of available resources so clients know what to fetch:
@mcp.resource("file://logs/{filename}")
def get_log_file(filename: str) -> str:
"""Read a log file."""
return open(f"/var/log/{filename}").read()
When a client lists resources, it discovers available log files and can fetch specific ones as needed. The key difference from Tools: the client application decides when to fetch Resources (often at conversation start), while the AI model decides when to call Tools (during the conversation).
Prompts: Reusable Interaction Templates
Prompts are pre-defined conversation starters that users select explicitly. They provide structure for recurring workflows.
When to Use Prompts
Prompts shine for repeatable, structured interactions:
- Code review: Always analyze the same aspects (security, performance, readability)
- Daily standup: Always collect status, blockers, and plans
- Data analysis: Always start with the same exploration steps
- Report generation: Always follow the same format
Defining Prompts
from mcp.server.fastmcp import FastMCP
from mcp.types import UserMessage, AssistantMessage
mcp = FastMCP("Workflow Server")
@mcp.prompt()
def code_review(language: str, code: str) -> list:
"""Perform a structured code review."""
return [
UserMessage(
content=f"""Review this {language} code for:
1. **Bugs** — Logic errors, edge cases, null handling
2. **Security** — Input validation, injection risks, auth issues
3. **Performance** — Unnecessary allocations, O(n²) loops, missing indexes
4. **Readability** — Naming, structure, comments where needed
Code to review:
```{language}
{code}
Provide specific line references and fix suggestions for each issue found.""" ) ]
When a user selects this prompt in their AI client, they fill in the `language` and `code` parameters. The structured template ensures every code review covers the same aspects.
### Multi-Turn Prompts
Prompts can include multiple messages to set up a conversation context:
```python
@mcp.prompt()
def debug_session(error_message: str, stack_trace: str) -> list:
"""Start a structured debugging session."""
return [
UserMessage(
content=f"I'm getting this error:\n\n{error_message}\n\nStack trace:\n```\n{stack_trace}\n```"
),
AssistantMessage(
content="I'll help you debug this. Let me analyze the error and stack trace systematically. First, let me identify the root cause..."
),
UserMessage(
content="Please: (1) identify the root cause, (2) explain why it happens, (3) suggest a fix with code, (4) recommend how to prevent this class of error."
)
]
This creates a three-message conversation context before the AI even responds — setting up a productive debugging workflow.
✅ Quick Check: A user selects an MCP Prompt for “SQL query optimization.” Who controls when this prompt is used — the AI model, the client app, or the user? (Answer: The user. Prompts are user-controlled — the user explicitly selects them as conversation starters. This contrasts with Tools (model-controlled) and Resources (application-controlled).)
Combining All Three Primitives
A well-designed MCP server uses all three primitives together:
mcp = FastMCP("Project Assistant")
# RESOURCE: Static project context
@mcp.resource("project://readme")
def get_readme() -> str:
"""Project README documentation."""
return open("README.md").read()
@mcp.resource("project://schema")
def get_db_schema() -> str:
"""Database schema for the project."""
return open("schema.sql").read()
# TOOL: Dynamic actions
@mcp.tool()
async def query_database(sql: str) -> str:
"""Execute a read-only SQL query."""
# ... execute and return results
@mcp.tool()
async def create_issue(title: str, body: str) -> str:
"""Create a GitHub issue for this project."""
# ... create and return issue URL
# PROMPT: Structured workflows
@mcp.prompt()
def feature_planning(feature_name: str) -> list:
"""Plan a new feature with structured analysis."""
return [UserMessage(
content=f"Plan the '{feature_name}' feature. Analyze: "
f"1) database changes needed, "
f"2) API endpoints, "
f"3) testing strategy, "
f"4) rollout plan."
)]
How they work together:
- The client loads Resources (README, schema) as conversation context
- The user selects the Prompt “feature planning” to start a structured workflow
- The AI calls Tools (query database, create issue) as needed during the conversation
The Decision Matrix
When deciding which primitive to use:
Does the AI need to perform an action?
→ Yes → Use a TOOL
Is it static data the AI should have as context?
→ Yes → Use a RESOURCE
Is it a reusable conversation starter?
→ Yes → Use a PROMPT
| Primitive | Who Controls | When Used | Purpose |
|---|---|---|---|
| Tool | AI model | During conversation | Execute actions |
| Resource | Client app | Before/during conversation | Provide context |
| Prompt | User | Conversation start | Structure workflows |
Practice Exercise
Create an MCP server for a “documentation assistant” that uses all three primitives:
- Resource: Expose a project’s README and API docs
- Tool: Search documentation for a keyword and return matching sections
- Prompt: “Explain this API endpoint” — a template that structures endpoint explanation requests
Test with Claude Desktop. Try loading the resources, using the prompt, and calling the search tool in one conversation.
Key Takeaways
- Resources expose data and context — fetched by the client, not called by the AI model
- Resources use URIs with custom schemes (
file://,db://,config://) - Prompts are user-selected conversation templates with parameter support
- Prompts ensure consistency for recurring workflows (code reviews, debugging, planning)
- Use all three primitives together: Resources for context, Tools for actions, Prompts for structure
- Decision rule: actions → Tool, data → Resource, templates → Prompt
Up Next
In the next lesson, you’ll build real-world MCP servers that connect to databases, APIs, and file systems — the patterns you’ll use most in production.
Knowledge Check
Complete the quiz above first
Lesson completed!