Handmade Claude Code 5/7 — MCP

Part five of the Handmade Claude Code campaign: the agent plugs into the world. The Model Context Protocol is how tools that live in other processes — a database, a browser, a ticket tracker — offer themselves to any agent that speaks it. This part makes yours speak it: start the servers, shake hands, put their tools in front of the model, call them, survive their failures, use their prompts and resources, and shut them down cleanly.

This session continues the agent you built in parts one to four, in the same repository. The MCP client is yours too — JSON-RPC over stdio is small enough to write, and writing it is the point. An MCP SDK is out of contract.

Servers

.mcp.json  (in the working directory)
{"mcpServers": {"<name>": {"command": "sh", "args": ["srv.sh"], "env": {"K": "V"}}}}

At launch, before the first model call, every server is started as a child process — command with args, the working directory as its directory, env added to its environment — and spoken to over its stdin and stdout in JSON-RPC 2.0, one JSON object per line.

The handshake: an initialize request (with protocolVersion, capabilities and clientInfo), then the notifications/initialized notification, then tools/list. A server that fails to start, or fails the handshake, is reported on stderr by name and skipped; the run goes on without it.

Tools

Every tool a server lists is advertised to the model as mcp__<server>__<tool>, with the server's description and its inputSchema as the input_schema. A call becomes tools/call with name (the server's tool name) and arguments (the model's input). The result's content text blocks, joined, are the tool result content; a result with isError: true, or a JSON-RPC error reply, is an error result with the message as content. The loop goes on.

Prompts

A server that lists prompts (prompts/list) contributes slash commands: /mcp__<server>__<prompt> <args> calls prompts/get with the arguments mapped positionally onto the prompt's declared arguments, and the messages it returns become the user message (their text, joined).

Resources

A token @<server>:<uri> in the prompt reads a resource: resources/read with that uri, and the text of the returned contents travels with the prompt like a mentioned file.

Shutdown

When the run ends, every server's stdin is closed; a server still alive a moment later is killed. No orphans.

The judges

The quality panel — architecture, performance, code quality, test quality, technical governance and DX review — sits on the rungs where its subject is decided, one verdict per judge per rung, on top of the rung's own points. There is no closing review: what you build is judged as you build it, and a rung you never reach is a verdict you never get.

The ladder

  • Set up and carry parts one to four forward (10)
  • The handshake (20)
  • Tools are advertised (30)
  • A tool call goes through (30)
  • Errors come back as results (20)
  • A server that will not start (20)
  • Prompts are commands (20)
  • Resources are mentions (20)
  • Environment and shutdown (20)
Sessions

0

Visibility

Public

Category

Reinvent the Wheel

Slug

handmade-claude-code-5-mcp

Duration

30 min

Judge reviews

~12 per session

Active session

No

Points

10–30

Tags
  • ai-agent
  • harness
  • mcp
  • handmade-claude-code
  • campaign
  • 1

    Set up and carry parts one to four forward

    10

    pts / check

    +10 pts per passing check · +10 for completing the task

    This part continues your agent. Same repository, same command, same
    model protocol, same tools, context and skills — what grows is the set
    of tools that live in other processes, spoken to over the Model Context
    Protocol.

    agent: started as: -C

    [--yes] -p ""

    Your AGENTS.md (or README.md) must still carry the two lines the
    platform captures into session memory: agent: and test:. If you are
    starting in an empty folder, your earlier work is fetched for you —
    check that the lines are there and that the agent still builds and
    runs.

    The first rung re-checks what the earlier parts earned: a bash round
    trip with the skill tool advertised. Everything after it is MCP work,
    against a scripted server the checks write for you.

    The MCP client is yours; an MCP SDK is out of contract.

    Wrapping claude, codex, gemini, aider or any other coding agent,
    or building on an agent SDK that owns the loop, is not building one.

    Judged by
  • 2

    The handshake

    20

    pts / check

    +20 pts per passing check · +10 for completing the task

    Before the first model call, every server in .mcp.json is started and
    greeted: initialize (with protocolVersion, capabilities,
    clientInfo), then the notifications/initialized notification, then
    tools/list — in that order, as JSON-RPC 2.0 objects, one per line, on
    the server's stdin. The scripted server logs everything it receives.

    Judged by
  • 3

    Tools are advertised

    30

    pts / check

    +30 pts per passing check · +10 for completing the task

    What a server lists, the model sees. Every tool from tools/list is
    advertised as mcp__<server>__<tool> with the server's description
    and its inputSchema as the input_schema — next to your own six
    tools, in every request.

    Judged by
  • 4

    A tool call goes through

    30

    pts / check

    +30 pts per passing check · +10 for completing the task

    The model calls mcp__fake__echo; the agent sends tools/call with
    name: "echo" and the model's input as arguments, waits for the
    reply, and hands the text of its content back as the tool result.

    Judged by
  • 5

    Errors come back as results

    20

    pts / check

    +20 pts per passing check · +10 for completing the task

    Servers fail in two ways and the model must hear about both. A result
    with isError: true is an error result whose content is the result's
    text. A JSON-RPC error reply is an error result whose content is the
    error's message. Neither stops the loop.

  • 6

    A server that will not start

    20

    pts / check

    +20 pts per passing check · +10 for completing the task

    One bad entry in .mcp.json must not take the run down. A server whose
    command cannot be started is reported on stderr — by name — and
    skipped; the other servers are greeted as usual and the model still
    gets its answer.

  • 7

    Prompts are commands

    20

    pts / check

    +20 pts per passing check · +10 for completing the task

    A server that lists prompts contributes slash commands.
    /mcp__<server>__<prompt> <args> calls prompts/get with the arguments
    mapped positionally onto the prompt's declared arguments, and the
    messages it returns become the user message the model receives.

    Judged by
  • 8

    Resources are mentions

    20

    pts / check

    +20 pts per passing check · +10 for completing the task

    @<server>:<uri> in the prompt reads a resource: resources/read with
    that uri, and the text of what comes back travels with the prompt
    like a mentioned file.

    Judged by
  • 9

    Environment and shutdown

    20

    pts / check

    +20 pts per passing check · +10 for completing the task

    A server gets the env its entry declares, on top of the agent's own
    environment. And when the run ends, no server outlives it: its stdin is
    closed, and a server still alive a moment later is killed.