MCP Server Architecture: How It Works, and What It Can’t Do
An MCP server exposes tools, resources, and prompts to an AI agent over a standard protocol, replacing custom integrations with one interface. This guide covers the primitives, the transports, and the full request flow — then the part the spec is silent on: MCP standardizes how an agent reaches context, not whether that context is current or whether two tool calls in the same reasoning step saw the same state.
TL;DR: An MCP server is a program that exposes capabilities — tools, resources, and prompts — to an AI agent over the Model Context Protocol, so any compliant client can use them without a bespoke integration. It handles discovery, invocation, and transport, and it does that job well. What it does not do is make any claim about the quality of what it returns. The protocol has no notion of freshness, and no notion of a consistent snapshot across calls. A server fronting five backends returns five different moments, and nothing in the message envelope tells the agent that happened. That is the context gap, and it lives underneath MCP, not inside it.
What an MCP Server Actually Is
An MCP server is a program that exposes a set of capabilities — executable tools, readable resources, and reusable prompts — to AI applications over the Model Context Protocol, a JSON-RPC-based standard. The server describes what it can do in machine-readable form; any compliant client can discover those capabilities at runtime and invoke them without integration code written for that specific pairing.
The word “server” is doing less work than usual here. An MCP server is often not a network service at all — it can be a local process a desktop client launches as a subprocess and talks to over stdin and stdout, or a Python file of eighty lines. The term describes a role in a protocol conversation, not a deployment shape.
Three roles make up the architecture. The host is the AI application the user interacts with — an IDE assistant, a chat client, an agent runtime. The client lives inside the host and maintains exactly one connection to one server, so a host running four servers runs four clients. The server exposes capabilities and answers requests.
That one-client-per-server rule matters more than it looks. Each connection is an isolated session with its own capability negotiation and its own state. Servers do not talk to each other, and nothing in the architecture gives two of them a shared view of anything — a fact that will matter further down.
The Three Primitives: Tools, Resources, and Prompts
Everything an MCP server exposes falls into three categories, and the distinction is about who decides to use it.
Tools are the primitive people mean when they say MCP. A declaration carries a name, a description written for a model rather than a human, and an input schema. The description is load-bearing: a vague one produces a tool the model never picks, or picks wrongly.
Resources are the quieter half, and frequently underused. Being application-controlled and read-only, they can be attached to context without the model spending a reasoning turn deciding to fetch them. Schemas and reference documents belong here rather than behind a tool call. Prompts are user-initiated workflows — the least used of the three, and the one most likely to be missing from a server built only for tool calling.
Primitive
Controlled by
What it is
Example
Tools
The model
A function the agent can call, with a JSON Schema for its inputs. Can have side effects.
create_ticket, run_query, send_email
Resources
The application
Read-only data addressed by URI, loaded into context. No side effects.
file:///repo/README.md, db://orders/schema
Prompts
The user
Parameterized templates the user invokes deliberately, often as a slash command.
/summarize-pr, /review-incident
How an MCP Server Works: The Request Flow
The lifecycle is short, and most integration bugs live in the first two steps rather than the interesting one.
Initialize. The client sends an initialize request carrying its protocol version and capabilities. The server responds with the version it will speak and what it offers. The client sends an initialized notification. Only now is the session live.
Discover. The client calls tools/list, resources/list, and prompts/list, and the server returns declarations including the JSON Schema for each tool’s arguments. This is where MCP earns most of its value — the agent learns what exists at runtime, so adding a capability to a server makes it available to every connected client without redeploying any of them.
Invoke. The client sends tools/call with the tool name and an arguments object. The server executes — hits a database, calls an internal API, reads a file — and returns content blocks: text, images, or embedded resources. The host feeds the result back into the model’s context and the model continues reasoning.
Notify. If it declared the capability, the server can push one-way notifications: a resource changed, the tool list changed, a log line.
Underneath all of it is JSON-RPC 2.0. Nothing exotic in the wire format, which is the point of choosing it.
Transports: stdio, HTTP, and Why the Choice Matters
MCP defines two standard transports, and the operational difference between them is larger than the protocol difference.
stdio. The client spawns the server as a child process and exchanges newline-delimited JSON over stdin and stdout. No ports, no auth, no network. The server inherits the user’s machine and credentials, and its lifetime is the client’s. This is the right transport for local filesystem and developer tooling, and why so many MCP servers ship as a single command.
Streamable HTTP. The server is a real HTTP endpoint. The client POSTs JSON-RPC messages to it, and the server replies with one JSON response or upgrades to a Server-Sent Events stream. This is the transport for remote, multi-user deployments, and where the hard questions live: authentication, per-user authorization, session identity, multi-tenancy. MCP defines an OAuth 2.1-based authorization framework here, but the server still owns the part that matters — scoping every tool call to the identity behind it. A stdio server that assumes one user and ambient credentials does not become a remote one by swapping the transport.
What MCP Standardizes — and What It Deliberately Doesn’t
Before MCP, connecting agents to systems was combinatorial: every AI application needed its own integration to every internal system. MCP collapses that into one interface — write the server once and any compliant client can use it. It is the argument that made the Language Server Protocol the default in editor tooling, and it works for the same reason: the standard describes the shape of the conversation and stays out of the semantics of what is being said.
Which is also the source of its limitation. Read the specification looking for what it guarantees about returned data and you will find nothing, because there is nothing to find. That is not an oversight; a transport protocol should not have opinions about the freshness of your inventory count. But it means the guarantees an agent actually needs are all somebody else’s job:
Nothing says a tool result is current. A tools/call response is a content block. No freshness field, no as-of timestamp, no staleness indicator. A result computed from a cache refreshed nine minutes ago and one read from a live row are identical in shape.
Nothing correlates two calls. Each tools/call is an independent request with its own id. No snapshot handle, no transaction identifier, no way for a client to say “resolve these three against the same version of the world,” and no way for a server to say it didn’t.
Nothing spans servers. Two servers are two sessions with no shared clock and no shared state, and neither knows the other was consulted.
None of this is a defect in MCP. It is a scoping decision, and the right one. The trouble is that “standardized context protocol” reads as a claim about context quality when it is a claim about context plumbing.
Five Backends, Five Different Moments
A typical enterprise MCP server is a façade: eight tools, and behind them a transactional database, a cache of precomputed counters, a vector index, a warehouse, and a search cluster. This is good engineering — the server is exactly the abstraction layer it should be. It is also where the problem enters.
Consider a support agent handling a refund. It makes three calls in one reasoning step:
get_account(id) reads the transactional store. Current to the millisecond.
get_risk_signals(id) reads a counter maintained by a streaming pipeline — seconds behind, because the pipeline has to move a transaction through ingestion and aggregation before it lands.
find_similar_cases(text) hits a vector index rebuilt on a schedule. Minutes behind.
Three tool calls. Three successful responses. Three different moments, assembled into one prompt describing a world that never existed. A charge that committed forty seconds ago is present in the balance, absent from the risk signals, and invisible to retrieval.
Nothing throws. Every backend answered its own question correctly, and the MCP layer did its job perfectly — three calls routed, three results returned. The agent, which has no way to know any of this, reasons flawlessly on top of an incoherent picture and produces a decision as confident as a right one.
This is the context gap: the decision executes on context that is incomplete, inconsistent, or outdated at execution time. Adding a sixth backend adds a sixth moment. The composed stack widens the gap rather than closing it, because each system it adds is a separate clock and no two agree.
It gets worse under concurrency, the normal operating condition for agents. Ten agents in parallel each read the same lagging counter, each see the same calm number, each approve. By the time the counter reflects the first decision, the other nine have committed. The pipeline lag is wider than the validity window — the span in which the context the decision acted on was still true.
What the Backend Behind an MCP Server Should Provide
If the protocol can’t give you coherence, the data layer has to. The requirement states in one sentence: the tool calls an agent makes in a single reasoning step should resolve against the same coherent view of state, across every retrieval pattern they need. Not the same system in the loose sense of a shared gateway — the same committed state, so the balance, the aggregate, and the semantic lookup all describe one moment.
That means the layer under your MCP server has to serve retrieval patterns usually split across four products: point lookups on structured records, range scans over history, aggregations that resolve inside the decision’s window instead of trailing a pipeline, and similarity search over embeddings. In one system, three tool calls hit one consistent snapshot instead of three clocks. In four, no amount of protocol design puts the moments back together.
This is what we build at Tacnode: real-time, multi-modal context infrastructure serving structured, derived, and semantic retrieval from one internally coherent view. The Tacnode Context Lake™ is PostgreSQL-compatible, so an MCP server fronting it fronts one endpoint rather than a fan-out across five. Derived signals — velocity counts, rolling aggregates, risk tiers — are maintained as incremental materialized views that converge within sub-second of the underlying change rather than rebuilding on a pipeline cadence; aggregations you would rather not precompute resolve on demand against the same data.
Two scoping notes, because architects ask them first. In the primary pattern, Tacnode reads from your existing system of record by change data capture and is not in its write path — your transactions commit exactly where they do today. What changes is that the decision gets accurate context, so the agent does not approve what it would have blocked had it seen current state. View maintenance is asynchronous with sub-second lag, not synchronous at commit — which closes the gap for the reasoning step, not for the commit. Ten agents racing inside the same sub-second window still need a serialization point somewhere. When the state being contended is state the agents themselves write — a budget, a limit, a reservation — Tacnode can own that dataset authoritatively, with full ACID semantics, and the conflict is resolved rather than discovered later.
Your MCP server keeps its tool list, its schemas, its descriptions. What changes is what sits behind the tools — and the three calls in a reasoning step finally agree about what time it is.
MCP solved the integration problem, and solved it well. It never claimed to solve the coherence problem. That one is still yours.
Frequently Asked Questions
MCPAI AgentsAgent ToolingData ArchitectureContext Lake