Tools, Resources, and Prompts

Combining Capabilities

4 min read

Each capability on its own is straightforward. The design work starts when one server offers all three and you have to decide which job goes where.

A Complete Example: Knowledge Base Server

Here is the same knowledge base exposed three ways at once. Notice that each capability answers a different question about the same documents:

One knowledge base, three surfaces

Resources — "let me read that"
Tools — "go find it" / "change it"
Prompts — "do the usual thing"
Storage

The pairing that makes this work is search_knowledge and doc://{id}. Resources are addressable but not discoverable by the model — it has no way to guess a document ID. The search tool exists specifically to turn a question into the URIs the resource layer can serve. Ship one without the other and half the server is unreachable.

from mcp.server import MCPServer

mcp = MCPServer(name="knowledge-base")


# 1. RESOURCES — read-only views, addressed by URI
@mcp.resource("doc://{doc_id}", mime_type="text/markdown")
async def document(doc_id: str) -> str:
    """A single knowledge-base document."""
    doc = await db.get_document(doc_id)
    return doc.content


# 2. TOOLS — actions, including the ones with side effects
@mcp.tool()
async def search_knowledge(query: str, limit: int = 5) -> str:
    """Search the knowledge base and return the best matching documents."""
    return await kb.search(query, limit=limit)


@mcp.tool()
async def add_document(title: str, content: str, tags: list[str] | None = None) -> str:
    """Add a new document to the knowledge base."""
    doc_id = await db.add_document(title, content, tags or [])
    return f"Created doc://{doc_id}"


# 3. PROMPTS — standardised interactions the user picks from a menu
@mcp.prompt()
def summarize_topic(topic: str) -> str:
    """Summarise everything the knowledge base knows about a topic."""
    return f"Search the knowledge base for '{topic}', then summarise what you find. Cite each doc:// URI you used."

The three capabilities are doing three different jobs here, and the split is the whole design. doc://{doc_id} is a template, so the server exposes every document without listing any of them. add_document is a tool rather than a writable resource because it has a side effect. summarize_topic is a prompt because the user chooses it deliberately — prompts are user-initiated, which is exactly what tools are not.

Workflow: AI Using All Capabilities

Trace one real request end to end. The order is not arbitrary — search has to come first, because nothing else knows which documents exist:

"Summarise our AI security policies"

User picks the prompt

summarize_topic with topic="AI security". Optional — a user who knows what they want can skip straight to asking

search_knowledge

Tool call. Returns doc://policy-123 and doc://policy-456. This is the discovery step — without it the model has no IDs to read

read doc://policy-123

Resource read. Full text, no arguments, no side effects

read doc://policy-456

Same again. Reads are cheap and independent, so they can run together

Summarise

No server involvement. The model works from text it actually retrieved rather than from memory

Steps two and three are the ones worth internalising. Search returns identifiers, not content; the resource layer turns identifiers into content. Collapsing those into a single tool that returns full documents seems tidier until a search matches thirty files and the context window is gone.

Resource + Tool Coordination

Design resources and tools to work together:

# Resource: Read-only view
Resource(uri="user://123", name="User 123 Profile")

# Tool: Modify user
Tool(name="update_user", description="Update user profile")

Best Practices

PracticeReason
Clear separationResources for reading, tools for actions
Consistent URIsMakes resources predictable
Related naminguser://123 with update_user tool
Document relationshipsExplain in descriptions

Now let's apply these patterns in a hands-on lab. :::

Quiz

Module 3 Quiz: Tools, Resources, and Prompts

Take Quiz
Was this lesson helpful?

Sign in to rate