Skip to content

Resources

Resources let your MCP server expose data that clients can read. Unlike tools (which perform actions), resources are for providing context — configuration files, database records, log entries, or any data the LLM might need.

How Resources Work

MCP Client pmcp Your Code
│ │ │
├─ resources/list ────────►│ │
│ ├─ ListResourcesRequest ──►│
│ │◄─ ResourceListResponse ──┤
│◄─ resource list ─────────┤ │
│ │ │
├─ resources/read(uri) ───►│ │
│ ├─ ReadResourceRequest ───►│
│ │◄─ ReadResourceResponse ──┤
│◄─ resource contents ─────┤ │

Python

Static Resources

Register a resource with a fixed URI:

from protomcp import resource, ResourceContent
@resource(uri="config://app", description="Application configuration")
def app_config(uri: str) -> ResourceContent:
return ResourceContent(
uri=uri,
text='{"debug": false, "log_level": "info"}',
mime_type="application/json",
)

The handler receives the requested URI and returns a ResourceContent with the data. You can return text or binary data:

@resource(uri="logo://main", description="Company logo", mime_type="image/png")
def logo(uri: str) -> ResourceContent:
with open("logo.png", "rb") as f:
return ResourceContent(uri=uri, blob=f.read(), mime_type="image/png")

Resource Templates

Templates use URI patterns with placeholders. The client can request any URI matching the pattern:

from protomcp import resource_template, ResourceContent
@resource_template(
uri_template="db://users/{user_id}",
description="Read a user record by ID",
mime_type="application/json",
)
def read_user(uri: str) -> ResourceContent:
user_id = uri.replace("db://users/", "")
user = db.get_user(user_id)
return ResourceContent(uri=uri, text=json.dumps(user))

Returning Multiple Contents

A handler can return a list of ResourceContent for multi-part responses:

@resource(uri="metrics://dashboard", description="Dashboard metrics")
def dashboard(uri: str) -> list[ResourceContent]:
return [
ResourceContent(uri="metrics://dashboard/cpu", text="72%"),
ResourceContent(uri="metrics://dashboard/memory", text="4.2GB/8GB"),
]

TypeScript

import { resource, resourceTemplate } from 'protomcp';
resource({
uri: 'config://app',
description: 'Application configuration',
handler: (uri) => ({
uri,
text: '{"debug": false}',
mimeType: 'application/json',
}),
});
resourceTemplate({
uriTemplate: 'db://users/{user_id}',
description: 'Read a user record by ID',
handler: (uri) => {
const userId = uri.replace('db://users/', '');
return { uri, text: JSON.stringify(getUser(userId)) };
},
});

Go

protomcp.RegisterResource(protomcp.ResourceDef{
URI: "config://app",
Name: "app_config",
Description: "Application configuration",
MimeType: "application/json",
HandlerFn: func() []protomcp.ResourceContent {
return []protomcp.ResourceContent{{
URI: "config://app",
Text: `{"debug": false}`,
}}
},
})
protomcp.RegisterResourceTemplate(protomcp.ResourceTemplateDef{
URITemplate: "db://users/{user_id}",
Name: "read_user",
Description: "Read a user record by ID",
HandlerFn: func(uri string) []protomcp.ResourceContent {
userID := strings.TrimPrefix(uri, "db://users/")
return []protomcp.ResourceContent{{URI: uri, Text: getUser(userID)}}
},
})

Combining with Tools

Resources and tools complement each other. A common pattern: expose data via resources, provide actions via tools.

NOTES = {}
@resource_template(uri_template="notes://{id}", description="Read a note")
def read_note(uri: str) -> ResourceContent:
note_id = uri.replace("notes://", "")
return ResourceContent(uri=uri, text=NOTES.get(note_id, "Not found"))
@tool("Create a new note")
def create_note(id: str, content: str) -> ToolResult:
NOTES[id] = content
return ToolResult(result=f"Created note {id}")