The capstone: two servers, one client, live

Sixteen lessons of parts. This lesson assembles them — and every frame below is captured from the assembled system running, because a capstone you only read about is a diagram with ambitions.

The composition

One client, two servers: ours (Paper Trail, the course's own hundred lines) and one we did not write — Anthropic's filesystem server, straight from the registry, spawned via npx, scoped to a single directory:

python
config = {
    "mcpServers": {
        "paper-trail": {
            "command": "uv",
            "args": ["run", "--with", "fastmcp", "python", "paper_trail.py"],
        },
        "files": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-filesystem", "./safe-root"],
        },
    }
}

async with Client(config) as c:
    tools = await c.list_tools()

That config block is lesson 12's vocabulary, twice. Run it, and discovery across both sessions — captured:

text
16 tools from 2 servers:
  paper-trail_order_status
  paper-trail_draft_refund
  files_read_text_file
  files_write_file
  files_edit_file
  files_search_files
  files_list_allowed_directories
  … (14 files_* tools in all)
cross-server read: Reviews W31: packaging praised, shipping slow.
our server still: {"customer":"Meera S","title":"The Covenant of Water",
                   "status":"shipped","carrier":"Delhivery","eta":"2026-08-05"}

Read that output slowly, because half the course is standing in it.

The namespacing is the collision defence from lesson 13, running. Every tool arrives prefixed by its server's config name — paper-trail_order_status, files_read_text_file — so two servers exposing a search could coexist, and a malicious server cannot silently shadow a trusted tool's name. The prefix is the client's doing, not the servers': neither server knows the other exists.

The ./safe-root argument is lesson 14's roots idea, as an argument. The filesystem server takes its allowed directories at spawn time and refuses everything outside them — it even ships a list_allowed_directories tool so the model can ask where the walls are. Honest-server boundary enforcement, exactly as billed: it protects against overreach, and a hostile filesystem server would need lesson 15's container instead.

And count the tools: 14 of 16 now come from code we did not write. One config block took this system from "two verbs I authored and read" to "sixteen verbs, mostly a stranger's". That is the composition threat from lesson 13 as arithmetic — the unit of exposure was never the server, it was the set — and it is also the value proposition, in the same number. M+N cuts both ways, which by now is the course's oldest refrain.

Making it a research desk

The full build from here is deliberate assembly, no new machinery. Put the two-pass client from lesson 10 on top, so a model orchestrates those sixteen tools behind your approval gate. Give Paper Trail two more tools — save_note(text, tags) and search_notes(query) over any small store — and the model can retain findings across a session: retrieval as composable tools the model reaches for, not a pipeline bolted on. Swap the filesystem server for a web-search server from the registry and the desk reads the live web instead of a folder — one config block again, zero client changes.

Then govern it like you mean it, because this configuration assembles the lethal trifecta on purpose: private order data (Paper Trail), untrusted content (whatever the second server fetches), and write-capable verbs. The full checklist you now own: descriptions read in the Inspector before first connect (lesson 11), write-verbs on manual approval forever (lesson 14), the third-party server version-pinned — npx -y package@1.2.3, never floating (lesson 14) — and containerised with the lesson 15 flags if it touches anything you care about, session logs you can replay (lesson 14), and the remote variant behind real identity (lesson 16). A trifecta you can name, gate, and audit is the one that does not surprise you.

What the exercise proves

Not that you can follow a config format. That capability is compositional now: systems are assembled from servers the way pages are assembled from packages — some yours, most not, each a trust decision — and every discipline in this course exists because that sentence is both the product and the threat model.

The capstone running: one client, two servers, sixteen namespaced tools — composition as value and exposure at once
The capstone running: one client, two servers, sixteen namespaced tools — composition as value and exposure at once

Try this: run the composition above exactly as shown — it works verbatim, safe-root and all. Then add a third server from the registry and watch the tool list grow without one line of client code changing. When that stops feeling remarkable, you have internalised MCP; the next lesson is about where it goes from here.

← Previous