Every team building with AI models runs into the same integration problem in a new form. A model can only act if something tells it which functions exist, what arguments they take, and what the caller is allowed to do. For most of that period the answer was a bespoke connector per product. The Model Context Protocol, open-sourced by Anthropic on November 25, 2024, replaced that with a shared wire format. That shift moves the interesting decisions from "how do we connect" to "what do we standardize at the boundary".
The answer is not "everything". A protocol that defines how tools are described is not a governance model, and treating it as one is how teams end up with an agent that can issue refunds in production because nobody wrote down who was allowed to approve that. This piece separates the parts MCP already answers from the parts your organization has to answer.
What MCP fixes, in concrete terms
MCP is a JSON-RPC 2.0 protocol with three roles. Hosts are the applications that initiate connections, clients are the connectors inside a host, and servers expose context and capabilities. Connections are stateful and begin with capability negotiation, so both sides declare what they support before any work starts.
The specification defines two standard transports: stdio, where the client launches the server as a subprocess, and Streamable HTTP, where the server runs independently and serves multiple clients. Streamable HTTP replaced the earlier HTTP plus Server-Sent Events transport from the 2024-11-05 revision. Servers offer resources, prompts, and tools. Clients can offer sampling, roots, and elicitation back to servers.
The practical consequence is worth stating plainly: an internal function wrapped as an MCP tool can be consumed by a chat client, an editor, and a workflow engine without three separate integrations. That value is real. It also turns the tool definition into an interface contract that other teams depend on, which is exactly where governance begins.
What the specification leaves to you
A tool definition carries a unique name, an optional human-readable title, a JSON Schema inputSchema, an optional outputSchema, and optional annotations describing tool behavior. The spec is explicit that clients should treat those annotations as untrusted unless they come from a trusted server. That sentence tells you most of what you need to know about leaning on metadata for security decisions: do not.
MCP does not define naming conventions, semantic versioning of tools, tenancy, network placement, data classification, approval workflows, or which tools an agent may call in which context. Two teams can implement the specification correctly and still produce a fleet that nobody can audit. The rest of this piece closes that gap.
Auth and scopes decide your risk profile
Authorization is optional in MCP. When it is implemented over an HTTP transport, the spec says it should conform to the authorization section, which is a deliberately small subset of OAuth 2.1. It requires protected resource metadata (RFC 9728) so a client can locate the right authorization server, requires authorization server metadata (RFC 8414), and recommends dynamic client registration (RFC 7591). Servers using stdio should not follow this flow at all; they take credentials from the environment.
The spec's security notes name the failure modes you will actually meet: token audience binding and validation, token theft, open redirection, and the confused deputy problem. Translated into policy, that means four rules.
- Issue tokens scoped to a tool family, not to an entire server. A server exposing both "read ticket" and "issue refund" should not hand out a credential that can do both.
- Bind tokens to the intended resource and validate the audience at the server. A token minted for your internal wiki should be worthless at a partner's endpoint.
- Keep lifetimes short and make revocation possible without rotating every credential in the fleet.
- Never forward a user's token to a third-party server unless that user's consent names that specific destination.
One more decision hides here: which identity the agent carries. If the agent acts as a shared service account, every action collapses into one identity and you lose the ability to answer "who asked for this". Carry both, the agent identity and the human on whose behalf it acts, in every request and every log line.
Server governance: one owner, one version, one switch
An MCP server is a production dependency that can execute actions. Give each one an owning team, an on-call rotation, a pinned version, and a documented contract. Three practices do most of the work.
First, separate environments. A single server with an environment flag is a common shortcut and a reliable source of incidents, because the same code path ends up holding production credentials. Run distinct servers for development, staging, and production, with distinct scopes.
Second, allowlist. The project publishes a registry, and a registry answers "what exists", not "what may run here". Maintain an internal list of approved servers with a named reviewer and a review date, and require an exception to add one. A third-party server is code that reaches into your systems, and the OWASP Top 10 for LLM applications lists supply chain and excessive agency among its leading risk categories for that reason.
Third, a kill switch. You need to disable a tool, or an entire server, within minutes without redeploying the host application. If the only control is a code change and a release, you do not have a control; you have a hope with a calendar.
Auditability: log the call, not the conversation
Conversation logs are not audit logs. A defensible record of an agent action captures the caller identity, both agent and human, the server and tool name, the tool version, a hash of the arguments with the full payload where sensitivity allows, the authorization decision, the result status, latency, and cost. Add a correlation identifier that ties the call to the host's trace so an incident review can reconstruct the sequence across services.
Two design choices matter more than the tooling. Keep authorization decisions in the log, not just outcomes, because a denial is the interesting event in a review. And write the log outside the server process, so a compromised or misconfigured server cannot rewrite its own history.
Where to standardize, and where to stay proprietary
The table below is the working version of the decision. The rule behind it: standardize anything that is expensive to change once other teams depend on it, and keep proprietary anything that moves with model capability or encodes your risk appetite.
| Layer | Standardize now | Keep proprietary | Why |
|---|---|---|---|
| Tool schema | JSON Schema input and output, one naming convention | Nothing | Cheap to set now, breaking to change later |
| Auth and scopes | OAuth 2.1 subset, RFC 9728 discovery, per-tool-family scopes | Nothing | Confused deputy and token theft are architectural |
| Transport | Streamable HTTP for remote, stdio for local tools | Custom transports for unusual networks | Two transports cover most deployments |
| Server lifecycle | Owner, version pin, environment split, allowlist | Nothing | Every server is a production dependency |
| Audit logging | One schema with identity and correlation IDs | Nothing | Needed for incident review and auditors |
| Approval and policy | The hooks and the event format | Risk tiers, approval UX, policy engine | Encodes your risk appetite, not a protocol |
| Tool selection | The tool interface | Routing, retrieval, ranking | Changes with each model release |
Two rows deserve a comment. Approval policy stays proprietary because it is the clearest place where a generic answer is worse than a specific one: a research assistant and a payments agent should not share a risk tier, and no protocol can know that. Tool routing stays proprietary because model behavior moves faster than standards committees; if you outsource ranking to a vendored library, you inherit its assumptions about which model is in use. The interface, by contrast, should be boring and fixed, so that swapping a router or a model does not ripple through every server you own.
What to do next
- Inventory every MCP server you run or use, including the ones an engineering team installed locally, and name an owner for each.
- Write the tool naming and schema convention before the fleet doubles, and enforce it in review.
- Change scopes so that dangerous tools require their own token, then test that a low-privilege token fails against them.
- Pick one audit log schema, emit it from the host rather than the server, and verify that a denial shows up with an identity attached.
- Define the kill switch procedure on paper, then run it once during a working day and time how long it takes.
The protocol question is mostly settled. What remains is the operational layer around it, and that layer is where the difference between a useful tool integration and an ungoverned back door gets decided. Standardize the boring parts early, keep the opinionated parts yours, and write down the decisions while they are still cheap to reverse.