Merit ACClaude architecture
Guide · agent architecture

Claude and MCP: the Model Context Protocol as an integration pattern

Every application that connects Claude to something outside itself (a database, a filesystem, an internal API, a search index) has to solve the same problem: how the model discovers what's available, and how a call from the model reaches the external system safely. The Model Context Protocol (MCP) is Anthropic's open answer: a standard interface between a client (the application or agent harness embedding Claude) and a server (something exposing tools, data, or prompt templates). This guide covers the architecture the standard makes possible. It applies whether the server is one your team wrote in an afternoon or a widely used connector someone else maintains.

1. The problem before a standard protocol

Before a shared protocol, connecting a model to an external tool meant custom code for that tool, written against one application's conventions for defining, invoking, and returning tools. A team building a support bot wrote an order-lookup integration against the order database. A team building an internal coding assistant wrote a second, unrelated one against the same database, with different argument shapes, different error handling, and different assumptions about what the harness expected back. Across an organization that's N tools times M applications, each cell a one-off integration that can't be reused even when the underlying tool never changed.

MCP collapses that grid. It defines how a server describes what it exposes (tools, data, prompt templates) and how a client discovers and uses that surface, regardless of which application embeds the client or which model it uses. A server built once against the protocol works with any MCP-compatible client. The integration becomes a property of the tool instead of a property of one application.

Be precise about what MCP standardizes. It does not standardize what a tool does, how well, or whether it's safe to call; that's still up to whoever built the server. It standardizes the conversation: how a client asks what's available, how it invokes something, and how results and errors come back. That narrower claim is enough to remove the N-times-M problem, because the interface was the part being reimplemented every time.

2. Core architecture: client and server

MCP splits responsibility along one line. A server exposes a defined set of capabilities over the protocol. A client, embedded in the application or agent harness running Claude, discovers what each connected server offers and mediates between the model and that server. The server doesn't need to know whether it's called from a coding agent, a chat app, or a research assistant. The client doesn't need per-server code, because every server speaks the same protocol.

An MCP server is a small program: a thin wrapper around an existing API, a database connection with a fixed set of allowed queries, or a purpose-built service. It answers a defined set of protocol messages: "what do you offer," "run this tool with these arguments," "give me this resource," "here's a prompt template, fill it in." The MCP client lives inside the harness. On startup it asks each connected server what it exposes, translates that into the model's tool-calling format, routes the model's tool calls to the right server, and brings results back into the conversation. The harness (the agent loop that calls the model, handles its responses, and loops until the task is done) sits around the client and needs no per-system logic; it speaks MCP once.

