What is MCP architecture?

Quick answer

MCP architecture is the overall structure the Model Context Protocol is built on, a host application containing one or more clients, each client holding a dedicated one-to-one connection to a single server, all communicating through a shared message format over a small number of interchangeable transport mechanisms. The design is deliberately layered so that a host application’s business logic, a client’s connection management, and a server’s actual capability implementation each stay cleanly separated, which is what lets any conforming client work with any conforming server without either side needing custom code written specifically for the other.

Summary slides
MCP architecture
The three roles and why they're kept structurally distinct
How messages actually move: JSON-RPC as the shared language
Why capability negotiation is architecturally central rather than…
Common mistakes teams make around MCP architecture

The three roles and why they’re kept structurally distinct

A host is the actual application a person uses, a code editor, a chat interface, an agent platform, and it’s responsible for the parts of the system that have nothing to do with any specific server, deciding which tools to surface to a model, managing the overall conversation, and coordinating across however many separate server connections a given session involves. A client lives inside the host and handles exactly one connection to exactly one server, managing that connection’s handshake, its ongoing session state, and the actual message traffic flowing back and forth. A server implements the protocol on the other end, exposing whatever tools, resources, and prompts it was built to provide and doing the real work behind them.

Keeping these three roles structurally distinct, rather than blurring host logic into client logic or client logic into server logic, is what makes the rest of MCP’s architecture work as cleanly as it does. A host can add a new server connection without touching how it manages any of its existing ones, since each client instance is self-contained. A server author never needs to know anything about which host applications will eventually connect to it, since a server only ever has to correctly speak the protocol to whatever client happens to be on the other end. This separation of concerns is the same principle that’s made layered software architecture reliable for decades, applied specifically to the problem of connecting AI models to external capability.

Why every client-server connection is deliberately one-to-one

A detail that surprises people encountering MCP’s architecture for the first time is that a client doesn’t multiplex several server connections through one shared channel, each client instance is dedicated to a single server, and a host wanting to connect to five different servers runs five separate client instances, one per connection. This isn’t an accidental limitation, it’s a deliberate architectural choice that keeps each connection’s state, its negotiated capabilities, its session, its authentication, entirely isolated from every other connection the host happens to also be managing.

This isolation matters directly for reliability and for security. A problem with one server, a dropped connection, a misbehaving response, a security issue, stays contained to that one client instance and doesn’t have any structural path to affect a completely separate connection to a different server. A host managing several connections this way can apply different levels of trust and different access controls to each one independently, treating a well-vetted internal server differently from a newly adopted third-party one, precisely because the architecture never forces those separate connections to share any state that would make that kind of differentiated treatment harder to enforce cleanly.

How messages actually move: JSON-RPC as the shared language

Underneath the roles this article has already described, MCP structures its actual message exchange using JSON-RPC, a well-established, lightweight format for representing requests, responses, and notifications as structured data. A client sends a request, calling a tool, reading a resource, and a server sends back a response carrying either a result or a structured error. Some messages flow the other direction as notifications, a server telling a client that its list of available tools has changed, without expecting or requiring a direct response back.

Choosing an established format here rather than inventing something bespoke was itself a deliberate architectural decision, since it means implementers on both sides can rely on existing, well-tested JSON-RPC libraries for the actual message parsing and validation, rather than every SDK needing to build that low-level plumbing from scratch. This is a small detail in isolation, but it’s part of a broader pattern running through MCP’s design: reuse an established, well-understood standard wherever one already fits, and reserve genuinely new specification for the parts of the problem that actually need it.

How the transport layer stays separate from everything above it

MCP deliberately separates its message format from how those messages actually travel between a client and a server, which is what lets the same protocol work whether a server runs as a local subprocess or as a remote network service. For a local server, messages travel over standard input and output, the client launches the server as a subprocess and communicates directly through its input and output streams, which works well when a server needs direct access to the same machine the host application is running on. For a remote server, messages travel over an HTTP-based connection, supporting the kind of persistent, bidirectional communication a long-running interaction needs even though the underlying transport is fundamentally request-based.

This separation between message format and transport mechanism is exactly the same layering principle the rest of MCP’s architecture follows, and it matters practically because it means a server’s actual tools and resources don’t need to change at all depending on how a client happens to be reaching it. A team can build a server once, expose it locally during development and remotely in production, and the tool definitions, the schemas, the actual logic behind each capability, stay entirely unaffected by that transport difference.

The lifecycle a connection actually goes through

