MCP Architecture
A deep dive into how the Model Context Protocol is structured — from transport layers and message formats to capability negotiation and security.
Transport Layers
MCP supports two primary transport mechanisms for communication between clients and servers:
1. Standard I/O (stdio)
The most common transport for local MCP servers. The host spawns the server as a child process and communicates via stdin/stdout.
// How stdio transport works: Host Process (Claude Desktop) ↓ spawns Server Process (e.g., mcp-server-filesystem) ↓ stdin ← Client sends JSON-RPC requests stdout → Server sends JSON-RPC responses stderr → Server logs (not part of protocol) // Advantages: - Simple setup, no network configuration - Secure (no open ports) - Works offline - Low latency
2. HTTP with Server-Sent Events (SSE)
Used for remote MCP servers that need to be accessed over a network. The client connects via HTTP and receives real-time updates through SSE.
// How HTTP/SSE transport works: Client → POST /message → Server (sends JSON-RPC requests via HTTP POST) Client ← GET /sse ← Server (receives JSON-RPC responses via SSE stream) // Advantages: - Remote access across networks - Supports authentication (headers, tokens) - Can be load-balanced - Firewall-friendly (standard HTTP)
Message Format: JSON-RPC 2.0
All MCP communication uses the JSON-RPC 2.0 protocol. This is a lightweight remote procedure call format encoded as JSON.
// Client sends a request to call a tool: { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "read_file", "arguments": { "path": "/home/user/document.txt" } } }
// Server responds with the result: { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "Contents of the document..." } ] } }
Message Types
| Type | Direction | Description |
|---|---|---|
request |
Client → Server | Invoke a method and expect a response (has id) |
response |
Server → Client | Result or error for a request (matches id) |
notification |
Either direction | One-way message, no response expected (no id) |
Server Capabilities
MCP servers declare their capabilities during initialization. There are three primary capability types:
Tools
Functions the AI can call to perform actions. Examples: read_file, run_query, create_issue. Tools have defined input schemas (JSON Schema) and return structured results.
Resources
Data the AI can read, identified by URIs. Examples: file:///path/to/doc.txt, db://users/123. Resources support subscriptions for change notifications.
Prompts
Reusable prompt templates with arguments. Examples: "Summarize this document", "Review this code". Prompts can generate multi-message conversations.
Connection Lifecycle
Every MCP connection follows a defined lifecycle:
-
Initialization
The client sends an
initializerequest with its protocol version and capabilities. The server responds with its own capabilities. -
Capability Exchange
Both sides negotiate what they support. The client learns which tools, resources, and prompts are available. The server learns what the client can handle (e.g., sampling, roots).
-
Initialized Notification
The client sends an
initializednotification to confirm the connection is ready. Normal operations can now begin. -
Normal Operation
The client can now call tools, read resources, and use prompts. The server can send notifications (e.g., resource changed). This phase continues until the connection closes.
-
Shutdown
Either side can close the connection. For stdio, this means terminating the process. For HTTP/SSE, the client closes the SSE connection.
// Step 1: Client sends initialize request { "jsonrpc": "2.0", "id": 0, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": { "sampling": {} }, "clientInfo": { "name": "claude-desktop", "version": "1.0.0" } } } // Step 2: Server responds with capabilities { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {}, "resources": { "subscribe": true } }, "serverInfo": { "name": "filesystem-server", "version": "0.5.0" } } } // Step 3: Client confirms initialization { "jsonrpc": "2.0", "method": "notifications/initialized" }
Protocol Versioning
MCP uses date-based versioning (e.g., 2024-11-05). During initialization, the client and server negotiate a protocol version they both support. This ensures backward compatibility as the protocol evolves.
Security Model
MCP includes several security mechanisms:
- Capability declaration: Servers explicitly declare what they can do; clients decide what to allow.
- User consent: Hosts should prompt users before allowing tool execution, especially for write operations.
- Sandboxing: Servers should operate with minimal permissions — only access what they need.
- Transport security: HTTP/SSE transport should use TLS (HTTPS). Stdio is inherently local.
- Authentication: HTTP/SSE servers can require API keys, OAuth tokens, or other authentication headers.
Data Flow Diagram
// Complete MCP data flow for a tool call: 1. User asks: "What files are in my project?" ↓ 2. AI Model decides to use the list_directory tool ↓ 3. Host sends tool call request to MCP Client ↓ 4. MCP Client sends tools/call JSON-RPC to MCP Server ↓ 5. MCP Server reads the file system ↓ 6. MCP Server returns file list as JSON-RPC response ↓ 7. MCP Client passes result back to Host ↓ 8. AI Model incorporates file list into its response ↓ 9. User sees: "Your project contains: src/, README.md, ..."
What's Next?
Now that you understand the architecture, the next lesson walks you through building an MCP server from scratch using TypeScript and Python SDKs.
Lilly Tech Systems