The Model Context Protocol is everywhere right now. Everyone is building MCP servers, connecting tools to agents, and exposing internal systems to AI clients.
What gets less attention is the architecture behind those servers.
An MCP server is not just a template you set up once and forget. Depending on the use case, the right structure can be completely different. A thin wrapper around an API, a local resource server, and an orchestration layer for long-running jobs all expose capabilities through MCP, but they should not be designed the same way.
I have seen six patterns emerge in practice. This is not an academic classification. It is a decision aid:
Which MCP server shape fits the problem?
The Model Context Protocol is an open protocol for connecting AI applications to external context and capabilities.
An MCP server can expose three main kinds of primitives:
In practice, most people start with tools because they are the most visible part of MCP. But a good MCP server is not just a bag of functions. It is a boundary between an AI client and a system that has data, behavior, permissions, and risk.
The important question is not only:
What can the agent call?
It is also:
What should the client expose, in what shape, with what permissions, and at what level of abstraction?
This is the simplest and most common pattern. One MCP tool maps closely to one existing API endpoint. The server is mostly a thin adapter between the MCP client and an existing service.
AI Client
|
MCP Tool: get_user(id)
|
REST API: GET /users/{id}
When it fits:
The API is stable, well documented, and already close to what the model needs. You want quick integration without adding much domain logic to the MCP server.
When it does not fit:
The API exposes low-level data that the model still has to join, filter, interpret, or clean up. Then the complexity moves into the prompt and the model has to act like an API integration layer. That is fragile.
Typical examples:
Main risk:
Tool bloat. If every endpoint becomes a tool, the model has too many similar options and too much raw API detail to reason about.
Design rule:
Use direct wrappers for small, obvious, low-risk API operations. Move to task-level tools when the agent has to combine or interpret several raw endpoints.
A composite MCP server hides multiple backend calls behind one task-level tool. The agent asks for the result it actually needs, and the server performs the aggregation.
AI Client
|
MCP Tool: get_order_summary(order_id)
|
+-- REST API: GET /orders/{id}
+-- REST API: GET /customers/{customer_id}
+-- REST API: GET /products/{product_id}
When it fits:
The agent repeatedly needs the same combination of data for one task. Without this pattern, it would make several tool calls and assemble the result itself.
When it does not fit:
The required combination changes constantly. If every task needs a different shape, a fixed composite tool can become too rigid.
Typical example:
A customer service agent that needs order data, customer profile, delivery status, and product information together.
Main risk:
The composite tool can become a hidden mini-application. If it starts making too many assumptions, the agent loses flexibility.
Design rule:
Return task-ready context, not a full backend dump. The output should be smaller, clearer, and safer than the raw data sources.
Not every capability should be a tool. Sometimes the best MCP server exposes structured resources that the client can read and inject as context.
AI Client
|
MCP Resource: file://project/README.md
MCP Resource: db://customer/123/profile
MCP Resource: config://service/payment
|
Local or remote data source
When it fits:
The agent needs context more than action. The data is read-only or should be treated as reference material.
When it does not fit:
The model needs to execute a parameterized operation, mutate state, trigger a workflow, or perform a search that has meaningful side effects or cost.
Typical examples:
Main risk:
Overexposure. Raw resource access can accidentally reveal too much. A filesystem server, for example, should not expose the entire machine by default.
Design rule:
Expose curated resources with clear roots, metadata, and permission boundaries. Prefer readable views over unrestricted raw access.
In this pattern, the MCP tool does not call a normal API. It delegates to another AI agent, model, or specialized reasoning component.
Main AI Client
|
MCP Tool: analyze_code(snippet)
|
Specialized Analysis Agent
When it fits:
A subtask benefits from a different model, narrower context, specialized tools, or a controlled reasoning workflow.
When it does not fit:
The task is simple enough for the main model. Additional agent hops add latency, cost, and debugging complexity.
Typical examples:
Main risk:
Loss of traceability. Once one agent calls another, failures become harder to explain. The main model sees a result, but not necessarily the reasoning quality behind it.
Design rule:
Use agent-backed tools when the subagent produces a bounded, verifiable result. Return evidence, confidence, and limitations where possible.
Some work should not happen inside a synchronous MCP tool call. The operation may take minutes, involve queues, or run in a background service.
In this pattern, the MCP server exposes a control surface over asynchronous infrastructure.
AI Client
|
MCP Tool: start_report_generation(params)
|
Event Queue / Job Scheduler
|
Report Service
|
MCP Tool: get_report_status(job_id)
MCP Resource: report://jobs/{job_id}/result
When it fits:
The operation is too slow or too expensive for a normal request-response call. The agent should start the job, receive a job ID, and check status later.
When it does not fit:
The operation is fast and deterministic. Adding a queue for a 200 ms call is unnecessary overhead.
Typical examples:
Main risk:
Ambiguous state. If the agent cannot tell whether a job is queued, running, failed, completed, or expired, the workflow becomes unreliable.
Design rule:
Make job state explicit. Provide status, progress, failure reason, result location, cancellation, and retry semantics.
In larger systems, one MCP server may act as a gateway in front of several domain-specific servers or services. This is not a special MCP primitive. It is an architectural pattern.
Many clients can connect to multiple MCP servers directly. A gateway is useful when you need shared concerns in one place.
AI Client
|
MCP Gateway: Auth, Routing, Logging, Policy
|
+-- Inventory MCP Server
+-- Order MCP Server
+-- Customer MCP Server
When it fits:
The system has many domains, teams, or permission boundaries. You need central authentication, routing, audit logging, tool filtering, or rate limiting.
When it does not fit:
The system is small. A gateway then becomes an unnecessary layer between the client and a few simple tools.
Typical examples:
Main risk:
The gateway becomes a bottleneck or a second platform. If every domain change requires gateway changes, team independence disappears.
Design rule:
Use a gateway for cross-cutting policy, not for domain logic. Domain ownership should stay with the domain servers.
Local resource access is often treated as its own category because it feels different from API integration. A local MCP server may read files, inspect repositories, query a local database, or expose an Obsidian vault.
Architecturally, local access usually belongs to one of two patterns:
The key issue is permission scope. Local access can be extremely powerful. A good local MCP server should restrict roots, filter results, avoid leaking secrets, and make dangerous actions explicit.
These questions help choose the right pattern:
| Question | Best fit |
|---|---|
| Do I have a simple existing API operation to expose? | Direct API Wrapper |
| Does the model need one task-level result from several sources? | Composite Service |
| Is the capability mostly read-only context? | Resource-Oriented MCP |
| Does the task need another model or specialized reasoning context? | Agent-Backed Tool |
| Does the operation run asynchronously or take too long for one call? | Event-Driven Control Surface |
| Do many teams or domains need one shared policy boundary? | Gateway or Federated MCP |
The patterns are not mutually exclusive. A production system often combines them:
Gateway MCP
|
+-- Composite tools for customer support
+-- Resource-oriented server for documentation
+-- Event-driven tools for report generation
+-- Agent-backed tools for specialized analysis
The more important decision is where complexity should live:
As a rule, complexity that is deterministic, security-sensitive, or repetitive should live outside the prompt.
Before exposing a capability through MCP, ask:
MCP makes integration easier. It does not make trust boundaries disappear.
Building an MCP server is easy. Building the right MCP server requires a conscious decision about shape, abstraction level, and risk.
Start with the simplest pattern that satisfies the requirement:
The best MCP server does not expose everything the backend can do. It exposes the smallest stable capability that helps the model complete the task without giving it unnecessary power or unnecessary raw data.
That is the real architecture decision.