Beginner

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.

stdio Transport
// 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.

HTTP/SSE Transport
// How HTTP/SSE transport works:

ClientPOST /messageServer
         (sends JSON-RPC requests via HTTP POST)

ClientGET /sseServer
         (receives JSON-RPC responses via SSE stream)

// Advantages:
- Remote access across networks
- Supports authentication (headers, tokens)
- Can be load-balanced
- Firewall-friendly (standard HTTP)
When to use which: Use stdio for local servers (filesystem, local databases). Use HTTP/SSE for shared servers, cloud deployments, or when multiple clients need to connect.

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.

JSON-RPC Request
// 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"
    }
  }
}
JSON-RPC Response
// 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:

  1. Initialization

    The client sends an initialize request with its protocol version and capabilities. The server responds with its own capabilities.

  2. 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).

  3. Initialized Notification

    The client sends an initialized notification to confirm the connection is ready. Normal operations can now begin.

  4. 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.

  5. Shutdown

    Either side can close the connection. For stdio, this means terminating the process. For HTTP/SSE, the client closes the SSE connection.

Initialization Handshake
// 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.

📚
Version Negotiation: The client proposes its latest supported version. The server can accept it or respond with an older version it supports. If no common version exists, the connection fails gracefully.

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.
Security Principle: Never grant an MCP server more access than it needs. A filesystem server should only access specified directories, not the entire disk. A database server should use read-only credentials when possible.

Data Flow Diagram

Complete Data Flow
// 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 Client4. MCP Client sends tools/call JSON-RPC to MCP Server5. MCP Server reads the file system
           ↓
6. MCP Server returns file list as JSON-RPC response
           ↓
7. MCP Client passes result back to Host8. 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.