Lesson 06 Resources & Prompts
Theme:

Lab 06 — Add a resource and a prompt to PKB

Lesson 06 · MCP Servers · ~25 min hands-on

By the end of this lab the PKB server exposes two new entry points beyond the search tool you built in Lesson 05: a static resource at pkb://about, a dynamic resource template at pkb://notes/{title}, and a welcome prompt. All three should be visible from the MCP Inspector's Resources and Prompts tabs without any extra wiring beyond FastMCP decorators.

Prereqs

Step 1 — Create pkb/resources.py

Resources live in their own module so the wiring in server.py stays small. Create the file alongside tools.py with the following content:

"""Resources exposed by PKB."""

from mcp.shared.exceptions import McpError
from mcp.types import ErrorData, METHOD_NOT_FOUND

from pkb.tools import NOTES


def get_note(title: str) -> str:
    """Return the body of one note by title. URI: pkb://notes/{title}."""
    if title not in NOTES:
        raise McpError(ErrorData(
            code=METHOD_NOT_FOUND,
            message=f"unknown note: {title}",
        ))
    return NOTES[title]


def about() -> str:
    """Static resource describing the PKB server. URI: pkb://about."""
    return (
        "PKB — Personal Knowledge Base MCP server (course sample).\n"
        "Exposes notes via pkb://notes/{title} and tools for searching them."
    )

What this does, line by line:

Step 2 — Create pkb/prompts.py

Prompts are even simpler — a function that returns the rendered string. FastMCP handles the rest.

"""Prompts exposed by PKB."""


def welcome(name: str = "friend") -> str:
    """Render the welcome prompt."""
    return (
        f"Hi {name}! I'm your PKB assistant. "
        "Want me to summarize today's notes or search for something specific?"
    )

The default name="friend" means the prompt can be invoked with no arguments and still render. In Inspector the argument will appear with an empty input box and a placeholder hint.

Step 3 — Wire server.py

Add the new imports near the top of pkb/server.py, then register the resource and prompt handlers with FastMCP decorators. The additions look like this:

from pkb import resources, prompts


@mcp.resource("pkb://about")
def about_resource() -> str:
    return resources.about()


@mcp.resource("pkb://notes/{title}")
def note_resource(title: str) -> str:
    return resources.get_note(title)


@mcp.prompt()
def welcome(name: str = "friend") -> str:
    return prompts.welcome(name)

The decorators do the registration: @mcp.resource("pkb://...") attaches the function as a resource handler with that URI (static or template, depending on whether the URI contains {placeholders}); @mcp.prompt() registers the function as a prompt named after the function itself. FastMCP introspects the signature to figure out the argument schema.

Step 4 — Reinstall (only if you added new files outside the package)

Both resources.py and prompts.py sit inside the existing pkb/ package, so the editable install picks them up automatically — no reinstall needed. If you ever add a new top-level module or change pyproject.toml, rerun:

pip install -e .

Step 5 — Run under Inspector

From the project root:

mcp dev pkb/server.py

The Inspector UI opens in your browser. Click the Resources tab. You should see two entries:

Step 6 — Read the about resource

In the Resources tab, click pkb://about, then click Read. The response pane shows the static text returned by pkb/resources.py:about:

PKB — Personal Knowledge Base MCP server (course sample).
Exposes notes via pkb://notes/{title} and tools for searching them.

Step 7 — Read a dynamic resource

Click pkb://notes/{title}. Fill in title=hello (or whatever key exists in your NOTES dict). Click Read. The response pane shows the note body.

Now try title=does-not-exist. You should see a structured error response with code METHOD_NOT_FOUND and the message unknown note: does-not-exist. This is the McpError you raised in resources.py being serialised over JSON-RPC.

Step 8 — Invoke the prompt

Click the Prompts tab. You should see one entry, welcome. Click it, fill in name=Tester (or leave it blank to use the default), and click Get Prompt. The rendered message appears:

Hi Tester! I'm your PKB assistant. Want me to summarize today's notes or search for something specific?

Step 9 — Confirm in Claude Desktop

Restart Claude Desktop (the PKB config from Lesson 04 is still in place — no edits needed). In a new chat, type:

read the pkb://about resource

Claude routes that through the resource read API on the PKB server and quotes the response back. Depending on the Claude Desktop build, the welcome prompt may also surface as a slash command (/welcome) in the prompt menu — try it if your build supports it.

What you should leave with

Troubleshooting

Common stumbles