The Exchange · 7/2026

Model Context Protocol

Plugging knowledge and capabilities into AI agents.

David Cai · github.com/davidcai/kungfu-mcp

The Matrix · 1999

Neo is smart and has great potential, but he lacks the capability to defend himself in the dangerous world of Matrix.

An AI agent is smart, but alone is not enough.
It needs domain knowledge and capabilities.

In this analogy, Neo is the AI agent
— then, what will be the MCP?

What is MCP

MCP is a protocol.

Model Context Protocol — an open standard for connecting AI applications to external tools and data. Open-sourced by Anthropic, Nov 2024; now adopted across Claude, OpenAI, Google DeepMind, IDEs, and inspector tooling.

Speaks JSON-RPC 2.0

Over a transport — stdio for local, HTTP for remote. Anything speaking the protocol interoperates.

Spec + SDKs

TypeScript, Python, Kotlin, C#, Java. Today's server is TypeScript.

modelcontextprotocol.io

Where MCP fits

A brain in a jar, until you give it hands.

An agent is a model calling tools in a loop. Without tools, an LLM is a well-read brain with frozen knowledge and no hands. MCP is the standardized socket where tools and data plug into that loop.

LLM decides calls tool gets result reasons repeats answers

model = brain · MCP tools = hands · MCP resources = reference library

The Matrix · 1999

“I know Kung Fu.”

In The Matrix, an operator plugs a cable into Neo's brain — Kung Fu downloaded directly into his mind. He opens his eyes, fully trained.

What is MCP?

The AI agent is Neo

Smart, but isolated in a box — no hands, no tools, no world to act in.

MCP is the Cable

An open standard connecting AI models to external tools, software, and databases.

The Server is the Operator

A standardized translator linking the AI to browsers, calendars, and files.

MCP SERVER

One server, many hosts.

The kungfu-mcp server (our demo) catalogs 8 kung fu factions, narrates sparring matches, and ships an interactive arena UI. This one MCP server can serve many hosts.

Host

Claude · Cursor · MCPInspector

Anything that speaks MCP

MCPHTTP

Server

kungfu-mcp

Hono + Streamable HTTP · :3001/mcp · tools · resources · app

The economic argument

M×N becomes M+N.

Before

M agents × N integrations.

Every app writes its own Slack, GitHub, and database connector. M×N custom adapters to build, test, and maintain forever.

After

Each side implements once.

M + N. One port, like USB — the laptop doesn't care if you plug in a monitor, a drive, or a keyboard.

Same server, unchanged, in Claude, the Inspector, a custom host, your internal agent.

Protocol anatomy

Three primitives, one extension.

PrimitiveWho decides to use itIn kungfu-mcp
ToolsThe model — function callinglist_factions, get_faction, spar, spar_arena
ResourcesThe application / user — attached as contextkungfu://kungfu/roster · kungfu://factions/{id}
PromptsThe user — slash-command templates— not used here (mention only)
Apps (extension)Tool + ui:// resource → interactive iframespar_arena + ui://spar-arena/app.html

The control-plane distinction: tools are model-controlled, resources are application-driven. Tools and resources render the same markdown via a shared formatter — they differ only in who decides to fetch the content.

Primitive 01 · Tools

Tools are functions that a model can call.

server.registerTool(
  "list_factions",
  {
    description: "List all major kungfu factions known to the kung fu world…",
    inputSchema: {},
    outputSchema: {
      factions: z.array(z.object({
        id: z.string(), name: z.string(), catchphrase: z.string(),
      })),
    },
  },
  async () => ({
    content: [{ type: "text", text: rosterMarkdown() }],
    structuredContent: { factions: /* typed data */ },
  }),
);

Three parts: a name, a schema contract (Zod → JSON Schema over the wire), and a handler. The description is the API documentation the model reads to decide when to call. Bad descriptions = tools that never get called.

Primitive 02 · Resources

A resource is information addressable by URI.

const factionTemplate = new ResourceTemplate("kungfu://factions/{id}", {
  list: async () => ({
    resources: KUNGFU_FACTIONS.map((f) => ({
      uri: `kungfu://factions/${f.id}`,
      name: `${f.name} profile`,
      mimeType: "text/markdown",
    })),
  }),
  complete: { id: () => KUNGFU_FACTIONS.map((f) => f.id) },
});