This is an ordinary networking client-server split. The protocol doesn't care which model sits behind the client, and nothing in it is exclusive to Claude. It matters here because Claude's agentic surfaces (the API's tool-use mechanics and harnesses built on them) act as MCP clients, so the reuse benefit, trust questions, and local-versus-remote tradeoff below apply directly to systems built with Claude.

3. Tools, resources, and prompts: the three things a server can expose

MCP defines three kinds of things a server can offer. Keep them distinct. A common source of over-engineered servers is exposing everything as a tool when a resource would be simpler and cheaper.

Tools. Callable actions with a defined input schema and output shape. Conceptually the same as a function-calling tool in a direct API integration, exposed over MCP's transport instead of defined inline in one application. Use a tool for anything that does something or needs arguments to know what to fetch: "create a ticket with this title and description," "run this query with these parameters," "send this message to this channel." The model decides when to call it and with what arguments; the server decides what happens.

Resources. Read-only data a client can attach to a conversation (a file, a database record, a configuration document) without the model constructing a tool call. Use a resource for content that exists independently of any request and needs no arguments to identify: a project's README, a customer record by known ID, a log file. A tool models an action; a resource models context the application can choose to include.

Prompts. Reusable, parameterized prompt templates a server offers to any connected client. They package "here's a tested prompt for doing X, fill in these slots" so the prompt engineering lives with the server's author, who knows the underlying system best, and isn't re-derived by every application. This is the least used of the three in practice. It matters for the same reason as the other two: it moves something that was duplicated per application into one maintained place.

Rule of thumb: if the model needs to decide per turn whether and how to invoke something with specific arguments, it's a tool. If it's useful context that needs no per-call arguments, it's a resource. Making static or slowly changing data a tool adds a needless decision to the model's action space and a needless round-trip to fetch something that could have been attached directly.

4. The real benefit of decoupling

The payoff of the client-server split is ownership. A team that owns an internal system (a ticketing platform, a data warehouse, a deployment pipeline) can build and maintain one MCP server for it, and every MCP-compatible agent in the organization can use it without that team learning or supporting each agent framework. Before MCP, "let people build agents against our system" meant maintaining one integration contract per consuming team's framework, a support burden that grew with the system's internal popularity.

MCP servers are also a unit of reuse across organizations. A server for common infrastructure (version control, project tracking, a class of database) can be written once and used by any team at any company running an MCP-compatible client, the way a good library gets reused across unrelated projects. The integration effort is paid once and amortized across every future consumer.

The decoupling has a cost. A server meant for unknown future clients must be more conservative about its interface than a tool built for one application. It can't assume anything about how a harness manages context, retries, or error presentation. A good general-purpose MCP server takes more discipline than a single-caller tool, for the same reason a published library's API deserves more care than a private helper.

5. Trust boundaries and authorization

Connecting to an MCP server, especially one your team didn't build, means trusting claims you usually can't verify from the client: that its tools do what their descriptions say, that it touches only the data it claims to, and that its internal authorization enforces what it says. That's the same kind of trust any third-party API requires. MCP makes third-party tool integration easy enough that teams reach for it more often, so the trust question comes up more often.

The governing principle is the same as for tool design generally: enforce authorization independently of what the model requests, and never infer it from the model's assurance that a call is safe. That applies equally to enforcement inside an MCP server. A server that exposes a delete_record tool and trusts whatever arguments the model supplies, because the model is "following the user's instructions," has the same authority gap as a custom tool built the same careless way. The protocol doesn't add a check nobody designed in. See The ten disciplines of governed agentic DevSecOps, specifically the architecture-rule and tool-design disciplines, for why that separation matters and how to get it right. Everything there about a custom tool's internal enforcement applies unchanged to an MCP server's.

Ask this before adding any MCP server to an agent's toolkit: if this server's authorization checks are wrong or missing, what's the blast radius, and is that bounded by the server's own design or by trusting the model to ask only for things it should? If it depends on the model's judgment instead of a check the server enforces regardless of what's asked, that's the architecture gap discipline 1 of the DevSecOps guide describes, relocated to a server you may not have written.

Give an unfamiliar MCP server the same scrutiny as any third-party code that will run with access to your data or systems. Reading what its tools claim to do doesn't verify what they do. A server description is text the server chose to publish about itself.

6. Local vs. remote MCP servers

MCP servers come in two deployment shapes. The choice changes what the server can be trusted to do and what operational properties your application inherits.

A local server runs as a process on the client's machine. Typical uses are filesystem access, local developer tools, and resources that only make sense on one machine (a local Git repository, a local build tool, files on disk). It usually talks to the client over a low-latency local channel instead of a network protocol, and it inherits the permissions of the process it runs under. No separate deployment and no network round-trip, but its blast radius is whatever that local user account can touch. Keep that in mind before pointing a local filesystem server at anything more sensitive than a scratch directory.

A remote server runs elsewhere and is reached over the network. It fits a shared internal system (a ticketing platform, a shared database) that many clients on many machines need to reach the same way. Remote servers bring the usual costs of a networked service: latency on every call, the server's availability becoming a dependency of every application using it, and a network-trust boundary. The server is a separate, independently operated system reachable by anything else that can reach it, and its authentication and network exposure become part of what you trust when you connect.

Neither is better in general. Use a local server when the resource exists only locally and only one client needs it. Use a remote server when the resource is shared infrastructure that many clients and users must reach consistently, and that consistency is worth the cost of running and securing a network service. Picking wrong shows up later as wasted operational overhead (a network service for something that only runs on one laptop) or an integration that gets reimplemented four more times because nothing else could reach it (a local-only integration that five teams needed).

7. Worked example: custom tool integration versus a standalone MCP server

The practical question is narrower than "should we use MCP": for this integration, is a custom tool built into our application enough, or does it deserve a standalone MCP server others can reuse? Don't default to MCP everywhere.

Scenario: a customer-support agent needs to look up order status from the
company's internal orders database.

Option A — a custom tool, built directly into the support agent's own
application code:
  - Fast to build: one function, one schema, wired directly into this one
    agent's tool list.
  - No protocol overhead, no separate process or service to run and monitor.
  - Only usable by this one application. If the billing team's agent, or the
    internal ops dashboard, later needs the same order lookup, they each
    write their own version against the same database — and now there are
    three integrations to keep in sync with the schema, not one.

Option B — a standalone MCP server for the orders database, maintained by
the team that owns that system:
  - More upfront work: a real service, with its own deployment, its own
    authorization checks independent of any calling agent, its own
    versioning as the database schema evolves.
  - Usable immediately by any MCP-compatible client — the support agent, the
    billing team's agent, the ops dashboard — without the orders team
    needing to understand any of those callers individually.
  - The orders team now owns one authorization surface to secure and audit,
    instead of trusting that every application team that reimplements
    "query the orders table" got the authorization checks right on their
    own.

Ease of building rarely decides it; a custom tool usually wins that, especially the first time. Two questions decide it: will more than one consumer realistically need this integration, now or soon, and is the exposed system sensitive or complex enough that one owning team should handle its authorization and maintenance instead of N teams reimplementing it? A one-off script that reads a config file for a single agent almost never earns an MCP server. A shared system with access-control implications that several teams want to build agents against almost always does. If you build it as a custom tool anyway, the second team asking for the same access forces the decision later, with more integrations already depending on the unshared version.

Keep reading