CC-201d · Module 1
MCP Protocol Anatomy
4 min read
The Model Context Protocol is a JSON-RPC 2.0 interface between Claude and external services. Understanding the protocol is the difference between building MCP servers that work and building MCP servers that work reliably.
The protocol has three primitives: tools, resources, and prompts. Tools are functions Claude can invoke — they take parameters and return results. Resources are data Claude can read — files, database records, API responses. Prompts are pre-built templates that help Claude use the server effectively. Most developers only implement tools, which means they leave two-thirds of the protocol's power on the table.
The lifecycle is straightforward. Claude discovers your server at session start via the MCP configuration. It calls the server's initialize endpoint to negotiate capabilities. Then it calls list_tools, list_resources, and list_prompts to discover what the server offers. Those discovery responses are what consume context tokens — every tool description, every parameter schema, every resource template gets loaded into Claude's context window and stays there for the entire session.
This is why tool design matters as much as tool implementation. A server with 30 poorly-described tools will consume 15,000 tokens of context and confuse Claude about when to use which tool. A server with 8 well-described tools will consume 3,000 tokens and give Claude clear, unambiguous guidance. The protocol is simple. The design is where the craft lives.
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{ name: "my-tool-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler("tools/list", async () => ({
tools: [{
name: "lookup_customer",
description: "Look up a customer by email or account ID. Returns name, plan, MRR, and last activity date.",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Email address or account ID" }
},
required: ["query"]
}
}]
}));
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "lookup_customer") {
const result = await lookupCustomer(request.params.arguments.query);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);