Mcp resource patterns
Skill VersoXBT/claude-initial-setup/skills/mcp-development/mcp-resource-patterns
Patterns for MCP resources including URIs, templates, subscriptions, dynamic resources, context provision, and MIME types. Use when the user is exposing data as MCP resources, designing resource URIs, implementing resource templates with parameters, setting up resource subscriptions, or providing context to Claude through resources.From its SKILL.md
npx -y skills add VersoXBT/claude-initial-setup --skill mcp-resource-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.5 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
MCP Resource Patterns
Patterns for designing and implementing MCP resources. Covers URI design, templates, subscriptions, dynamic resources, MIME types, and context provision strategies.
When to Use
- User is exposing data as MCP resources (files, configs, database records)
- User is designing resource URI schemes
- User needs parameterized resource templates
- User wants real-time resource updates via subscriptions
- User is providing contextual data to Claude through resources
Core Patterns
Static Resources
Expose fixed data sources that Claude can read for context.
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
// Static resource with a fixed URI
server.resource(
"project-config",
"config://project",
{ description: "Project configuration including build settings and dependencies." },
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(await loadProjectConfig(), null, 2)
}]
})
);
// Static resource for a text file
server.resource(
"readme",
"file:///project/README.md",
{ description: "Project README with setup instructions and architecture overview." },
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "text/markdown",
text: await fs.readFile("README.md", "utf-8")
}]
})
);
Resource Templates
Parameterized URIs that resolve to specific resources based on input.
// Template with a parameter
server.resource(
"user-profile",
new ResourceTemplate("users://{userId}/profile", { list: undefined }),
{ description: "User profile data by user ID." },
async (uri, { userId }) => {
const user = await db.users.findById(userId);
if (!user) {
return { contents: [] }; // Empty contents signals not found
}
return {
contents: [{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
id: user.id,
name: user.name,
email: user.email,
role: user.role
}, null, 2)
}]
};
}
);
// Template with list callback for discovery
server.resource(
"log-file",
new ResourceTemplate("logs://{date}/{level}", {
list: async () => {
const dates = await getAvailableLogDates();
return dates.flatMap(date =>
["info", "warn", "error"].map(level => ({
uri: `logs://${date}/${level}`,
name: `${date} ${level} logs`,
description: `${level}-level logs from ${date}`
}))
);
}
}),
{ description: "Application logs filtered by date and level." },
async (uri, { date, level }) => ({
contents: [{
uri: uri.href,
mimeType: "text/plain",
text: await readLogFile(date, level)
}]
})
);
Resource Subscriptions
Notify clients when resource content changes.
# Python - resource with subscription support
from mcp.server import Server
import mcp.types as types
server = Server("my-server")
@server.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri="metrics://system/health",
name="System Health",
description="Real-time system health metrics.",
mimeType="application/json"
)
]
@server.read_resource()
async def read_resource(uri: str) -> str:
if uri == "metrics://system/health":
metrics = await collect_system_metrics()
return json.dumps(metrics)
raise ValueError(f"Unknown resource: {uri}")
# Notify subscribers when metrics change
async def on_metrics_update():
await server.request_context.session.send_resource_updated(
uri="metrics://system/health"
)
Dynamic Resource Lists
Generate resource lists dynamically based on current state.
// List resources dynamically from a database
server.resource(
"db-table",
new ResourceTemplate("db://{schema}/{table}", {
list: async () => {
const tables = await db.query(
"SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema NOT IN ('pg_catalog', 'information_schema')"
);
return tables.map(t => ({
uri: `db://${t.table_schema}/${t.table_name}`,
name: `${t.table_schema}.${t.table_name}`,
description: `Database table ${t.table_schema}.${t.table_name}`,
mimeType: "application/json"
}));
}
}),
{ description: "Database table schema and sample data." },
async (uri, { schema, table }) => {
const columns = await db.getColumns(schema, table);
const sample = await db.query(`SELECT * FROM "${schema}"."${table}" LIMIT 5`);
return {
contents: [{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({ columns, sample_rows: sample }, null, 2)
}]
};
}
);
MIME Types
Choose the right MIME type for resource content.
// Common MIME types for MCP resources
const mimeTypes = {
"application/json": "Structured data, API responses, configs",
"text/plain": "Logs, raw text, CLI output",
"text/markdown": "Documentation, READMEs, notes",
"text/html": "Web content (use sparingly)",
"application/xml": "XML configs, SOAP responses",
"text/csv": "Tabular data, exports",
"application/pdf": "Binary documents (base64 encoded)",
"image/png": "Screenshots, diagrams (base64 encoded)",
};
// Binary content uses base64 encoding
server.resource("screenshot", "screen://current", {}, async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "image/png",
blob: await captureScreenBase64() // base64-encoded string
}]
}));
Anti-Patterns
- Using opaque IDs in URIs instead of human-readable paths (
res://abc123vsusers://42/profile) - Not implementing the list callback on templates (Claude cannot discover available resources)
- Returning massive resources without summarization (floods context window)
- Using
text/plainfor structured data that should be JSON - Not handling missing resources (return empty contents, do not throw)
- Exposing sensitive data (secrets, credentials) through resources without access control
- Making resource reads slow by including expensive computations (cache instead)
Quick Reference
| Concept | Pattern |
|---|---|
| Static resource | Fixed URI, single data source |
| Resource template | Parameterized URI with {param} syntax |
| List callback | Discovery function returning available resources |
| Subscription | Server pushes updates when content changes |
| Binary content | Use blob field with base64, set correct MIME type |
URI scheme conventions:
file:///path/to/file # Local files
db://{schema}/{table} # Database objects
config://{section} # Configuration
logs://{date}/{level} # Log entries
metrics://{category} # Monitoring data
repo://{owner}/{name} # Repository data
Checklist: human-readable URIs, descriptive descriptions, correct MIME types, list callbacks for discoverability, concise content, redacted sensitive data.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most mcp tooling skills give in ~1.6k tokens
Counted across 780 of the 1,136 authors here whose files we hold, read 2026-09-06
- Use Zod for input validationin 34 of 780, across 21 files
- Use stdio for local clientsin 27 of 780, across 10 files
- Restart Claude Code after configurationin 26 of 780, across 23 files
- Verify MCP server connection before using toolsin 23 of 780, across 17 files
- Define input schemas for every toolin 20 of 780, across 11 files
- Use Streamable HTTP for remote clientsin 18 of 780, across 8 files
- Pin SDK version in package.jsonin 17 of 780, across 6 files
- Keep server logic independent of transportin 16 of 780, across 6 files
- Verify SDK methods against official documentationin 15 of 780, across 5 files
- Format evaluation results as an XML filein 15 of 780, across 12 files
- Test servers using the MCP Inspectorin 15 of 780, across 14 files
- Create ten complex and independent evaluation questionsin 14 of 780, across 11 files
Said here and by no other author read
- use human-readable paths for resource URIs
- implement list callbacks for all resource templates
- return empty contents for missing resources
- use blob field with base64 for binary content
- set correct MIME types for all resource content
- provide descriptive names and descriptions for resources
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.