3 concepts MCP:
- Hiểu MCP resources — read-only data exposed by server
- Static vs dynamic resources
- Use @mcp.resource decorator
- URI patterns cho resources
Mở đầu: Resources = data, not actions
- Tools — actions (create, update, delete)
- Prompts — instructions (templates)
- Resources — data (read-only)
Resource = document, database record, API response. Client read, doesn't execute.
When resources vs tools?
| Resource | Tool | |
|---|---|---|
| Purpose | Expose data | Perform action |
| Side effect | None (read) | May have |
| URI | Yes (unique identifier) | No |
| Example | file://report.pdf, db://users/123 | get_user(123), create_post() |
Resource = GET. Tool = POST/PUT/DELETE (loose analogy).
Static resource
Fixed URI, content:
@mcp.resource("docs://report.pdf")
def get_report():
return """This report describes Q3 performance..."""
Clients can read resource docs://report.pdf.
Dynamic resource (URI template)
URI with parameters:
@mcp.resource("docs://{doc_id}")
def get_doc(doc_id: str):
if doc_id not in docs:
raise ValueError(f"Doc {doc_id} not found")
return docs[doc_id]
Client reads docs://report.pdf or docs://plan.md — same function.
Full example — documents
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("DocumentMCP")
docs = {
"report.pdf": "Q4 report content...",
"plan.md": "Project plan content...",
}
@mcp.resource("docs://all")
def list_all_docs() -> str:
return "\n".join(docs.keys())
@mcp.resource("docs://{doc_id}")
def read_doc(doc_id: str) -> str:
if doc_id not in docs:
raise ValueError(f"Doc '{doc_id}' not found")
return docs[doc_id]
Test via Inspector → Resources tab.
Client read resource
resources = await client.list_resources()
result = await client.read_resource("docs://report.pdf")
for content in result.contents:
if content.type == "text":
print(content.text)
Why resources matter
1. Auto-discovery
Client list all resources without prior knowledge. User browse data without remembering tool names.
2. Caching-friendly
Read-only + URI-identified → client can cache by URI.
3. Standard contract
Any client can read any resource từ any MCP server. Universal data access pattern.
4. Claude awareness
Claude thấy resources available, có thể reference: "Based on docs://report.pdf, the answer is..."
Resource types
Text
@mcp.resource("schema://users")
def get_schema() -> str:
return """CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100)
)"""
JSON
import json
@mcp.resource("config://app")
def get_config() -> str:
return json.dumps({"version": "1.0", "debug": False})
Binary (via URI)
@mcp.resource("image://logo")
def get_logo():
with open("logo.png", "rb") as f:
return f.read() # binary content
MCP supports binary resources qua specific content type.
Resources + tools combo
Resources cho browsing, tools cho mutation:
@mcp.resource("docs://{doc_id}")
def get_doc(doc_id):
return docs[doc_id]
@mcp.tool()
def edit_doc(doc_id: str, old: str, new: str):
docs[doc_id] = docs[doc_id].replace(old, new)
Clean separation.
URI conventions
Good:
docs://report.pdf— clear scheme + identifierusers://alice— namespacefile:///home/jimmy/note.md— RFC 3986 format
Bad:
doc1— no schemesomedata— no structure
Use scheme that makes sense cho domain.
Anti-patterns
Resource with side effects
@mcp.resource("counter://next")
def counter():
global count
count += 1
return str(count) # MUTATING on read!
Resources MUST be read-only. Client caches, call multiple times → inconsistent.
Fix: Tool if action needed.
Expose entire database as resource
Too much data, tied to impl details.
Fix: Curated views. docs://all, docs://{id}, specific.
Mix text + binary without content type
Client parse fail.
Fix: Explicit MIME types.
Áp dụng ngay
Bài tập 1: Add resources (20 phút)
Add to server:
docs://list— list all doc IDsdocs://{id}— specific docdocs://stats— count + total size
Test via Inspector.
Bài tập 2: Client list + read (15 phút)
Client enumerate resources, read one, print content.
Tóm tắt bài học
- Resources = read-only data với URI.
- Static: fixed URI. Dynamic: URI template with params.
@mcp.resource("scheme://path")decorator.- Complement tools: resources for browse, tools for mutate.
- Never mutate on resource read — contract.