Exposing Resources and Prompts, Not Just Tools, from an MCP Server
Tools are actions. Many agent workflows also need readable context and reusable templates.
Search across all documentation pages
Tools are actions. Many agent workflows also need readable context and reusable templates.
MCP models those as resources (URI-addressable data) and prompts (named message templates with arguments).
Keep your tools, then declare resources for files or API snapshots agents should read, and prompts for multi-step task templates hosts can insert. List, read, and get each primitive through the SDK; document URIs and prompt args as carefully as tool schemas.
scheme://path).# verify decorator APIs at build for your mcp package version
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("catalog-server")
DOCS = {
"intro": "# Product intro\nWelcome to the catalog API.",
"limits": "# Rate limits\n100 req/min per key.",
}
@mcp.resource("docs://{slug}")
def read_doc(slug: str) -> str:
"""Return a markdown doc by slug."""
return DOCS.get(slug, f"Unknown doc: {slug}")
@mcp.prompt()
def review_change(service: str, risk: str = "medium") -> str:
"""Template for reviewing a service change."""
return (
f"Review the proposed change for `{service}`.\n"
f"Risk level: {risk}.\n"
"List regressions, rollout steps, and monitors to watch."
)
@mcp.tool()
def list_doc_slugs() -> str:
"""List documentation slugs available as resources."""
return ", ".join(sorted(DOCS))
if __name__ == "__main__":
mcp.run(transport="stdio")// Patterns - verify registerResource / registerPrompt names at build
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({ name: "catalog-server", version: "1.0.0" });
server.registerTool(
"list_doc_slugs",
{ description: "List documentation slugs", inputSchema: {} },
async () => ({ content: [{ type: "text", text: "intro, limits" }] }),
);
// Resources and prompts: use the SDK helpers for your version, e.g.
// server.resource(...) / server.registerResource(...)
// server.prompt(...) / server.registerPrompt(...)
// Return URI, mimeType, and text/blob contents per schema.Prefer checking the current SDK reference for exact resource / prompt registration helpers - names evolve slightly across releases.
| Primitive | Primary question | Side effects | Typical consumer |
|---|---|---|---|
| Tool | "Do something" | Often yes | Model tool call |
| Resource | "Read this URI" | Should be read-only | Host/context attach |
| Prompt | "Start from this template" | No (template only) | Host slash-commands / UI |
Mixing them poorly creates confusion: a "read_file" tool that also mutates state, or a prompt that secretly calls APIs.
docs://, config://, ticket://).Some hosts auto-fetch resources; others only list them until a user or agent requests a read.
Some hosts surface prompts as slash commands; others ignore them.
Document your primitives for the hosts you officially support.
Pattern: prompt sets the task, resource supplies policy docs, tool performs the change.
That split keeps token use lower than stuffing everything into one mega-tool result.
| Approach | When it wins | When it loses |
|---|---|---|
| Tools only | Simple action APIs | Large context payloads |
| Tools + resources | Context on demand | Hosts without resource UI |
| Host-side RAG only | Centralized retrieval | Per-server data locality |
| Static files in repo | Offline coding agents | Live multi-tenant data |
No. Tools alone are common. Add resources and prompts when they remove friction for your hosts.
Yes when the SDK and host support resource references in tool results. That is a good pattern for large outputs.
Apply the same auth as tools on remote servers. A readable resource can leak customer data as easily as a tool.
The protocol does not give you a global cache. Hosts may cache; your server should treat each read as authoritative unless you document otherwise.
Short examples help. Long few-shots may belong in a resource the prompt points to.
Use an MCP client harness to call the prompts list/get methods and assert the rendered messages.
Prefer structured args. Free-form blobs recreate the problem prompts were meant to solve.
text/plain and text/markdown for LLM-facing docs; more specific types when hosts render non-text.
Related: Anatomy of an MCP Server: Handlers, Schemas, and Transport
Related: Building Your First MCP Server in TypeScript or Python
Related: Handling Large Tool Outputs: Auto-Spill to Sandbox Files
Related: MCP's Three Primitives: Tools, Resources, and Prompts
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026