Lesson 04 Your First MCP Server
Theme:
Lesson 04 · Slide 1

Lesson 04 — Your First MCP Server

From an empty server to a callable tool, in one sitting.

MCP Servers Module 2

Lesson 04 · Slide 2

One tool: pkb.echo

By the end of this lesson the PKB server exposes a single tool — pkb.echo(message: str) -> str — that returns the message right back to the caller. Both the MCP Inspector and Claude Desktop can call it, and you'll watch the round-trip from both sides.

It's a deliberately tiny tool. The point isn't what echo does; the point is that you wire up everything around it — project, entry point, client config — without any moving parts you can't see.

Lesson 04 · Slide 3

The shape of an MCP tool

Every tool the protocol exposes carries the same four pieces of metadata:

This is exactly what the tools/list response from Lesson 02 returned — one entry per tool, with those four fields.

Lesson 04 · Slide 4

FastMCP for ergonomic tools

@mcp.tool()
def echo(message: str) -> str:
    """Echo the message back."""
    return message

FastMCP introspects the function's type hints and docstring to build the schema. The parameter name and type become the inputSchema; the docstring becomes the tool description; the function name becomes the tool name. Four metadata fields, derived from four pieces of Python you'd be writing anyway.

Lesson 04 · Slide 5

Wiring FastMCP into pkb/server.py

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("pkb", version="0.1.0")

@mcp.tool()
def echo(message: str) -> str:
    """Echo the message back."""
    return message

def main() -> None:
    mcp.run()

This replaces the placeholder server.py you scaffolded in Lesson 03 — same file, same main() entry point, but now there's an actual tool behind it.

Lesson 04 · Slide 6

Wiring up Claude Desktop config

{
  "mcpServers": {
    "pkb": { "command": "pkb" }
  }
}

The pkb binary came from [project.scripts] in pyproject.toml (set up in Plan A Task 4) — pip install -e . drops it onto your PATH. Restart Claude Desktop after editing the config and PKB shows up in the tools panel, ready to call.

Lesson 04 · Slide 7

Calling the tool from Claude Desktop

[Screenshot placeholder]

Claude Desktop showing the pkb.echo tool call expanded, with arguments {"message": "hello world"} and the returned text block.

Lesson 04 · Slide 8

Same tool through MCP Inspector

mcp dev pkb/server.py
Lesson 04 · Slide 9

What just happened on the wire

Recap the Lesson 02 lifecycle for this one call:

  1. The client connected over stdio and sent initialize; the server replied with its capabilities.
  2. The client called tools/list — the response included an entry for echo with its schema.
  3. The client called tools/call with {name: "echo", arguments: {message: "ping"}}.
  4. The server returned a content block: [{type: "text", text: "ping"}].

Same JSON-RPC envelopes you traced by hand in Lesson 02 — just produced by your code instead of read from a transcript.

Lesson 04 · Slide 10

Error handling preview

Heads-up

If you raise from inside the tool function, FastMCP wraps the exception in a JSON-RPC error response — the client sees a structured failure, not a crashed server. We'll dig into schema validation and structured errors properly in Lesson 05.

Lesson 04 · Slide 11

Common gotchas

Lesson 04 · Slide 12

Recap & what's next

Coming up: Lesson 05 — proper schemas, structured errors, idempotency.
1 / ?