MCP Architecture Explained: Clients, Servers, Hosts & Transports
A comprehensive breakdown of the MCP architecture — how clients, servers, hosts, and transports work together to enable AI-tool communication.
MCP Architecture Explained
The Model Context Protocol architecture is a three-layer system consisting of hosts, clients, and servers connected through standardized transports. This architecture cleanly separates user-facing concerns (hosts), protocol management (clients), and tool implementation (servers), enabling a modular ecosystem where components can be mixed, matched, and composed.
Understanding this architecture is essential for anyone building MCP servers, integrating MCP into applications, or designing systems that use MCP for AI-tool communication.
Architecture Overview
The Three Layers
┌──────────────────────────────────────────────────────────┐
│ HOST │
│ (e.g., Claude Desktop, Cursor) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Client 1│ │ Client 2│ │ Client 3│ ┌─────────┐ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │ LLM │ │
│ │ │ │ │ (Claude, │ │
│ │ │ │ │ GPT, │ │
│ │ │ │ │ etc.) │ │
│ │ │ │ └─────────┘ │
└───────┼─────────────┼────────────┼───────────────────────┘
│ │ │
┌────▼────┐ ┌────▼────┐ ┌───▼─────┐
│ Server │ │ Server │ │ Server │
│ (File │ │ (GitHub)│ │ (DB) │
│ system) │ │ │ │ │
└─────────┘ └─────────┘ └─────────┘
Layer 1: Hosts -- User-facing applications that embed AI capabilities. A host manages the user interface, maintains MCP client instances, enforces security policies, and coordinates between the AI model and connected servers.
Layer 2: Clients -- Protocol connectors within hosts. Each client maintains exactly one connection to one MCP server. Clients handle the protocol lifecycle: initialization, capability negotiation, message routing, and shutdown.
Layer 3: Servers -- Standalone programs that expose capabilities (tools, resources, prompts) through the MCP protocol. Servers wrap external systems -- APIs, databases, file systems, services -- and make them accessible to AI models.
Hosts: The User-Facing Layer
What Hosts Do
An MCP host is the AI application the user interacts with directly. It has several responsibilities:
- Client management: Creates and manages MCP client instances, one per connected server
- Security enforcement: Applies security policies, manages consent, and controls which servers are allowed
- Model integration: Passes tool descriptions to the AI model and routes tool calls to the correct client
- User interface: Presents server capabilities to the user and handles consent dialogs
- Configuration: Reads server configuration (typically from a JSON file) and initializes connections
Examples of MCP Hosts
| Host | Type | Notable MCP Features |
|---|---|---|
| Claude Desktop | Desktop AI assistant | First MCP host; JSON config for servers |
| Claude Code | CLI AI coding agent | Direct MCP server management |
| Cursor | AI-powered IDE | Project-level MCP server configuration |
| VS Code + Copilot | Code editor with AI | MCP server support via Copilot |
| Windsurf | AI coding IDE | Built-in MCP server management |
| Zed | Code editor | Native MCP support |
| ChatGPT Desktop | Desktop AI assistant | MCP server connectivity |
| Custom applications | Any software | Built using MCP client SDKs |
Host Configuration
Most hosts use a JSON configuration file to define which servers to connect to:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"],
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxx"
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
},
"remote-api": {
"url": "https://mcp.example.com/sse",
"transport": "sse",
"headers": {
"Authorization": "Bearer token123"
}
}
}
}
Each entry defines:
- A name for identification
- Either a command (for local stdio servers) or a url (for remote servers)
- Optional arguments and environment variables
- Optional transport specification (defaults to stdio for command-based, SSE for URL-based)
Host Responsibilities in Detail
Tool aggregation: When a host has multiple clients connected to different servers, it aggregates all available tools into a single list that the AI model sees. The model does not know or care which server provides which tool -- it just sees a unified menu of capabilities.
Host aggregated tool list:
├── From GitHub server:
│ ├── create_issue
│ ├── list_pull_requests
│ └── search_repos
├── From Filesystem server:
│ ├── read_file
│ ├── write_file
│ └── search_files
└── From Database server:
├── query
├── list_tables
└── describe_table
Tool routing: When the model calls a tool, the host must route the call to the correct client (and therefore the correct server). This routing is based on which server registered which tool during discovery.
Consent management: Before executing a tool call that has side effects (e.g., writing a file, creating a GitHub issue), the host should prompt the user for consent. The level of consent required is a host policy decision.
Clients: The Protocol Layer
What Clients Do
An MCP client is a protocol component inside a host. Each client manages a single connection to a single server. Its responsibilities:
- Connection management: Establishes and maintains the transport connection
- Protocol lifecycle: Handles initialization, capability negotiation, and shutdown
- Message routing: Sends requests to the server and processes responses
- Capability tracking: Remembers what the connected server supports
- Error handling: Manages connection errors, timeouts, and retries
Client-Server Relationship
The relationship is strictly 1:1 -- one client instance per server connection. This is a deliberate design choice:
Host
├── Client A ←──────→ Server A (GitHub)
├── Client B ←──────→ Server B (Filesystem)
└── Client C ←──────→ Server C (Database)
Each connection is independent. If Server B crashes, Clients A and C continue to function normally. This isolation is a key reliability feature.
Client Implementation Example
Here is how a client connects to a server using the TypeScript SDK:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
// Create a transport for a local server
const transport = new StdioClientTransport({
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: {
GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_TOKEN,
},
});
// Create a client
const client = new Client(
{ name: "my-host-app", version: "1.0.0" },
{ capabilities: { sampling: {} } } // Declare client capabilities
);
// Connect to the server
await client.connect(transport);
// Discover server capabilities
const tools = await client.listTools();
console.log("Available tools:", tools.tools.map(t => t.name));
const resources = await client.listResources();
console.log("Available resources:", resources.resources.map(r => r.uri));
// Call a tool
const result = await client.callTool("search_repos", {
query: "MCP server",
language: "typescript",
});
console.log("Search results:", result.content);
// Clean up
await client.close();
Python equivalent:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-github"],
env={"GITHUB_PERSONAL_ACCESS_TOKEN": os.environ["GITHUB_TOKEN"]},
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize
await session.initialize()
# Discover tools
tools = await session.list_tools()
print(f"Available tools: {[t.name for t in tools.tools]}")
# Call a tool
result = await session.call_tool("search_repos", {
"query": "MCP server",
"language": "typescript",
})
print(f"Results: {result.content[0].text}")
Servers: The Capability Layer
What Servers Do
MCP servers are the programs that actually provide capabilities to AI models. They:
- Declare capabilities: During initialization, report which primitives they support (tools, resources, prompts)
- Register tools: Define functions the AI model can call, with schemas and descriptions
- Expose resources: Provide data that applications can read into context
- Offer prompts: Supply reusable prompt templates for common workflows
- Handle execution: Perform the actual work when a tool is called or a resource is read
- Manage state: Maintain any state needed for their operation (database connections, API sessions, etc.)
Server Implementation Patterns
Minimal server (TypeScript):
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "example-server",
version: "1.0.0",
});
server.tool(
"hello",
"Say hello to someone",
{ name: z.string() },
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}!` }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);
Minimal server (Python):
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("example-server")
@mcp.tool()
def hello(name: str) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run()
For a deep dive into the three primitives servers expose, see MCP Building Blocks.
Transports: The Communication Layer
Transports define how messages physically travel between clients and servers. MCP supports three transport mechanisms, each optimized for different deployment scenarios.
stdio Transport (Local)
The stdio (standard input/output) transport is used for local MCP servers that run on the same machine as the host application.
How it works:
- The host spawns the server as a child process
- Messages from client to server go through the process's stdin
- Messages from server to client come through the process's stdout
- The server's stderr is available for logging (not protocol messages)
┌──────────┐ stdin ┌──────────┐
│ │ ──────────────► │ │
│ Client │ │ Server │
│ (Host) │ stdout │ (Child │
│ │ ◄────────────── │ process)│
└──────────┘ └──────────┘
stderr → logs
Characteristics:
- Zero network configuration
- Very low latency (in-process communication)
- No authentication needed (inherits user's process permissions)
- Single-user by design
- Server lifecycle tied to host application
Message framing: Messages are delimited by newlines. Each JSON-RPC message is a single line:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}\n
{"jsonrpc":"2.0","id":1,"result":{...}}\n
{"jsonrpc":"2.0","id":2,"method":"tools/list"}\n
{"jsonrpc":"2.0","id":2,"result":{"tools":[...]}}\n
HTTP with SSE Transport (Remote)
The HTTP with Server-Sent Events transport is used for remote MCP servers accessible over the network.
How it works:
- The client connects to the server's SSE endpoint to receive server-to-client messages
- The client sends requests to the server via HTTP POST
- The server streams responses and notifications through the SSE connection
┌──────────┐ HTTP POST ┌──────────┐
│ │ ──────────────► │ │
│ Client │ │ Remote │
│ (Host) │ SSE stream │ Server │
│ │ ◄────────────── │ │
└──────────┘ └──────────┘
Characteristics:
- Works across network boundaries
- Requires authentication (OAuth 2.1)
- Supports multiple concurrent clients
- Server runs independently of any host
- Suitable for cloud deployment
Endpoint structure:
POST /mcp/message → Client sends requests
GET /mcp/sse → Client subscribes to server events
Streamable HTTP Transport (Remote, Modern)
The Streamable HTTP transport is an upgraded HTTP transport introduced in the March 2025 specification revision.
How it works:
- Client sends requests via HTTP POST
- Server can respond with either a regular HTTP response or upgrade to a streaming response
- Supports server-initiated messages through the streaming channel
- More efficient than SSE for many use cases
Characteristics:
- All the benefits of HTTP-based transport
- Better streaming support than SSE
- More efficient for bidirectional communication
- Recommended for new remote server deployments
- Full backward compatibility with SSE clients
Transport Comparison
| Feature | stdio | HTTP + SSE | Streamable HTTP |
|---|---|---|---|
| Deployment | Local only | Remote | Remote |
| Latency | Minimal | Network-dependent | Network-dependent |
| Authentication | OS-level | OAuth 2.1 | OAuth 2.1 |
| Multi-client | No | Yes | Yes |
| Server lifecycle | Tied to host | Independent | Independent |
| Setup complexity | Minimal | Moderate | Moderate |
| Streaming | Newline-delimited | SSE | Native streaming |
| Firewall-friendly | N/A | Yes (outbound HTTP) | Yes (outbound HTTP) |
| Best for | Dev tools, local files | Cloud APIs, SaaS | Modern cloud deployments |
For a detailed comparison of when to use each transport, see Local vs Remote MCP Servers.
The Connection Lifecycle
Every MCP connection follows a defined lifecycle with four phases.
Phase 1: Initialization
The client initiates the connection by sending an initialize request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"roots": { "listChanged": true },
"sampling": {}
},
"clientInfo": {
"name": "claude-desktop",
"version": "1.5.0"
}
}
}
The server responds with its own information:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": true, "listChanged": true },
"prompts": { "listChanged": true },
"logging": {}
},
"serverInfo": {
"name": "github-server",
"version": "2.1.0"
}
}
}
The client then sends an initialized notification:
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
Phase 2: Discovery
After initialization, the client discovers the server's capabilities:
// List tools
{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}
// List resources
{"jsonrpc": "2.0", "id": 3, "method": "resources/list"}
// List prompts
{"jsonrpc": "2.0", "id": 4, "method": "prompts/list"}
The server responds with detailed descriptions including names, descriptions, and schemas for each capability.
Phase 3: Normal Operation
During normal operation, the client and server exchange messages:
Client-to-server (requests):
tools/call-- Execute a toolresources/read-- Read a resourceprompts/get-- Get a prompt templatecompletion/complete-- Request auto-completionlogging/setLevel-- Configure logging
Server-to-client (requests, if client supports sampling):
sampling/createMessage-- Request the client to generate an LLM completion
Server-to-client (notifications):
notifications/tools/list_changed-- Tool list has been updatednotifications/resources/list_changed-- Resource list has been updatednotifications/resources/updated-- A specific resource has changednotifications/message-- Log message
Client-to-server (notifications):
notifications/roots/list_changed-- Working directories have changednotifications/cancelled-- Cancel a pending request
Phase 4: Shutdown
Either side can initiate shutdown:
- For stdio: The host terminates the child process
- For HTTP: The client closes the SSE connection
- Servers should handle graceful cleanup (close DB connections, flush buffers, etc.)
Initialization ──► Discovery ──► Normal Operation ──► Shutdown
│ │ │ │
│ initialize │ tools/list │ tools/call │ close
│ initialized │ resources/ │ resources/ │ connection
│ │ list │ read │
│ │ prompts/list │ notifications │
Capability Negotiation Deep Dive
Client Capabilities
Clients can declare support for:
| Capability | Description | Effect |
|---|---|---|
roots | Working directory awareness | Server can discover project roots |
roots.listChanged | Root change notifications | Client notifies when roots change |
sampling | LLM completion requests | Server can ask client for AI generations |
experimental | Experimental features | Protocol extensions |
Server Capabilities
Servers can declare support for:
| Capability | Description | Effect |
|---|---|---|
tools | Tool primitives | Server exposes callable tools |
tools.listChanged | Tool list change notifications | Server notifies when tools change |
resources | Resource primitives | Server exposes readable data |
resources.subscribe | Resource subscriptions | Client can subscribe to resource updates |
resources.listChanged | Resource list change notifications | Server notifies when resources change |
prompts | Prompt primitives | Server exposes prompt templates |
prompts.listChanged | Prompt list change notifications | Server notifies when prompts change |
logging | Structured logging | Server can send log messages to client |
experimental | Experimental features | Protocol extensions |
Negotiation Rules
- Clients should only call methods that correspond to server-declared capabilities
- Servers should only use features that correspond to client-declared capabilities
- If a server declares
toolsbut notresources, the client should not callresources/list - Protocol version is negotiated to the highest mutually supported version
Message Flow Patterns
Simple Tool Call
User → Host: "What issues are open in the auth module?"
│
Host → Model: [user message + tool descriptions]
│
Model → Host: "I'll search for open auth issues"
+ tool_call: search_issues({query: "auth", state: "open"})
│
Host → Client: Route to GitHub client
│
Client → Server: {"method": "tools/call", "params": {"name": "search_issues", ...}}
│
Server: Calls GitHub API, formats results
│
Server → Client: {"result": {"content": [{"type": "text", "text": "Found 3 issues..."}]}}
│
Client → Host: Return tool result
│
Host → Model: [tool result as context]
│
Model → Host: "There are 3 open issues in the auth module: ..."
│
Host → User: Display response
Multi-Tool Chain
User → Host: "Fix the bug in issue #42 and create a PR"
Step 1: Model decides to read the issue
Host → GitHub Client → GitHub Server: tools/call get_issue({number: 42})
Server → Client → Host → Model: Issue details
Step 2: Model decides to search the codebase
Host → Filesystem Client → FS Server: tools/call search_files({query: "auth handler"})
Server → Client → Host → Model: File matches
Step 3: Model decides to read a file
Host → Filesystem Client → FS Server: tools/call read_file({path: "src/auth.ts"})
Server → Client → Host → Model: File contents
Step 4: Model decides to write the fix
Host → Filesystem Client → FS Server: tools/call write_file({path: "src/auth.ts", content: "..."})
Server → Client → Host → Model: Success
Step 5: Model decides to create a PR
Host → GitHub Client → GitHub Server: tools/call create_pull_request({...})
Server → Client → Host → Model: PR #87 created
Model → User: "I've fixed the bug and created PR #87"
Server-Initiated Sampling
When a server needs an LLM completion (and the client supports sampling):
Client → Server: tools/call analyze_data({dataset: "sales"})
│
Server: Needs AI help to interpret the data
│
Server → Client: {"method": "sampling/createMessage", "params": {
"messages": [{"role": "user", "content": "Summarize this data: ..."}],
"maxTokens": 500
}}
│
Client → Host: Request LLM completion (with user consent)
│
Host → Model: Generate completion
│
Model → Host: Completion text
│
Host → Client → Server: {"result": {"content": {"type": "text", "text": "..."}}}
│
Server: Uses the completion in its analysis
│
Server → Client: {"result": {"content": [{"type": "text", "text": "Analysis complete: ..."}]}}
Architecture Patterns
Single-Host, Multiple Servers
The most common pattern. One AI application connects to several specialized servers:
┌─────────────────────────────────┐
│ Claude Desktop │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ C1 │ │ C2 │ │ C3 │ │
│ └──┬──┘ └──┬──┘ └──┬──┘ │
└─────┼───────┼───────┼─────────┘
│ │ │
┌─────▼──┐ ┌─▼────┐ ┌▼───────┐
│GitHub │ │File │ │Postgres│
│Server │ │Server│ │Server │
└────────┘ └──────┘ └────────┘
Multi-Host, Shared Servers
Remote MCP servers can be shared across multiple hosts:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Claude │ │ Cursor │ │ Custom │
│ Desktop │ │ IDE │ │ App │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────┬───────┴──────────────┘
│
┌──────▼──────┐
│ Remote MCP │ (Shared team server)
│ Server │
│ (via SSE) │
└─────────────┘
Hierarchical (Composable) Servers
An MCP server can also be an MCP client to other servers. This enables composable architectures:
┌──────────────┐
│ Host │
│ ┌──────┐ │
│ │Client│ │
│ └──┬───┘ │
└──────┼───────┘
│
┌──────▼──────┐
│ Orchestrator│ ◄── Acts as server to the host
│ Server │ ──► Acts as client to sub-servers
│ ┌──┐ ┌──┐ │
│ │C1│ │C2│ │
│ └┬─┘ └┬─┘ │
└───┼─────┼───┘
│ │
┌───▼──┐ ┌▼────┐
│Tool │ │Tool │
│Srv A │ │Srv B│
└──────┘ └─────┘
Security Architecture
Trust Boundaries
MCP defines clear trust boundaries in its architecture:
┌─── Trust Boundary 1: User ──────────────────────────┐
│ │
│ ┌─── Trust Boundary 2: Host ────────────────────┐ │
│ │ │ │
│ │ ┌── Trust Boundary 3: Server Connection ──┐ │ │
│ │ │ │ │ │
│ │ │ Client ◄──────────► Server │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
User boundary: The user must consent to connecting servers and authorizing operations.
Host boundary: The host enforces security policies, manages authentication, and controls information flow between servers (servers should not have direct access to each other).
Server connection boundary: Each client-server connection is isolated from others. One server cannot access tools or data from another server unless the host explicitly enables it.
Authentication Architecture
For local servers (stdio), no additional authentication is needed -- the server runs as the user's process.
For remote servers, MCP uses OAuth 2.1:
┌──────┐ ┌──────────┐ ┌───────────┐
│Client│ │ Auth │ │ Remote │
│ │──1──►│ Server │ │ MCP │
│ │◄─2──│ (OAuth) │ │ Server │
│ │──3──────────────────► │ │
│ │◄─4──────────────────── │ │
└──────┘ └──────────┘ └───────────┘
1. Authorization request (redirect to auth server)
2. Authorization code (after user consent)
3. Request with access token
4. Response
For a comprehensive guide to MCP security, see MCP Security Model.
Error Handling Architecture
JSON-RPC Error Codes
MCP uses standard JSON-RPC 2.0 error codes plus custom extensions:
| Code | Name | Meaning |
|---|---|---|
| -32700 | Parse error | Invalid JSON |
| -32600 | Invalid request | Not a valid JSON-RPC request |
| -32601 | Method not found | Requested method does not exist |
| -32602 | Invalid params | Invalid method parameters |
| -32603 | Internal error | Server internal error |
Error Response Format
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params: 'repository' is required",
"data": {
"field": "repository",
"constraint": "required"
}
}
}
Tool Execution Errors
Tool errors are distinct from protocol errors. A protocol error means the message was malformed. A tool error means the tool ran but failed:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "Error: Repository 'nonexistent/repo' not found. Verify the owner and repo name."
}],
"isError": true
}
}
The isError: true flag tells the AI model that the tool execution failed, allowing it to retry or try a different approach.
Performance Considerations
Latency Profile
| Operation | stdio | HTTP/SSE | Streamable HTTP |
|---|---|---|---|
| Initialization | ~50ms | ~200ms | ~200ms |
| Tool discovery | ~10ms | ~100ms | ~100ms |
| Simple tool call | ~5ms + execution time | ~50ms + execution time | ~50ms + execution time |
| Resource read | ~5ms + read time | ~50ms + read time | ~50ms + read time |
The protocol overhead is minimal in all cases. The dominant factor is always the actual tool execution time (API calls, database queries, file I/O).
Concurrency
MCP supports concurrent operations through JSON-RPC request IDs. Multiple requests can be in flight simultaneously, and responses can arrive in any order:
Client → Server: {"id": 1, "method": "tools/call", "params": {"name": "slow_query", ...}}
Client → Server: {"id": 2, "method": "tools/call", "params": {"name": "fast_lookup", ...}}
Server → Client: {"id": 2, "result": {...}} // Fast lookup completes first
Server → Client: {"id": 1, "result": {...}} // Slow query completes second
Summary
The MCP architecture is built on clean separation of concerns:
- Hosts manage the user experience and security
- Clients manage protocol connections (one per server)
- Servers implement capabilities (tools, resources, prompts)
- Transports handle physical communication (stdio, SSE, Streamable HTTP)
This layered design enables a modular, composable, and secure ecosystem where components can be independently developed, deployed, and scaled.
Continue learning:
- MCP Building Blocks -- Deep dive into tools, resources, and prompts
- Local vs Remote Servers -- When to use each transport
- MCP Security Model -- Authentication, permissions, and best practices
- Composability in MCP -- Building hierarchical systems
- Browse MCP Servers -- Explore available servers
Frequently Asked Questions
What are the main components of MCP architecture?
MCP architecture consists of three main components: Hosts (AI applications that users interact with, like Claude Desktop or Cursor), Clients (protocol connectors inside hosts that maintain 1:1 connections with servers), and Servers (programs that expose tools, resources, and prompts). These communicate over Transports (stdio for local, SSE or Streamable HTTP for remote).
What is an MCP host?
An MCP host is the user-facing AI application that contains one or more MCP clients. Examples include Claude Desktop, Cursor IDE, VS Code with Copilot, and custom AI applications. The host manages client instances, enforces security policies, routes messages between the AI model and connected servers, and handles user consent for tool operations.
What is the difference between an MCP client and an MCP host?
A host is the user-facing application (like Claude Desktop), while a client is a protocol component inside the host that manages a single connection to one MCP server. A host can contain many clients — one per connected server. The client handles the protocol details (initialization, message routing, capability tracking) while the host handles the user experience.
What transport protocols does MCP support?
MCP supports three transport protocols: stdio (standard input/output for local server communication), HTTP with Server-Sent Events (SSE) for remote servers, and Streamable HTTP (an upgraded HTTP transport for modern deployments). The stdio transport is used for local tools, while HTTP-based transports are used for remote and cloud-hosted servers.
How does the MCP initialization handshake work?
The handshake follows three steps: (1) The client sends an 'initialize' request with its protocol version and capabilities, (2) the server responds with its protocol version, capabilities, and server info, (3) the client sends an 'initialized' notification confirming the connection is ready. Both sides negotiate to the highest mutually supported protocol version.
What is capability negotiation in MCP?
Capability negotiation is the process during initialization where clients and servers declare which optional features they support. For example, a server might declare it supports tools and resources but not prompts. A client might declare it supports sampling (allowing servers to request LLM completions). This ensures both sides know what the other can do.
Can an MCP server also be an MCP client?
Yes. MCP's composable architecture allows a server to simultaneously be a client to another server. This enables hierarchical patterns where an orchestrating agent acts as a server to the user's host application while being a client to multiple specialized tool servers. This is a key feature for building multi-agent systems.
How does message routing work in MCP?
The host application routes messages between the AI model and MCP servers through their respective clients. When the model generates a tool call, the host identifies which client (and therefore which server) handles that tool, sends the request through the appropriate client, receives the response, and feeds it back to the model. Each client maintains an independent connection to its server.
What happens when an MCP server crashes?
When a stdio-based server process crashes, the client detects the broken connection and can report the error to the host. The host may attempt to restart the server process automatically or notify the user. For HTTP-based servers, standard reconnection logic applies. Well-designed hosts implement graceful degradation — the AI continues to work but without the tools from the crashed server.
Is MCP a synchronous or asynchronous protocol?
MCP supports both patterns. Requests and responses follow a request-response pattern (synchronous at the message level), but the protocol also supports asynchronous notifications in both directions — servers can notify clients of changes (like updated tool lists), and clients can notify servers of changes (like updated working directories). The underlying transport can handle multiple concurrent requests.
Related Guides
Compare local (stdio) and remote (SSE/HTTP) MCP server deployments. Learn when to use each approach with practical examples and trade-offs.
Master the three pillars of MCP functionality — Tools (model-controlled functions), Resources (app-controlled data), and Prompts (user-controlled templates).
A comprehensive guide to understanding the Model Context Protocol — what it is, why Anthropic created it, and how it standardizes AI-tool integration.