MCP Protocol Internals: Host / Client / Server Triangular Architecture and JSON-RPC 2.0 Message Format
Chapter 34: MCP Protocol Deep Dive: Architecture, Transport Layer, and Capability Declaration
34.1 The Origins of MCP
Before MCP (Model Context Protocol) existed, every developer who wanted to give an AI model tool capabilities faced the same fragmentation problem. OpenAI had its own Function Calling format, Anthropic had its Tool Use specification, LangChain had its own Tool abstraction โ and none of these were compatible with each other. A well-built database query tool that needed to support both ChatGPT and Claude required maintaining two entirely separate codebases.
In November 2024, Anthropic released MCP (Model Context Protocol): an open, vendor-neutral protocol standard designed to be the USB-C interface between AI models and external tools โ a unified connection standard where tool developers implement once and every MCP-compatible AI client can use their work.
MCP's core design philosophy centers on separation of concerns:
- MCP Server (server side): focuses on providing capabilities โ exposing tools, resources, and prompt templates
- MCP Client (client side): focuses on AI model integration โ proxying user requests to Server capabilities
- Host (host application): the application running the Client (e.g., Claude Desktop, Claude Code, custom AI apps)
34.2 Overall Architecture
34.2.1 Three-Layer Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Host Application โ
โ (Claude Desktop / Claude Code / Custom)โ
โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ MCP Client โ โ MCP Client โ โ
โ โ (Conn A) โ โ (Conn B) โ โ
โ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โ
โโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโ
โ โ
MCP Protocol MCP Protocol
(stdio / HTTP) (stdio / HTTP)
โ โ
โโโโโโโโผโโโโโโโ โโโโโโโโผโโโโโโโ
โ MCP Server โ โ MCP Server โ
โ (Filesystem)โ โ (Database) โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
A Host can maintain multiple MCP Client connections simultaneously, with each Client connecting to a single MCP Server. The Claude model accesses Server capabilities through the Host's Client layer โ the entire process is transparent to the model, which only sees an abstract tool-call interface.
34.2.2 Core Concept Reference
| Concept | Provided By | Purpose |
|---|---|---|
| Tools | MCP Server | Executable functions the model can invoke |
| Resources | MCP Server | Readable data, like files or URLs |
| Prompts | MCP Server | Predefined prompt templates users can select |
| Roots | MCP Client | Tells Server the current working directory scope |
| Sampling | MCP Client | Server requests Host to invoke the LLM |
34.3 Transport Layers
MCP defines two standard transport layers suited for different deployment scenarios.
34.3.1 stdio Transport (Local Process Communication)
stdio is the simplest and most commonly used transport, suited for scenarios where the Server and Client run on the same machine.
How it works:
- The Host application launches the MCP Server process via
subprocess - The Client sends JSON-RPC messages to the Server via standard input (stdin)
- The Server returns responses via standard output (stdout)
- Server logs go to stderr (without interfering with protocol communication)
Host Process
โ
โโโ stdin โ [JSON-RPC Request] โ MCP Server Process
โ
โโโ stdout โ [JSON-RPC Response] โ MCP Server Process
stdio advantages:
- Zero network configuration โ no ports, no TLS
- Natural process isolation (Server crash doesn't affect Host)
- High security (no network exposure surface)
stdio limitations:
- Server must be on the same machine as the Client
- One Server instance can only be connected by one Client
- No Server-initiated push notifications (without additional pipes)
Claude Desktop stdio configuration example (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Documents"],
"env": {}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}
34.3.2 HTTP + SSE Transport (Remote Service Communication)
HTTP + SSE (Server-Sent Events) is the transport layer designed for remote deployment, supporting separated Server and Client deployments โ suitable for multi-user, cloud-based scenarios.
Communication model:
- Client sends requests to Server via HTTP POST
- Server pushes notifications and responses to Client via SSE (one-way persistent connection)
MCP Client MCP Server (remote)
โ โ
โโโโ HTTP POST /message โโโโโโโโโถโ Send request
โ โ
โโโโ SSE /sse โโโโโโโโโโโโโโโโโโโ Receive responses/notifications (persistent)
โ โ
โ GET /sse โ Establish SSE connection โ
HTTP+SSE advantages:
- Supports remote Servers (cross-network access)
- Multiple Clients can connect to the same Server
- Server can proactively push events to Clients
- Supports standard HTTP authentication (Bearer Token, OAuth)
2025 Streamable HTTP addition: Anthropic's March 2025 specification update introduced Streamable HTTP as a more flexible HTTP transport variant, allowing multiple response messages to be returned in streaming fashion within a single HTTP request, while maintaining backward compatibility with SSE.
34.4 Message Format: JSON-RPC 2.0
MCP is built on the JSON-RPC 2.0 specification for message formatting. All messages are JSON objects in three categories:
34.4.1 Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/home/user/document.txt"
}
}
}
34.4.2 Response
Successful response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "File contents..."
}
],
"isError": false
}
}
Error response:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid Request",
"data": "File path does not exist"
}
}
34.4.3 Notification
Notifications are one-way messages that require no response (no id field):
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed",
"params": {}
}
34.5 Capability Declaration: The Initialization Handshake
When an MCP connection is established, the Client and Server mutually declare their supported features through Capability Negotiation.
34.5.1 Initialization Flow
Client Server
โ โ
โโโโโ initialize โโโโโโโโโโโโโโโถโ
โ (clientCapabilities) โ
โ โ
โโโโโ initialize response โโโโโโโ
โ (serverCapabilities) โ
โ โ
โโโโโ initialized โโโโโโโโโโโโโโถโ
โ (notification: init done) โ
โ โ
โ [Normal operation] โ
34.5.2 Complete Initialization Messages
Client's initialize request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {}
},
"clientInfo": {
"name": "claude-code",
"version": "1.0.0"
}
}
}
Server's initialization response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"prompts": {
"listChanged": false
},
"logging": {}
},
"serverInfo": {
"name": "my-filesystem-server",
"version": "2.1.0"
}
}
}
34.6 Tools Capability in Detail
34.6.1 Tool List Query
// Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
// Response
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "read_file",
"description": "Read the contents of a file at the specified path",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Absolute or relative file path"
},
"encoding": {
"type": "string",
"enum": ["utf-8", "base64"],
"default": "utf-8"
}
},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "Write content to a file at the specified path",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"},
"create_dirs": {
"type": "boolean",
"default": false,
"description": "Automatically create missing parent directories"
}
},
"required": ["path", "content"]
}
}
]
}
}
34.6.2 Tool Invocation
// Call request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/Users/alice/notes.md",
"encoding": "utf-8"
}
}
}
// Success response
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "# My Notes\n\nToday I learned about the MCP protocol..."
}
],
"isError": false
}
}
Tool response content arrays support multiple content types: text (plain text), image (base64-encoded image data), and resource (embedded resource references).
34.7 Resources Capability in Detail
Resources expose data from the MCP Server, functioning like a "virtual file system." Each Resource has a URI identifier, and Clients can read its contents.
34.7.1 Resource List Query
{
"result": {
"resources": [
{
"uri": "file:///home/user/project/README.md",
"name": "Project README",
"description": "Project documentation",
"mimeType": "text/markdown"
},
{
"uri": "db://myapp/users/schema",
"name": "Users table schema",
"description": "Complete DDL for the users table",
"mimeType": "application/sql"
}
]
}
}
34.7.2 Resource Subscription and Change Notifications
If the Server declares resources.subscribe capability, Clients can subscribe to resource changes:
// Subscription request
{
"jsonrpc": "2.0",
"id": 6,
"method": "resources/subscribe",
"params": { "uri": "file:///home/user/project/config.json" }
}
// Server-pushed change notification
{
"jsonrpc": "2.0",
"method": "notifications/resources/updated",
"params": { "uri": "file:///home/user/project/config.json" }
}
34.8 Prompts Capability in Detail
Prompts are Server-predefined prompt templates that users can select and use directly in the Host application. This is a frequently overlooked but highly practical MCP capability.
// Prompt list response example
{
"result": {
"prompts": [
{
"name": "code_review",
"description": "Perform a comprehensive code review of selected code",
"arguments": [
{"name": "code", "description": "Code to review", "required": true},
{"name": "language", "description": "Programming language", "required": false}
]
}
]
}
}
When a Client calls prompts/get with argument values, the Server returns a fully assembled message array ready to be sent to the LLM โ complete with the user's code interpolated into the template.
34.9 Sampling: Server-Initiated LLM Calls
Sampling is MCP's most advanced capability โ it allows the MCP Server to request the Host to invoke the LLM. This implements a reverse call pattern: normally the Client calls the Server, but with Sampling the Server can initiate an LLM inference request back through the Client.
// Server sends sampling/createMessage request to Client
{
"jsonrpc": "2.0",
"id": 10,
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Summarize the error patterns in this log: ERROR: connection timeout..."
}
}
],
"modelPreferences": {
"hints": [{"name": "claude-sonnet-4-5"}],
"intelligencePriority": 0.8,
"speedPriority": 0.5
},
"maxTokens": 500
}
}
A typical Sampling use case: a filesystem Server that detects code errors can request the Host's LLM to generate fix suggestions, then return them as part of the tool call result to the user.
34.10 Protocol Versioning and Backward Compatibility
MCP uses date-based version identifiers (e.g., 2024-11-05). The current stable version is 2024-11-05, with an updated specification including Streamable HTTP support released in March 2025.
Version negotiation rules:
- Client declares its maximum supported protocol version in the
initializerequest - Server responds with the protocol version it will use (no higher than what the Client declared)
- If versions are incompatible, the connection fails
When implementing an MCP Server or Client, use the official SDK rather than implementing the protocol manually โ you get automatic version negotiation and future compatibility guarantees for free.
Summary
MCP provides a unified standard interface for the AI tool ecosystem through a clear three-layer architecture (Host/Client/Server) and two transport layers (stdio/HTTP+SSE). Its core design philosophy โ separation of concerns, capability declaration, and protocol neutrality โ means tool developers implement once and their work is immediately available across all MCP-compatible AI applications.
Key takeaways:
- stdio suits local tools; HTTP+SSE suits remote services
- The initialization handshake establishes mutual capability sets between both parties
- Three core capability types: Tools (executable), Resources (readable data), Prompts (templates)
- JSON-RPC 2.0 is the message foundation for all protocol communication
- Sampling enables the unusual but powerful pattern of Server-initiated LLM inference
The next chapter moves into hands-on practice: building a complete custom MCP Server from scratch.