Every MCP connection moves through a defined sequence of phases regardless of which transport carries it. First comes initialization, where a client and server exchange protocol version information and each side declares what capabilities it supports, establishing a shared understanding of what’s actually possible over this specific connection before anything else happens. Then comes the operational phase, where the actual work happens, a client listing and invoking tools, reading resources, retrieving prompts, and a server responding to each of these requests as they arrive. Finally comes shutdown, where the connection closes cleanly, giving both sides a defined point to release whatever resources they were holding rather than leaving a connection in an ambiguous, half-closed state.

This explicit lifecycle matters for reliability in ways that are easy to overlook until something goes wrong. A client and server that skip proper initialization risk one side assuming a capability the other doesn’t actually support, discovering the mismatch only once a request fails in some confusing way rather than being caught cleanly upfront during the handshake. A connection with no defined shutdown phase risks leaving resources allocated indefinitely on one side after the other has already moved on, the kind of quiet resource leak that accumulates gradually and becomes hard to diagnose precisely because nothing about it looks like an obvious, immediate failure.

Why capability negotiation is architecturally central rather than incidental

MCP’s architecture treats capability negotiation as a first-class part of the protocol rather than something bolted on afterward, because servers genuinely vary enormously in what they offer, and a client has no reliable way to know in advance whether a specific server it’s connecting to supports resources, supports prompts, supports notifications about changing tool lists, or supports none of these beyond the most basic tool-calling. Building this negotiation into the handshake itself, rather than assuming every server supports every optional feature, is what lets MCP accommodate everything from a minimal, single-purpose server to a full-featured one without either extreme breaking the protocol for the other.

This design choice reflects a broader architectural philosophy running through MCP: the protocol should accommodate a wide range of implementation complexity rather than forcing every server to implement every possible feature just to be considered compliant. A server exposing exactly one tool and nothing else is just as valid an MCP implementation as one exposing dozens of tools, resources, and prompts together, and the negotiation step is precisely what lets a client interact correctly with either one without needing to know in advance which kind of server it’s actually talking to.

How this architecture supports the security concerns covered elsewhere

The architectural choices this article has described directly enable the security practices covered in more depth elsewhere in this collection’s discussions of MCP clients, tools, and servers. Because each client-server connection is isolated, a host can apply differentiated trust and differentiated access controls per connection rather than being forced into an all-or-nothing trust model across every server it happens to be connected to. Because capability negotiation happens explicitly, a host knows precisely what a given server can do before any request is ever sent, which is what makes it possible to curate what actually gets surfaced to a model rather than blindly exposing whatever a server happens to offer.

This is worth naming directly because architecture and security are often treated as separate concerns, when in MCP’s case the clean, layered structure this article has described is itself a meaningful part of what makes the surrounding security discipline achievable at all. A tangled, poorly separated architecture would make it considerably harder to apply the kind of connection-by-connection scrutiny and control that the broader discussion of tool-calling security and agent permissions depends on, and MCP’s deliberate isolation between connections is what keeps that scrutiny practical rather than an unmanageable, cross-cutting mess.

Common mistakes teams make around MCP architecture

1. Building a host application that blurs client and host responsibilities together, losing the clean isolation between separate server connections the architecture was specifically designed to provide.

2. Skipping proper capability negotiation and assuming every connected server supports the same features, discovering the mismatch only once an unsupported request fails confusingly.

3. Treating the transport layer, local versus remote, as something that should change a server’s actual tool definitions or logic, rather than keeping message format and transport genuinely separate.

4. Applying uniform trust across every connected server simply because the architecture technically allows blending them, rather than using the isolation between connections to apply differentiated scrutiny per server.

5. Leaving connections without a proper shutdown phase, accumulating quiet, gradually worsening resource leaks that are hard to trace back to their actual cause.

What connects these mistakes is failing to use the structural separation MCP’s architecture actually provides, the isolation between connections, the explicit negotiation of capabilities, the clean split between message format and transport, all exist specifically to make a system built on this protocol more reliable and more secure than one without that structure, and a team that flattens these distinctions loses exactly the benefit the architecture was designed to deliver.

The deeper point about MCP’s architecture is that its layered, deliberately isolated design isn’t incidental complexity, it’s what makes the protocol’s core promise, any conforming client working with any conforming server, actually hold up in practice, and understanding why each layer exists the way it does is what lets a team build on top of MCP with the same confidence in its structure that the protocol’s own design was built around from the start.