MCP Architecture Guide
How to design, deploy and test an MCP server: primitives, Streamable HTTP, the two protocol eras, domain-scoped servers and a 20-point acceptance test matrix.

This guide covers the decisions that shape an MCP server before and after the first tool works: which primitives to use, which transport and protocol versions to support, how to split servers, how to deploy and version them, and what to test before anyone relies on them. Protocol details come from the MCP specification as of October 2026. The diagram below is a reference design; we validate the APIs, permissions and identity set-up for each implementation.
The building blocks
MCP is an open protocol based on JSON-RPC 2.0. It is not a model and not an LLM API: it only standardises how AI applications get context and actions from external programs.
- Host: the AI application, such as a chat app, IDE or agent platform.
- Client: created by the host, one per server, each with a dedicated connection.
- Server: a program that exposes three primitives.
- Tools: functions the model can call, each with an
inputSchema(JSON Schema, 2020-12 dialect by default). - Resources: data addressed by URIs, read with
resources/read. Custom URI schemes are allowed if they follow RFC 3986. - Prompts: reusable templates that the user chooses.
Clients can offer elicitation. Sampling, roots and logging are deprecated in 2026-07-28, so avoid designing around them. For a diagram of the roles, see the MCP host, client and server architecture.
Transports
A server runs locally over stdio or remotely over Streamable HTTP. For remote servers:
- The server exposes a single endpoint path, such as
/mcp. - Every client message is a new HTTP POST, and the client's
Acceptheader lists bothapplication/jsonandtext/event-stream. - The server may answer a request with plain JSON or with an SSE stream. Clients must support both, so a simple server may always answer with plain JSON.
- Accepted notifications get 202 Accepted with no body.
- The server must validate
Originand answer 403 when it is present and invalid, to block DNS rebinding. - JSON-RPC batching was removed in 2025-06-18: one message per POST.
Two protocol eras
The spec calls revisions up to 2025-11-25 "legacy"; the current revision is 2026-07-28.
Legacy and 2026-07-28 compared
| Topic | Legacy (2025-11-25 and earlier) | 2026-07-28 |
|---|---|---|
| Start-up | initialize, then notifications/initialized | No handshake; mandatory server/discover |
| State | Optional MCP-Session-Id sessions | No protocol sessions; list results must not vary per connection |
| Per-request data | MCP-Protocol-Version header after initialisation | _meta with protocol version and client capabilities on every request, plus Mcp-Method and Mcp-Name headers |
| Caching | No cache fields in list results | ttlMs and cacheScope on list and read results |
| Liveness | ping | Removed |
| Resource not found | -32002 | -32602 |
Which era to target is a judgement, not something the spec states. The TypeScript SDK v2 client and the MCP Inspector default to the legacy handshake, while the Python client probes server/discover first and falls back. Supporting both eras on one endpoint is allowed: an initialize request selects legacy behaviour, and requests carrying _meta are served statelessly. That is what we did on the GeekMidst MCP server.
Server design
- One server per domain. Separate adapters for, say, knowledge, CRM records and marketing operations, so each has its own permission review, credentials and kill switch.
- Business logic separate from transport. Tool handlers call plain functions that you can unit test without an AI client. The JSON-RPC layer only parses, validates, dispatches and formats.
- Narrow tools with strict schemas. Name operations after business tasks, reject unknown fields with
additionalProperties: false, and set limits on strings and page sizes. Never offer unrestricted SQL or database credentials. - Use the two error channels correctly. An unknown tool is a protocol error (
-32602). Validation, API and business failures are tool results withisError: trueand a message the model can act on. - Honest annotations. Set
readOnlyHint: trueon read tools. Remember defaults:readOnlyHintis false anddestructiveHintis true, and clients must treat annotations as untrusted hints. - Structured output. When you return
structuredContent, also return the serialised JSON in a text block for older clients.
Reference MCP deployment: knowledge and action servers
Reference architecture: a reusable pattern, not a live client integration
Select a component to see what it is responsible for and how data moves to and from it.
Components and data flows (text version)
| Component | Type | Responsibility |
|---|---|---|
| User | People | Asks questions, chooses prompts and confirms any action that would change data. |
| AI hostchat app, IDE or agent | AI and agents | The AI application. It runs the model, creates one MCP client per server and decides when to ask the user before a tool runs. |
| MCP client: knowledge | AI and agents | Keeps a dedicated connection to the knowledge server, discovers its tools and resources and sends requests. |
| MCP client: actions | AI and agents | Connects to the actions server. Obtains an OAuth access token for the signed-in user and sends it on every request. |
| HTTP edgeTLS, origin check, rate limit | Identity, policy and governance | Terminates TLS, rejects invalid Origin headers with 403, applies rate limits and request size limits before the server code runs. |
| Authorization serverOAuth 2.1 with PKCE | Identity, policy and governance | Your identity provider. Issues short-lived tokens whose audience is the MCP server, with the minimum scopes for the user's role. |
| Knowledge serverread-only tools and resources | Integration layer | Searches and returns approved documentation as cited passages and URI-addressed resources. Has no write path. |
| Actions servernarrow tools, OAuth resource server | Integration layer | Validates each token's audience and scope, validates input against the tool schema, then calls one domain API. Write tools are few and named. |
| Approved content store | Data and analytics | Published documentation, policies or product knowledge that has been cleared for assistant use. |
| Business system APIone domain per server | External or custom system | The system of record. Its own permissions, limits and validation still apply to every call. |
| Audit log and metrics | Data and analytics | Records caller, tool, outcome and timing for every call. Sensitive arguments are excluded or masked by default. |
Data flows
- User → AI host: question or prompt. The user asks a question or picks a server prompt. Prompts are user-chosen templates, not instructions the server can inject.
- AI host → User: confirm action. Before a write tool runs, the host shows the call and its arguments. If the user declines, nothing is sent.
- AI host → MCP client: knowledge: tool and resource calls. The model chooses tools from the discovered list; the host routes the call to the right client.
- AI host → MCP client: actions: confirmed tool calls. Only calls the host is allowed to make, after any confirmation, reach the actions client.
- MCP client: actions → Authorization server: OAuth 2.1 + PKCE. Authorisation code flow with PKCE and the resource parameter naming the MCP server. On failure the user is asked to sign in again.
- MCP client: knowledge → HTTP edge: Streamable HTTP POST. One JSON-RPC message per POST. Rejected requests get a 4xx and the client surfaces the error.
- MCP client: actions → HTTP edge: POST with bearer token. The token travels in the Authorization header on every request, never in the URL. Missing or expired tokens get 401.
- HTTP edge → Knowledge server: validated request. Requests that pass origin, size and rate checks are forwarded. Over the limit, the edge answers 429.
- HTTP edge → Actions server: validated request. Same checks, plus routing to the server that owns the domain.
- Knowledge server → Approved content store: read approved content. Read-only queries against the content index. If the index is down, the tool returns isError with a clear message.
- Actions server → Business system API: scoped API call. Uses the server's own credentials or a token issued for the upstream API, never the client's MCP token. Timeouts return a tool error.
- Actions server → Audit log and metrics: audit event. Every call writes who, what, outcome and timing. If audit writing fails, write tools should fail closed.
- Knowledge server → Audit log and metrics: request log. Anonymous or pseudonymous request metrics for capacity and abuse detection.
Deployment
- Stateless where possible. Without sessions, any instance can answer any request, which suits load balancers and serverless hosting.
- Secrets stay server side. Upstream API credentials live in the server's secret store, never in tool arguments, prompts or responses.
- Sandbox first. Point new servers at sandbox or test environments, with test users whose permissions match real roles.
- Protect with OAuth when data is not public. The server becomes an OAuth 2.1 resource server and publishes RFC 9728 protected resource metadata. The detail is in MCP security and governance and MCP security and authorisation.
Versioning
Three versions move independently: the protocol revision, the SDK, and your tool contract. Pin SDK versions and read their changelogs before upgrading. Treat a tool's name and input schema as a public API: add optional fields freely, but publish a new tool name for breaking changes and keep the old one for a stated period. The spec keeps deprecated features for at least 12 months, which is a sensible model for your own tools.
Testing matrix
These 20 acceptance tests should pass in every environment before go-live, and again after each SDK or protocol upgrade.
MCP server acceptance tests
| Test | What to check |
|---|---|
| Startup and health | The server starts cleanly and a basic request succeeds through the real HTTP path |
| Version compatibility | Supported versions are negotiated; unsupported ones are rejected with the documented error |
| Capability discovery | initialize and server/discover report only capabilities that work |
| Tool discovery | tools/list returns every tool with a valid schema, in a stable order |
| Resource access | Listing, templates and reads work; a missing resource returns an error, never empty contents |
| Prompt discovery | Prompts list and render; a missing required argument returns -32602 |
| Input validation | Wrong types, unknown fields and oversized values are rejected as tool errors |
| Authentication | No token, an expired token or a token for another audience gets 401 |
| Authorisation | A valid token without the needed scope gets 403 with insufficient_scope |
| Tool execution | Each tool returns correct results for known test data |
| Read-only boundaries | Read tools cannot change data, whatever arguments are sent |
| Human confirmation | Write tools run only after the host's confirmation step |
| Error handling | Upstream failures become clear tool errors without stack traces |
| Rate limiting | Excess requests are slowed or refused without affecting other users |
| Audit logging | Every call produces an audit record with caller, tool and outcome |
| Timeouts and retries | Slow upstreams time out; retries do not duplicate writes |
| Upstream unavailable | The server stays up and reports the outage clearly |
| Secret protection | No credentials appear in responses, logs or error messages |
| Cross-user isolation | One user's token never returns another user's data |
| Client compatibility | The SDK clients, MCP Inspector and your target hosts connect and call tools |
MCP Inspector 2.12.0 has a CLI mode that makes many of these tests scriptable, for example npx @modelcontextprotocol/inspector --cli https://example.com/mcp --method tools/list, with --protocol-era legacy|auto|modern to test each era.
Questions we hear
Should we build for 2026-07-28 only?
Do we need SSE?
How many tools should one server have?
Where do vendor servers fit?
Related
MCP server development
Design, build and test a server with this checklist
→MCP integration consulting
Vendor vs custom servers, governance and rollout
→MCP security and governance
Threats, OAuth and enterprise controls
→MCP vs direct API integration
When an MCP layer is worth it
→Sources
- https://modelcontextprotocol.io/docs/learn/architecture
- https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http
- https://modelcontextprotocol.io/specification/2026-07-28/changelog
- https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning
- https://modelcontextprotocol.io/specification/2026-07-28/server/tools
- https://www.npmjs.com/package/@modelcontextprotocol/inspector
Review your MCP design
Send us your planned tools and deployment. We will check them against this matrix and suggest changes.




