MCP

MCP Architecture👨‍💻

The MCP architecture consists of three layers: hosts, clients, and servers. A host is the AI application the user interacts with (Claude Desktop, an IDE plugin, a custom chatbot). Inside the host, one or more MCP clients manage connections to MCP servers. Each client maintains a 1:1 connection with a single server, handling the protocol lifecycle from initialization through shutdown. Servers expose tools, resources, and prompts to clients over a transport layer. MCP supports multiple transports: stdio for local process communication, and Streamable HTTP for remote server communication. All messages follow the JSON-RPC 2.0 format, providing a language-agnostic, well-structured wire protocol. During initialization, clients and servers negotiate capabilities to establish what features are available for the session.

Key Takeaways

  • 1The three-tier architecture separates concerns cleanly: hosts handle user interaction and AI model communication, clients manage protocol sessions with servers, and servers expose capabilities. A single host can run multiple clients connected to different servers simultaneously.
  • 2MCP uses JSON-RPC 2.0 as its message format. Every message is either a request (with id, method, params), a response (with id, result or error), or a notification (with method but no id). This format is language-agnostic, human-readable, and has well-defined error codes.
  • 3The stdio transport communicates via standard input/output streams. The host spawns the server as a child process and pipes JSON-RPC messages through stdin/stdout. This is the simplest transport and works for local integrations without any network configuration.
  • 4The Streamable HTTP transport uses HTTP POST for client-to-server requests, HTTP GET with Server-Sent Events (SSE) for server-to-client streaming, and HTTP DELETE for session termination. Session IDs in headers route requests to the correct server instance.
  • 5Capability negotiation happens during the initialize handshake. The client declares what it supports (roots, sampling), and the server declares what it offers (tools, resources, prompts, logging). Both sides use these declarations to avoid calling unsupported methods.
  • 6Security in MCP is transport-dependent. Stdio transport inherits the security of the host process (file system permissions, environment variables). HTTP transport requires explicit authentication (API keys, OAuth tokens) and should always use TLS in production.

Examples

Host spawning multiple MCP clients for different servers

typescript

A host typically manages multiple MCP clients, each connected to a specialized server. This pattern lets the AI model access file system operations, database queries, and API calls through a unified interface. The host aggregates tool lists from all servers and presents them to the model as a single collection. Each client-server connection is independent and isolated.

JSON-RPC 2.0 message flow in an MCP session

json

This shows the complete message flow of an MCP session using JSON-RPC 2.0. Each request has a unique id that the response echoes back, enabling the client to match responses to requests. Notifications lack an id and expect no response. The initialize/initialized exchange must complete before any tool or resource requests. Every message has the jsonrpc: 2.0 version field.

Stdio transport: spawning a server as a child process

typescript

With stdio transport, the client spawns the server as a child process and communicates via stdin/stdout pipes. Environment variables are passed to the child process, which is how servers receive configuration like database URLs and API keys. The cwd option sets the server's working directory. This transport requires no network configuration and works entirely within the local machine.

Streamable HTTP transport with session management

typescript

Streamable HTTP transport connects to remote servers over HTTPS. The client sends JSON-RPC requests as HTTP POST, receives responses in the POST body, and can open a GET-based SSE stream for server-initiated notifications. Session IDs are managed via the mcp-session-id header. The DELETE method terminates the session. This transport works with standard HTTP infrastructure including load balancers, CDNs, and authentication proxies.

Capability negotiation and conditional feature usage

typescript

After initialization, the client should check the server's declared capabilities before calling any methods. This prevents errors from calling unsupported methods and allows the host to adapt its behavior based on what each server offers. For example, a server that only exposes tools will not have a resources capability, so the host should skip resource listing for that server.

Common Mistakes

Mistake:

Assuming all MCP servers run on the same transport. Developers hardcode stdio assumptions and then cannot connect to remote HTTP servers, or vice versa.

Fix:

Design your host/client code to support multiple transports. Use configuration (like Claude Desktop's mcpServers config) to specify the transport per server. The MCP client SDK handles the transport abstraction -- just instantiate the correct transport class.

Mistake:

Sending requests before the initialization handshake completes. For example, calling tools/list immediately after connecting without waiting for the initialize response and sending notifications/initialized.

Fix:

Always follow the initialization sequence: send initialize request, wait for the response, send notifications/initialized, then proceed with normal requests. The SDK handles this automatically when you call client.connect(), but if you are implementing the protocol manually, the order is critical.

Mistake:

Exposing an MCP HTTP server on a public network without authentication or TLS, allowing anyone to invoke tools and read resources.

Fix:

Always use HTTPS (TLS) for remote MCP servers. Add authentication middleware (API keys, OAuth, JWT) to the HTTP transport. The MCP protocol itself does not define authentication -- it is the server operator's responsibility to secure the transport layer.

Mistake:

Treating the client-server relationship as many-to-one. Developers try to have multiple clients share a single transport or server instance, causing message routing conflicts.

Fix:

Each MCP client maintains exactly one connection to one server. If you need multiple servers, create multiple client instances. The Streamable HTTP transport supports multiple clients through session management, but each session is still a distinct 1:1 connection.

Mistake:

Ignoring JSON-RPC error codes and treating all errors the same, missing the distinction between parse errors (-32700), invalid requests (-32600), method not found (-32601), and internal errors (-32603).

Fix:

Handle different JSON-RPC error codes appropriately. Method not found (-32601) means the server does not support that capability -- check capabilities. Invalid params (-32602) means bad arguments -- fix the request. Internal error (-32603) means a server-side failure -- retry or report.

Best Practices

  • Use stdio transport for local servers that run on the same machine as the host. Use Streamable HTTP for remote servers, multi-tenant deployments, or servers behind authentication. The transport choice should match your deployment model.
  • Always check server capabilities after initialization before calling any primitive methods. A robust host gracefully adapts to servers that offer different combinations of tools, resources, and prompts.
  • Implement graceful shutdown in both clients and servers. Close the transport cleanly to avoid orphaned processes (stdio) or leaked sessions (HTTP). Listen for SIGINT/SIGTERM and call close() on the client or server.
  • Log all JSON-RPC messages during development. Since every message is human-readable JSON, logging the full message stream makes debugging protocol issues straightforward. Disable verbose logging in production to avoid performance overhead.
  • For HTTP transport, implement session cleanup with timeouts. If a client disconnects without sending a DELETE request, the server should automatically clean up the session after a configurable idle timeout (e.g., 30 minutes).
  • Keep the host's tool aggregation logic simple. When a host connects to multiple servers, namespace tools by server name to avoid conflicts (e.g., filesystem:read-file vs database:read-schema). Present the combined tool list to the model with clear descriptions.

Summary

The MCP architecture is built on three layers: hosts (AI applications), clients (protocol session managers), and servers (capability providers). Communication uses JSON-RPC 2.0 messages over pluggable transports -- stdio for local processes and Streamable HTTP for remote servers. During initialization, clients and servers negotiate capabilities to establish what features are available. A single host can manage multiple clients, each connected to a different server, aggregating tools, resources, and prompts into a unified interface for the AI model. Security is transport-dependent: stdio inherits process permissions, while HTTP requires explicit authentication and TLS.

Practice MCP with hands-on challenges

Learn mcp architecture hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master MCP with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.