MCP Protocol Deep Dive
Chapter 37: Deep Dive into the MCP Protocol
The Model Context Protocol (MCP) is an open protocol standard published by Anthropic in late 2024, designed to solve the fragmentation of AI system integration with external tools and data sources. Before MCP, every AI application needed custom integration code for every tool. MCP's arrival is analogous to what HTTP did for web communicationโone protocol to connect all tools. This chapter dissects MCP's design principles, architecture, and complete message specification.
37.1 Design Goals and Background
Why MCP?
Before MCP (fragmented):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AI App (Claude / GPT / Hermes) โ
โโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโค
โCustom โCustom โCustom โCustom โ ... โ
โSearch โDB โFile โAPI โ โ
โadapter โadapter โadapter โadapter โ โ
โโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโ
After MCP (unified):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AI App (Claude / GPT / Hermes) โ
โโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MCP Protocol (unified)
โโโโโโโโโโโโโโผโโโโโโโโโโโโโ
โ โ โ
โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ
โMCP Searchโ โMCP DB โ โMCP File โ
โServer โ โServer โ โServer โ
โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ
Three Core Design Goals
| Goal | Meaning | Analogy |
|---|---|---|
| Standardization | Unified tool-call format, eliminating fragmentation | HTTP for web requests |
| Security | Servers declare capabilities; clients request only what they need | OAuth least-privilege principle |
| Interoperability | Any MCP Client works with any MCP Server | USB standard |
Protocol Version History
| Version | Release | Key Changes |
|---|---|---|
| 0.1.0 | 2024-11 | Initial release, basic Tool/Resource support |
| 0.2.0 | 2024-12 | Added Prompt type, improved error handling |
| 1.0.0 | 2025-Q1 | Stable release, SSE Transport added |
| 1.1.0 | 2025-Q2 | OAuth auth, batch request support |
37.2 Client-Server Architecture
Core Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MCP Host (e.g., Hermes Agent) โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ MCP Client 1 โ โ MCP Client 2 โ โ
โ โ (one per server) โ โ (one per server) โ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ
โ MCP Protocol โ MCP Protocol
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ MCP Server 1 โ โ MCP Server 2 โ
โ (database tools) โ โ (filesystem tools) โ
โ โ โ โ
โ Tools: โ โ Tools: โ
โ - query_db โ โ - read_file โ
โ - write_db โ โ - write_file โ
โ โ โ โ
โ Resources: โ โ Resources: โ
โ - db://tables โ โ - file:///docs โ
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
Key Role Definitions
MCP Host:
- Creates and manages multiple MCP Client instances
- Coordinates LLM inference and tool invocation
- Manages the user-facing interface
MCP Client:
- Maintains a 1:1 connection with a single MCP Server
- Manages the protocol state machine
- Forwards tool-call requests from the Host
MCP Server:
- Provides specific Tools, Resources, or Prompts
- Runs as an isolated process (security boundary)
- Can use stdio, SSE, or HTTP transport
37.3 Transport Layer: Three Options
Transport 1: Stdio
The simplest optionโthe Client spawns the Server as a child process and communicates via stdin/stdout.
Client
|โโ spawn โโโ Server (child process)
|โโ stdin (JSON-RPC requests) โโโ Server
|โโ stdout (JSON-RPC responses) โโโ Server
stderr: Server logs (not part of protocol)
class StdioTransport:
def __init__(self, command: list):
import subprocess
self.process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
def send(self, message: dict):
import json
line = json.dumps(message) + "\n"
self.process.stdin.write(line.encode())
self.process.stdin.flush()
def receive(self) -> dict:
import json
line = self.process.stdout.readline()
return json.loads(line.decode())
Best for: Local development, CLI tool integration, process-level security isolation
Transport 2: SSE (Server-Sent Events)
The server pushes messages via HTTP SSE; the client sends requests via HTTP POST.
Client Server (HTTP service)
|โโ GET /sse โโโโโโโโโโโโโโ |
| (establish SSE stream) |
|โโ text/event-stream โโโโโ |
| |
|โโ POST /message โโโโโโโโโ |
| {"jsonrpc":"2.0",...} |
|โโ 200 OK โโโโโโโโโโโโโโโโ |
|โโ SSE: {"result":...} โโโ |
Best for: Remote Servers, web integration, server-initiated push events
Transport 3: Streamable HTTP (MCP 1.1+)
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","method":"tools/call",...}
---
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc":"2.0","result":...}
Transport Comparison
| Feature | Stdio | SSE | Streamable HTTP |
|---|---|---|---|
| Setup complexity | Simple (local process) | Medium (needs HTTP server) | Medium |
| Network traversal | No | Yes | Yes |
| Real-time push | No | Yes | Yes |
| State management | In-process | Session-based | Stateless |
| Security isolation | Process-level | Network-level | Network-level |
| Best for | Local tools | Remote services | Cloud APIs |
37.4 Three Capability Types: Tool, Resource, Prompt
Capability 1: Tool
A Tool is an executable functionโthe LLM decides when and how to call it.
{
"name": "search_database",
"description": "Search records in the database by keyword",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search keyword"
},
"table": {
"type": "string",
"enum": ["users", "products", "orders"]
},
"limit": {
"type": "integer",
"default": 10
}
},
"required": ["query", "table"]
}
}
Capability 2: Resource
A Resource is read-only data or content the Server can provideโthe LLM includes it in context.
{
"uri": "db://customers/recent",
"name": "Recent customers",
"description": "Customer records added in the past 30 days",
"mimeType": "application/json"
}
Resource URIs support template syntax:
{
"uriTemplate": "file:///documents/{category}/{filename}",
"name": "Document resource",
"description": "Access documents by category and filename"
}
Capability 3: Prompt
A Prompt is a predefined prompt template the Server offers to help the LLM use its capabilities optimally.
{
"name": "analyze_sales_data",
"description": "Optimal prompt for analyzing sales data and generating insight reports",
"arguments": [
{
"name": "time_period",
"description": "Analysis period (e.g., Q4 2024)",
"required": true
},
{
"name": "focus_area",
"description": "Focus area (e.g., region, product line)",
"required": false
}
]
}
Capability Comparison
| Type | Semantics | LLM Role | Side Effects |
|---|---|---|---|
| Tool | Executable function | Decides when/how to call | Yes (stateful) |
| Resource | Read-only data | Includes in context | No |
| Prompt | Prompt template | Gets optimized prompt text | No |
37.5 Complete Message Specification (JSON-RPC 2.0)
MCP is built on JSON-RPC 2.0. All messages are JSON objects.
JSON-RPC 2.0 Message Types
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "method_name",
"params": {}
}
// Success response
{
"jsonrpc": "2.0",
"id": 1,
"result": {}
}
// Error response
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid Request",
"data": {}
}
}
// Notification (no response expected โ no "id" field)
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": {}
}
Full Lifecycle Message Flow
Client Server
|โโ initialize โโโโโโโโโโโโโโโโโโโโโโโโ |
| {protocolVersion: "2024-11-05", |
| capabilities: {...}, |
| clientInfo: {name:"hermes",...}} |
| |
|โโ initialize result โโโโโโโโโโโโโโโโ |
| {protocolVersion: "2024-11-05", |
| capabilities: {tools:{}, |
| resources:{}, prompts:{}}, |
| serverInfo: {name:"db-server"}} |
| |
|โโ initialized (notification) โโโโโโโโ |
|โโ tools/list โโโโโโโโโโโโโโโโโโโโโโ โ |
|โโ tools/list result โโโโโโโโโโโโโโโโโ |
|โโ tools/call โโโโโโโโโโโโโโโโโโโโโโโ โ |
| {name:"search_database", |
| arguments:{query:"...",table:"users"}} |
|โโ tools/call result โโโโโโโโโโโโโโโโโ |
| {content:[{type:"text",text:"..."}],|
| isError: false} |
Complete Tool Call Request/Response
// Request
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "search_database",
"arguments": {
"query": "VIP customer",
"table": "users",
"limit": 5
}
}
}
// Success response
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [
{
"type": "text",
"text": "Found 3 VIP customer records:\n1. Alice (ID: 1001)\n2. Bob (ID: 1002)"
},
{
"type": "resource",
"resource": {
"uri": "db://users/vip-list",
"mimeType": "application/json",
"text": "[{\"id\":1001,\"name\":\"Alice\",...}]"
}
}
],
"isError": false
}
}
// Tool execution error (protocol succeeded, tool failed)
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [
{"type": "text", "text": "Database query failed: connection timeout. Please retry."}
],
"isError": true
}
}
Error Code Reference
Standard JSON-RPC error codes:
-32700 Parse error Invalid JSON received
-32600 Invalid Request Request not conforming to JSON-RPC spec
-32601 Method not found Method does not exist
-32602 Invalid params Invalid method parameters
-32603 Internal error Internal server error
MCP application-layer error codes:
-32001 Resource not found
-32002 Tool execution error
-32003 Unauthorized
-32004 Rate limit exceeded
37.6 MCP vs OpenAPI
| Dimension | MCP | OpenAPI |
|---|---|---|
| Design goal | AI-native tool invocation | RESTful API documentation |
| Transport | JSON-RPC 2.0 | HTTP REST |
| Schema format | JSON Schema (subset) | JSON Schema (full) |
| Capability types | Tool + Resource + Prompt | Endpoint |
| State management | Stateful (session) | Stateless |
| Streaming | Native (SSE) | Requires extensions |
| AI integration | First-class citizen | Retrofitted |
| Ecosystem maturity | Emerging (2024) | Mature (2011) |
Why Not Just Use OpenAPI?
OpenAPI limitations for LLM tool-calling:
1. Designed for human developers; LLMs struggle with complex REST semantics
2. Cannot express "what side effects does this tool have"
3. No mechanism for LLM-specific prompt templates
4. Inconsistent auth and capability discovery standards
MCP advantages:
1. Explicit semantics (Tool vs Resource vs Prompt each have clear roles)
2. Built-in capability discovery (all capabilities returned at initialize)
3. Designed for streaming LLM output (native SSE support)
4. LLM-friendly error handling with isError flag
37.7 MCP Integration in Hermes
# hermes/mcp/client.py
import asyncio
from typing import Any, Dict, List
class HermesMCPClient:
def __init__(self, transport):
self.transport = transport
self._request_id = 0
self._tools: List[Dict] = []
async def initialize(self) -> Dict:
result = await self._request("initialize", {
"protocolVersion": "2024-11-05",
"capabilities": {"roots": {"listChanged": True}, "sampling": {}},
"clientInfo": {"name": "hermes-agent", "version": "1.7.0"}
})
await self._notify("notifications/initialized")
tools_result = await self._request("tools/list")
self._tools = tools_result.get("tools", [])
return result
async def call_tool(self, name: str, arguments: Dict) -> Dict:
return await self._request("tools/call", {
"name": name,
"arguments": arguments
})
async def read_resource(self, uri: str) -> Dict:
return await self._request("resources/read", {"uri": uri})
async def _request(self, method: str, params: Dict = None) -> Any:
request_id = self._next_id()
message = {"jsonrpc": "2.0", "id": request_id, "method": method}
if params:
message["params"] = params
result = await self.transport.send_and_receive(message)
if "error" in result:
raise MCPError(result["error"]["message"], result["error"]["code"])
return result.get("result")
async def _notify(self, method: str, params: Dict = None):
message = {"jsonrpc": "2.0", "method": method}
if params:
message["params"] = params
await self.transport.send(message)
def _next_id(self) -> int:
self._request_id += 1
return self._request_id
class MCPError(Exception):
def __init__(self, message: str, code: int):
super().__init__(message)
self.code = code
Chapter Summary
MCP provides a unified language for AI Agent integration with external tools through standardized JSON-RPC 2.0 messaging:
- Design goals: Standardization, security, and interoperability together solve AI tool integration fragmentation
- Client-Server architecture: Host manages multiple Clients; each Client connects to exactly one Serverโclear separation of concerns
- Three transports: Stdio (local process), SSE (remote streaming), Streamable HTTP (stateless cloud)
- Three capability types: Tool (execute actions), Resource (provide data), Prompt (prompt templates)โeach with dedicated semantics
- JSON-RPC 2.0: Mature message format with unified error handling and clear request/response/notification types
- vs OpenAPI: MCP is AI-native by designโnot a replacement for OpenAPI but a complement for LLM tool-calling
Review Questions
- MCP's Stdio transport requires Server and Client to be on the same machine. How would you deploy a truly cross-node MCP Server in a Kubernetes cluster?
- MCP's
initializeresponse returns all Tools. If a Server has 500 Tools, can an LLM's context window fit all their descriptions? How would you solve this? - MCP messages use JSON encoding. For tools returning large binary data (images, PDFs), is JSON the optimal choice? What improvements would you propose?
- If two MCP Servers both provide a Tool named
search, how should Hermes handle the conflict?