Static resource: kungfu://kungfu/roster · Template: kungfu://factions/{id} — enumerable via list() and autocompletable via complete.id(). The URI is the contract: rename it and you break every client that references it.

Live demo

MCP Inspector — tools & resources.

Host #1 · http://localhost:3001/mcp

01

list_factions

Markdown roster + structuredContent JSON in one result.

02

get_faction shaolin

Techniques with threat scores. Try a bogus id — error lists valid ids.

03

spar shaolin vs wudang

3 rounds, random picks, threat-score verdict. Run twice — different each time.

04

browse resources

Read roster; browse factions/{id} with id autocomplete.

Land the line: "The Inspector is host #1. Same server, zero changes, meets host #2 in ten minutes." Full script: Appendix A, Demo 1.

The payoff

Neo can do kung fu now.

Resources extend what the agent knows — 8 Kung Fu factions, and signature techniques. Tools extend what it can do — run a real sparring match with computed scoring.

Design pattern

One call. Two consumers.

return {
  content: [{ type: "text",
    text: sparNarration(a, b, nameA, nameB, outcome) }],
  structuredContent: outcome,  // { rounds, verdict, winnerId }
};

content → prose

For the LLM to read.

"The crowd gasps. A chicken flees." — what the model summarizes to the user.

structuredContent → JSON

For machines to render.

{ rounds, verdict, winnerId } — what the arena UI animates. No prose-parsing, no drift.

Beyond text · MCP Apps

A UI is just a resource.

registerAppTool(server, "spar_arena", {
  title: "Kung Fu Spar Arena",
  inputSchema: {},
  _meta: { ui: { resourceUri: "ui://spar-arena/app.html" } },  // ← the hook
}, async () => ({
  content: [{ type: "text", text: "Arena ready. Select two factions…" }],
}));

One line. Point the host at a ui:// resource; it fetches the HTML, renders it in a sandboxed iframe, and your tool's answer comes alive. The bundle is one self-contained Vite file — one fetch, no asset pipeline for the host.

Beyond text · MCP Apps

The iframe is an MCP client too.

HOST · Cursor / Claude / Inspector
iframe (sandboxed) ← fetches ui://spar-arena/app.html
React UI · useApp()
app.callServerTool("spar", { … })
postMessage · window.parent
MCP SERVER · Hono + HTTP transport
spar_arena list_factions get_faction spar
ui:// resource → returns the bundled HTML string

Same tools, same server. The LLM called them via content; the iframe calls them via structuredContent — one source of truth, two consumers.

Live demo

Neo vs Morpheus.

Host (e.g., Cursor, Claude Desktop, or MCP Inspector) → call spar_arena → the arena renders inline. Pick factions, Begin the Spar, watch rounds animate from structuredContent. View Profile fires a live get_faction call.

What to watch for: inline iframe render · dropdowns filled via list_factions · animated rounds · winner's catchphrase

How to build · Anatomy

The whole server, on one slide.

const transport = new WebStandardStreamableHTTPServerTransport({
  sessionIdGenerator: () => newSessionId,
});
const server = new McpServer({ name: "kungfu-mcp", version: "1.0.0" });
registerTools(server);            // src/tools.ts — 4 tools
registerDataResources(server);    // src/resources.ts — roster + profiles
registerApp(server, bundledHtml);   // src/app.ts — arena tool + ui:// resource
await server.connect(transport);

Hono + hono/cors (must expose the mcp-session-id header for browser hosts). One McpServer + transport per session, tracked in a Map. File-per-primitive layout: tools.ts · resources.ts · app.ts · shared data.ts + format.ts.

How to build · Recipe

Six steps to your own server.

01

Install the SDK

npm i @modelcontextprotocol/sdk zod — TS, Python, Kotlin, C#, Java.

02

Define tools

Name + description + schema + handler via server.registerTool.

03

Add resources

Context users and apps attach — server.registerResource.

04

Pick a transport

stdio for local CLIs · Streamable HTTP for remote or app-capable.

05

Verify with Inspector

Before touching any real host — schemas, results, autocomplete.

06

Optional: bundle a UI

Register a ui:// resource — @modelcontextprotocol/ext-apps.

Reference implementation: github.com/davidcai/kungfu-mcp — npm install && npm start gives you everything shown today.

Takeaways

MCP is USB for agents

Resources are knowledge, tools are capability, apps are living UI.

github.com/davidcai/kungfu-mcp

1 / 19
← / → · scroll · N = notes