Model Context Protocol: Architecture, System Design, and the Security Surface
A technical deep dive for architects, covering protocol internals, deployment topology, authorization, threat modeling, and the business consequences of each design choice.
1. Why MCP exists
Enterprise AI systems share an integration problem. Every model host (an IDE assistant, a chat application, an agent runtime, an internal copilot) needs to reach the same systems: ticketing, source control, CRM, cloud control planes, document stores, and internal APIs. Without a shared protocol, each host-to-tool pairing needs its own connector with its own authentication handling, schema conventions, and error model. With N hosts and M tools, that can mean up to N×M bespoke integrations.
Conceptually, MCP can move integration effort toward N+M reusable interfaces, although authorization, host differences, and operational requirements still introduce additional integration work. A tool is exposed once as an MCP server, and any compliant host can consume it. The protocol is an open specification built on JSON-RPC 2.0. It defines a small set of primitives, explicit capability negotiation, transport independence, and a standard authorization model for remote servers.
The useful architectural question is not whether MCP is a good abstraction. It is whether the protocol's trust model, failure semantics, and operational characteristics fit your requirements. The rest of this article works through that question layer by layer.
2. Architectural roles
Three roles should stay distinct in every design review.
- Host. The application the user works in. The host owns the user relationship, the model session, the consent UI, and the policy decisions about what the model can see and do.
- Client. A protocol connector instantiated by the host, paired one-to-one with a server. It handles the session lifecycle, capability negotiation, and message routing. It is plumbing, and it should not carry business policy.
- Server. Exposes capabilities (tools, resources, prompts). It may wrap a local process, a SaaS API, or an internal service. Most engineering effort and most security risk concentrate here.
The host is an important policy enforcement point; the gateway, MCP server, and downstream domain service must independently enforce their applicable security controls. The server must be treated as partially untrusted, even when your own team wrote it, because its outputs flow directly into the model's context window and can shape subsequent behavior.
3. Protocol foundations
3.1 Message model and handshake
All traffic is JSON-RPC 2.0: requests carry an id, responses echo it, and notifications carry no id and expect no response. Every session begins with an initialize exchange in which client and server agree on a protocol version and declare capabilities. The client then sends notifications/initialized to complete initialization before normal operation.
// Client → Server
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": { "roots": { "listChanged": true }, "elicitation": {} },
"clientInfo": { "name": "example-host", "version": "1.4.0" }
}
}
// Client → Server (notification, no id)
{ "jsonrpc": "2.0", "method": "notifications/initialized" }
The handshake is the first enforcement point. A host that requires structured output or a specific authorization revision should refuse servers that do not advertise it, rather than degrade silently. Protocol revisions are date-stamped; pin the versions your host supports, track the changelog, and test upgrades explicitly. Verify current revision details at modelcontextprotocol.io before committing to a design, because the specification evolves.
3.2 Primitives and who controls them
MCP separates capabilities by who controls their invocation. This is the most important design idea in the specification.
| Primitive | Controlled by | Typical use | Security concern |
|---|---|---|---|
| Tools | Model | Actions and queries | Side effects, injection, overbroad reach |
| Resources | Application | Documents, config, log excerpts | Data exposure, untrusted content |
| Prompts | User | Templated workflows, slash commands | Lower risk; review template contents |
| Sampling | Server requests, host approves | Server-side agent logic using host's model | Budget burn, behavior steering, context leakage |
| Roots | Client declares | Filesystem or URI boundaries | Boundary violations if not enforced |
| Elicitation | Server requests, user answers | Structured input mid-operation | Credential phishing through host UI |
3.3 Tool definitions are prompt surface
Hosts may supply tool names, descriptions, and JSON Schemas to the model; how much is included on each turn depends on host implementation and tool-selection strategy. A description can influence the model’s tool selection but must not be treated as a trusted system-level instruction. This makes descriptions a high-value injection target and means they deserve the same review, versioning, and testing discipline as code.
Annotations such as readOnlyHint, destructiveHint, and idempotentHint help hosts decide when to ask for confirmation. They are server-supplied hints, not guarantees. A host that makes authorization decisions solely on a server's self-declared readOnlyHint has delegated its security decision to the party it is supposed to constrain.
4. Transports
The transport determines most operational and security characteristics.
4.1 stdio
The host launches the server as a child process and exchanges newline-delimited JSON over standard input and output. There is no network exposure, but the server inherits the user's operating-system privileges. Installing a stdio server is effectively installing arbitrary code. Its supply chain (package registry, version pinning, provenance, signature verification) requires the same governance as any other software dependency.
4.2 Streamable HTTP
Introduced in the 2025-03-26 revision and replacing HTTP+SSE, Streamable HTTP is the transport for remote servers. The client POSTs JSON-RPC messages to a single endpoint. The server responds with a direct JSON body, or opens an SSE stream when it needs to send server-initiated messages or hold a long-running response. Servers may assign an Mcp-Session-Id at initialization; this header is not mandatory for stateless deployments. Clients should send the negotiated protocol version on subsequent requests where required by the selected specification revision.
- Statelessness is a decision, not a default. Subscriptions and in-flight elicitation require session affinity or a shared session store. Decide early, because it changes your scaling model.
- Session IDs are not credentials. They identify a session. Authorization comes from the bearer token on every request.
- Validate the
Originheader. Locally bound HTTP servers reachable from a browser are exposed to DNS rebinding unless they checkOriginand bind to loopback appropriately. - Resumability needs storage. If clients can resume streams, the server must buffer or replay events within a defined TTL.
5. The request lifecycle
Following one tool call end to end shows where state, failures, and trust boundaries sit.
- Discovery. The client calls
tools/listand caches the result. It must handlenotifications/tools/list_changed, because servers can alter their tool surface at runtime. - Context assembly. Tool definitions are included in each model request and consume tokens every turn. Large servers can occupy a meaningful share of the context window before the user has typed anything.
- Model decision. The model emits a call with arguments. The host validates those arguments against
inputSchemabefore dispatch. The model is never trusted to produce valid or safe input. - Policy gate. The host evaluates whether the tool is permitted for this user, workspace, and data classification; whether confirmation is required; and whether arguments fall within bounds such as a path inside a permitted root or a recipient on an allowlist.
- Dispatch. The client sends
tools/call. For remote servers, the request carries the bearer token for that server's audience. - Execution. The server performs the operation, using its own credentials or a token obtained through token exchange for each downstream dependency.
- Result. The server returns
contentand, in newer revisions,structuredContentconforming tooutputSchema, enabling deterministic parsing downstream.
5.1 Error taxonomy
Two error classes must stay separate. Protocol errors are JSON-RPC error responses: unknown tool, malformed request, or server fault. Tool execution errors return as a normal result with isError: true, so the model can observe the failure and recover. Conflating the two causes real bugs, because a protocol error is often handled by the client's retry logic while a tool error is something the model should reason about.
// Tool execution error: returned as a result the model can reason about
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [
{ "type": "text",
"text": "Rate limited by ticketing API. Retry after 30 seconds." }
],
"isError": true
}
}
5.2 Timeouts and idempotency
Timeouts are not solved by the protocol. Each server must define its own behavior for long-running operations and communicate progress, and the host must enforce a ceiling so that a hung tool cannot stall an agent loop indefinitely.
Idempotency is equally important. Models retry, and networks cause client retries. Any side-effecting tool should be idempotent by construction, accept an idempotency key, or be gated so that a duplicate invocation is harmless or detected.
6. End-to-end enterprise MCP execution sequence
This example uses an enterprise IdP, a remote MCP server, a gateway, and a domain API. It explicitly separates identity validation from operation authorization. The diagram shows a successful path; a denied policy decision stops execution before the MCP server invokes the ERP API.
| Boundary | Required enforcement |
|---|---|
| Host → gateway | Authenticated client, user intent, confirmation for sensitive actions |
| Gateway → MCP server | Tool allowlist, audience validation, rate limits, trace propagation |
| MCP server → domain API | Resource-level authorization, scoped downstream identity, transaction rules |
| Every boundary → audit | Correlation IDs, decision and outcome, payload minimization |
Failure handling: token expiration triggers reauthorization; an unavailable policy engine should fail closed for sensitive actions; a timed-out write requires reconciliation before retry; and audit failure must follow an explicit, risk-based availability policy.
7. Authorization for remote servers
The authorization model for HTTP-based servers uses OAuth-based mechanisms and related IETF standards, with exact requirements depending on the selected MCP specification revision. The elements an architect needs to understand are the following.
- Protected resource metadata (RFC 9728). The server publishes metadata naming its authorization server(s), so clients discover where to obtain tokens without hardcoded configuration.
- Authorization code flow with PKCE. Used for user-delegated access. The implicit flow and the resource-owner password grant are not appropriate.
- Resource indicators (RFC 8707). The client names the target MCP server in the token request, so the resulting token is audience-bound and cannot be replayed against a different server.
- Dynamic client registration. Eases onboarding but requires an authorization server policy governing which clients may register and which redirect URIs they may use. Enterprises typically restrict or pre-approve registration.
6.1 The rule most implementations break: no token passthrough
An MCP server must validate that tokens were issued for it, and it must not forward the client's token to downstream APIs. Passthrough breaks audience boundaries and turns the server into a confused deputy with access to everything the token reaches. The correct pattern is to obtain appropriate downstream credentials through approved delegation or token exchange where supported, or use a tightly constrained service identity; do not forward the MCP access token to a different resource audience.
Validation on every request should check signature, issuer, expiry, and audience. The following sketch shows the minimum checks in a gateway or server middleware.
def validate_mcp_token(token: str, expected_audience: str) -> Claims:
claims = jwt.decode(
token,
key=jwks_client.get_signing_key_from_jwt(token).key,
algorithms=["RS256", "ES256"], # never allow "none" or HS* with a public key
audience=expected_audience, # RFC 8707 resource indicator
issuer=TRUSTED_ISSUER,
options={"require": ["exp", "iat", "aud", "iss", "sub"]},
)
if claims.get("azp") not in APPROVED_CLIENT_IDS:
raise AuthError("client not approved for this server")
return claims
6.2 Identity model for agent actions
The MCP server can act in three identity modes, each with different audit, consent, and blast-radius properties.
| Mode | How it works | Audit | Blast radius |
|---|---|---|---|
| As the user (delegated) | Token exchange preserves the user's identity and scope | Attributable to the person | Bounded by the user's own access |
| As the application | Service principal with application permissions | Attributed to the app; user context lost | Often far exceeds any single user |
| Hybrid | User context carried for audit; app permissions for specific actions | Both user and app recorded | Controlled by per-action policy |
Application permissions granted to an MCP server are a common way to give an AI-accessible surface more reach than any individual user has. Prefer delegated access for anything touching personal or regulated data, and reserve application permissions for narrow, audited operations.
8. Security threat model
MCP's threat model differs from conventional API security because the consumer of the API is a language model. The model reads untrusted content and can be steered by it. The threats below belong in every design review.
- Indirect prompt injection. A tool that reads email, web pages, or tickets can return instructions that the model follows, causing other tool calls with attacker-chosen arguments. This is the most important threat class. The model cannot reliably separate data from instructions, so mitigation must be architectural: keep untrusted-content readers apart from privileged actions, require confirmation for privileged actions when untrusted content is in context, and restrict egress.
- Tool poisoning. A malicious or compromised server can place instructions in its tool descriptions, which are injected into every turn. Treat third-party metadata as untrusted input, review it at onboarding, and pin it.
- Rug pulls. A server approved as benign can change its definitions or behavior later. Pin tool definitions at approval, and alert on any
list_changedevent that alters a schema or description. - Confused deputy and token misuse. Passthrough tokens, overbroad application permissions, and missing audience checks.
- Overbroad capability. A "run any SQL" or "execute shell" tool is a capability, not a feature. Prefer narrow tools with constrained arguments, and sandbox general-purpose ones.
- Local server compromise. stdio servers run with user privileges and can read credentials, modify files, and reach the network. Require provenance, pin versions, and restrict filesystem and network access at the operating-system level.
- Server-side request forgery. Servers that fetch URLs on the model's behalf can be steered toward internal endpoints or cloud metadata services. Apply allowlists, block link-local addresses, and segment the network.
- Sampling abuse. A server can consume model budget or attempt to shape behavior through sampling requests. Enforce quotas, require approval in sensitive contexts, and log every request.
- Elicitation phishing. A server requesting credentials through the host's UI is impersonating the host. Hosts should refuse secret elicitation or route it through trusted, out-of-band flows.
- Exfiltration through chaining. Individually safe tools can combine into an exfiltration path: read a sensitive file, then send its contents through a messaging tool. Controls must reason about data flow, not only about individual tools.
7.1 Capability separation
A useful principle is to classify tools by trust profile. Sources read untrusted content, such as inbound email, web fetches, and public tickets. Sinks perform external or irreversible actions, such as sending, deleting, deploying, or transferring. A policy engine should block or require explicit confirmation when a sink runs in a context where a source has contributed content, and it should display the full arguments to the user before the action proceeds.
This will not stop every attack. It does convert an unbounded risk into a bounded, auditable one, which is the most defensible position currently available.
def authorize_call(ctx: CallContext, tool: ToolSpec, args: dict) -> Decision:
# 1. Schema validation before any policy evaluation
validate(args, tool.input_schema)
# 2. Taint: did untrusted content enter this conversation?
tainted = ctx.session.untrusted_sources_seen
# 3. Sink rule: irreversible or external actions in tainted context
if tool.is_sink and tainted:
return Decision.require_confirmation(show_args=args)
# 4. Data classification and destination
if tool.is_sink and classify(args) > tool.max_classification:
return Decision.deny(reason="data classification exceeds tool limit")
# 5. Bounds on arguments (paths, recipients, sizes)
if not within_bounds(tool, args, ctx.policy):
return Decision.deny(reason="argument outside policy bounds")
return Decision.allow()
9. Reference enterprise architecture
Beyond individual developer machines, the pattern that scales is a gateway: a governed point through which all host-to-server MCP traffic flows. The gateway centralizes common identity checks, server approval, policy, audit, and egress controls. Each MCP server and downstream service still owns resource-level authorization and domain validation.
8.1 What the gateway owns
The gateway provides one identity enforcement point, one registry of approved servers, one audit stream, and one egress policy. It is also a single point of failure and a high-value target. It must be deployed as a highly available service with its own resilience design, and its administrative interfaces need the strongest access controls in the estate.
8.2 Audit record design
Each invocation should produce a record with the caller identity, the tool and server identifiers, a hash or redacted form of the arguments (full arguments may contain regulated data), the result classification, the policy decision with its reason, latency, and the trace identifier. Records should be append-only and retained according to compliance requirements.
{
"ts": "2026-10-19T14:22:07.318Z",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"caller": { "user": "user-123@example.invalid", "host": "ide", "client_id": "ide-prod-01" },
"server": "tickets-internal",
"tool": "update_ticket_status",
"tool_pin": "sha256:9c1e...a7f2",
"args_sha256": "b41d...08e3",
"policy": { "decision": "require_confirmation", "reason": "sink_in_tainted_context" },
"confirmation": "approved_by_user",
"result": { "isError": false, "classification": "internal", "latency_ms": 412 }
}
10. System design
An MCP deployment has two planes. The control plane decides what is allowed: which servers are approved, which policies apply, which tool definitions are pinned, and where audit records go. The data plane carries traffic: gateway instances that authenticate and enforce policy on every call, and the MCP servers that perform the work. Separating them means policy can change without redeploying servers, and the data plane can scale without touching governance.
9.1 Components
- Gateway. A stateless service that terminates Streamable HTTP, validates bearer tokens and their audience, evaluates policy, applies rate limits, and forwards permitted calls. Because it holds no session state, instances can be added or removed behind a load balancer. Each instance caches validation keys and policy snapshots, so a control-plane outage does not take the data plane down.
- Registry. The authoritative list of approved servers, with owner, trust tier, endpoint, and pinned tool-definition hashes. Hosts never discover servers on their own; they receive what the registry publishes to their identity.
- Policy store. Source-controlled rules covering which principals may call which tools, data-classification limits, and source and sink constraints. Policy is versioned, reviewed through pull requests, and distributed as signed snapshots.
- Pinning service. Computes a hash over each tool's name, description, and schema at approval. The gateway compares live
tools/listoutput against the pin and blocks or alerts on drift. - Audit pipeline. Receives one pre-dispatch record and one completion record per invocation, writes to append-only storage, and applies retention policy.
- MCP servers. Stateless where possible, deployed behind an autoscaler driven by request latency and queue depth rather than CPU alone, since tool calls are usually I/O bound.
9.2 Request path
In this reference topology, a call crosses the gateway once in each direction. The gateway validates the token, looks up the target server and its pinned definitions in the registry, evaluates applicable policy against the arguments, and writes a pre-dispatch audit record. The MCP server and domain service enforce their own resource and transaction controls. It then forwards the call with a server-scoped token. When the result returns, the gateway inspects it for data-loss patterns and size limits, completes the audit record with the outcome and latency, and returns the result to the host. Two audit records per call is the minimum for a trail that can reconstruct what happened.
9.3 State and scaling
Most MCP traffic needs no shared state, which is the main reason to prefer stateless servers. State appears in three places: session context for subscriptions and in-flight elicitation, idempotency keys for side-effecting tools, and rate-limit counters. Keep session state in a shared store with a TTL. Make tool calls idempotent so that a retry on a different gateway instance is safe. Rate-limit counters need a distributed store, or per-instance limits divided by instance count, accepting some imprecision.
Capacity planning should start from tool-call volume and average latency, then add headroom for token-exchange round trips, one per downstream service per call or per cached token lifetime. Track tokens consumed by tool definitions as a separate cost line; that cost grows with the number of connected servers rather than with request volume.
9.4 Failure modes
| Failure | Behavior | Mitigation |
|---|---|---|
| Control plane unavailable | Gateways run on last-known-good policy and registry snapshot | Maximum snapshot staleness; fail closed beyond it |
| Auth server unavailable | New tokens cannot be issued; cached validation keeps existing sessions working | Short token lifetimes balanced against cache duration |
| Server timeout | Gateway returns an isError result after the host's ceiling | Per-tool timeouts; circuit breaker per server |
| Pinning drift | Calls to the changed tool are blocked pending review | Alert the owning team; explicit re-approval |
| Audit sink unavailable | Sink-classified tools are refused | Bounded local buffer; never silently drop pre-dispatch records |
| Gateway region failure | Traffic shifts to another region | Active-active with a regional registry replica |
9.5 Observability
Emit OpenTelemetry spans for each gateway decision and each downstream server call, propagating trace context across hops. Dashboards should track invocations per tool, p95 latency, error rate split between protocol and tool errors, policy denial rate, and pinning drift events. A sudden rise in denials is often the first visible sign of misconfiguration or an injection attempt, so denial rate belongs in alerting, not only in a chart.
# Example gateway policy (declarative, source-controlled)
server: tickets-internal
trust_tier: first_party
pinned_definitions:
update_ticket_status: sha256:9c1e4f...a7f2
tools:
search_tickets:
classification: internal
role: source # reads untrusted content
rate_limit: 60/min
update_ticket_status:
classification: internal
role: sink # irreversible state change
confirmation: when_tainted
allowed_principals: [group:support-leads]
egress:
deny_metadata_endpoints: true
allowed_domains: [tickets.internal.example.com]
11. Design guidance
- Prefer task-shaped tools over CRUD mirrors. One
create_incident_with_contexttool that validates its own fields beats five low-level calls the model must sequence correctly. Fewer tools mean lower token cost, fewer errors, and less attack surface per unit of capability. - Avoid the god tool. A single
executetool that accepts an operation name and a free-form payload cannot be constrained by schema, reasoned about by policy, or described meaningfully in an audit log. - Write descriptions for the model and review them as security artifacts. State what the tool does, when not to use it, and its side effects. Avoid imperative phrasing that could be mistaken for system instructions.
- Make schemas strict. Use enums, length bounds, patterns, and
additionalProperties: false. Every constraint is one the host can enforce before dispatch. - Bound results. Paginate, cap sizes, and use
outputSchema. Large unbounded results waste context and widen the injection surface. - Design for partial failure. Return actionable
isErrormessages that distinguish retryable from non-retryable failures. - Version deliberately. Renaming a tool or changing its required arguments breaks every dependent host. Add new tools rather than mutating old ones, and deprecate with notice.
- Never pass through credentials. It is the most common production mistake, and it bears repeating.
12. Business impact
The business case rests on four effects that should be measured rather than assumed.
- Integration cost. Reuse of common MCP interfaces can reduce duplicated engineering effort, but the reduction is not guaranteed to follow a simple formula. A capability built once works across every compliant host, limiting lock-in at the integration layer. Connectors survive a change of model provider or agent platform.
- Time to capability. Teams can expose internal systems to AI workflows without waiting on each host vendor. Without a gateway and registry, that speed produces shadow integrations, which cost more to remediate than they saved.
- Operating cost. Tool definitions may consume substantial tokens when supplied to the model; oversized tool surfaces can degrade selection quality. Track tokens per turn attributable to tool definitions as a budget line.
- Risk and governance. An ungoverned MCP estate creates unbounded exfiltration and action risk. Frame the investment in terms of blast radius: which systems an AI-initiated action can reach, what it can change, and what evidence remains afterward.
A sound adoption path starts with read-only servers against non-sensitive data, then builds the gateway, registry, and audit pipeline, and only then adds write capabilities behind confirmation gates and taint-aware policy. The controls must be proven before the most valuable integrations arrive.
13. Enterprise scenario: controlled invoice approval
Consider an AI assistant asked to approve a supplier invoice. This is a useful reference scenario because it combines retrieval, a state-changing tool, delegated identity, approval limits, and transaction audit.
- Read: the host calls a read-only invoice lookup tool and displays amount, supplier, status, and permitted actions.
- Decide: the model proposes
approve_invoice; the host checks intent and requests explicit confirmation for the write. - Authorize: the gateway checks caller and tool scope; the finance server verifies invoice-level entitlements and approval threshold.
- Execute: the ERP API checks current invoice state and records the approval with an idempotency key and concurrency control.
- Audit: correlated records capture the decision, user approval, backend transaction ID, and final outcome without retaining unnecessary sensitive payloads.
If the invoice is already approved, the approval limit is exceeded, or the backend times out, the server returns a controlled outcome. A retry must not create a second approval. The model must not infer success from a timeout.
14. Open questions for architects
Several decisions remain open and require explicit answers rather than defaults.
- Identity for agent actions. Act as the user, as the application, or as a hybrid. The right answer depends on reversibility and data sensitivity.
- Where data-flow policy is enforced. The host sees more context but is hard to standardize across implementations. The gateway is centralized but sees only traffic that passes through it.
- Trust tiers for servers. First-party, vetted third-party, and unvetted community servers should not receive identical default permissions.
- Long-running operations. Support for asynchronous, multi-hour work is still maturing in the specification. Verify current support before depending on it, and design fallbacks that do not rely on a single long-lived connection.
15. Deployment, testing, and architecture decisions
14.1 Deployment and isolation
Separate public ingress, MCP gateway, domain MCP servers, and backend systems into distinct network trust zones. Use workload identity or mTLS where appropriate for service-to-service authentication, restrict egress, and isolate tenants at identity, cache, session, and data layers. Stateless gateways are easier to scale; subscriptions and resumable streams may require session affinity or shared state.
14.2 Verification before production
| Test class | Minimum verification |
|---|---|
| Protocol conformance | Initialization, capability negotiation, tool discovery, cancellation, version compatibility |
| Authorization | Wrong audience, expired token, cross-tenant access, privilege escalation, denied tools |
| Adversarial | Prompt injection in results, poisoned tool metadata, SSRF, chained exfiltration |
| Reliability | Backend timeout, duplicate write, gateway failover, audit outage, policy snapshot staleness |
| Observability | Trace continuity, redaction, policy-decision audit, missing-span detection |
14.3 Architecture decisions
| Decision | Options | Guidance |
|---|---|---|
| Ownership | Centralized / federated servers | Favor domain-owned servers with common standards |
| Connectivity | Direct / gateway-mediated | Gateway for shared, regulated access; avoid unnecessary hops for local use |
| Identity | Delegated / service / hybrid | Prefer user-scoped delegation where user permissions matter |
| Execution | Sync / async | Use job patterns for long-running or non-idempotent workflows |
14.4 Adoption and success measures
Start with read-only, bounded capabilities; introduce governed writes after authorization and audit controls are proven. Measure onboarding lead time, tool reuse, p95 execution latency, policy coverage, transaction success rate, cost per completed workflow, and incident frequency. Avoid treating the number of registered tools as a success metric.
16. Conclusion
MCP has a clean separation of roles, a deliberate model of who controls each capability, and a standard authorization story for remote servers. Its security properties, however, come from architecture rather than from the protocol alone: how identity propagates, how tool definitions are governed, how untrusted content is kept away from privileged actions, and how the estate is observed and audited.
Treat MCP servers as partially untrusted capability providers, the host as the policy enforcement point, and the gateway as the place where governance becomes operational. Separate the control plane from the data plane so policy can change without redeploying capability. Done this way, MCP delivers its promise: integrations built once, governed centrally, and usable across whichever tools an organization adopts.
Technical references
Validate deployment-specific requirements against the selected MCP specification revision. Relevant primary sources: MCP specification, MCP architecture, RFC 9728, RFC 8707, RFC 8693, OpenTelemetry, and OWASP GenAI Security.