The Model-Client Protocol (MCP) is, at its core, a JSON-RPC-based protocol that enables a client (the thing driving an LLM) to talk to a server exposing capabilities over a small, fixed set of primitives:
- Tools: callable functions with a name, a description, and a JSON Schema for inputs. This is the primitive most people mean by “MCP” and the one doing heavy lifting in production: “lookup order,” “run a query,” “create a ticket.”
- Resources: readable context addressed by URI — a file, record, or document the client can pull in without the model calling a function.
- Prompts: reusable templates a server offers so common workflows don’t need to be respecified each time.
The spec also defines server-to-client capabilities: Sampling (server asks the client model to generate text); elicitation (added in the 2025-06-18 revision), which lets a server pause and request human input mid-task; and roots, which let a server learn which directories or URIs it’s actually allowed to touch.
None of these primitives are singularly novel; what’s novel is that they’re the same set regardless of model, framework or vendor on the client side. That one sentence captures both the value proposition and the source of the new kinds of successes and failures teams encounter.
Why this is a different integration problem
Before MCP, wiring an LLM into an enterprise system meant writing tool-calling code for each specific model or agent framework. Every framework had its own function-calling convention, schema description style and error-handling contract. Supporting a second framework didn’t double the work — it created a second, incompatible copy of the same logic.
MCP replaces that with one contract written once and usable by any compliant client, which measurably reduces maintenance: the burden now scales with the number of systems, not with the number of frameworks.
More consequentially, MCP changes where the integration decision is made. A traditional API integration is an agreement two systems make ahead of time: endpoints, payloads, auth, versioning are negotiated and both sides build to that plan. An MCP server doesn’t get to rely on such a plan. It doesn’t know which agent will call it, in what order, with which other servers, or for what human-provided goal. The plan doesn’t exist until the agent composes one at runtime out of whatever tools are available. That’s a different category of problem because the composing party is now a model reasoning over natural-language descriptions you wrote weeks or months earlier, without knowledge of the eventual context.
The description is the interface now
A tool’s JSON Schema tells the agent what parameters the tool accepts and their shape — this is mechanical and MCP handles it. A tool’s name and natural-language description tell the agent when to use it and how to prefer it over adjacent tools. Those are different jobs; only one is solved by a well-formed schema.
Compare two descriptions. The terse, technically correct one:
{ "name": "get_status", "description": "Returns the current status of a record given its ID." }
An agent reading that has no idea what domain “record” refers to, what ID format is expected, or how this tool differs from other status-returning tools. A more detailed description specifies the domain (for example, “order record”), ID format (numeric order numbers like 48213), the allowed status values (pending, processing, shipped, delivered, cancelled), and flags similarly named tools and the difference (e.g., “use instead of get_shipment_status, which returns carrier tracking events rather than internal order state”).
That longer description will feel like over-explaining to an engineer who already knows the context, but the agent doesn't. I’ve seen teams ship technically correct MCP servers that agents used badly or avoided entirely in favor of worse but better-described alternatives. The failure mode is not a stack trace: it’s an agent confidently calling the wrong tool, or the right tool with incorrect assumptions baked in — problems that only show up downstream when outputs look slightly off. Writing useful tool descriptions is closer to technical writing and product design than to backend engineering, and many integration teams lacked that skill early on.
Emergent composition cuts both ways
MCP’s appeal is that an agent can combine tools from servers that never agreed to work together, in combinations authors never planned for. That’s also the structural risk: sequencing logic that used to live in a human-authored, reviewable script now lives in a model’s runtime reasoning, generated fresh for each task. You can’t unit test a decision that doesn’t exist until the moment it’s made.
A tool that behaves correctly in isolation can still produce a bad outcome when an agent calls it in a different position in a chain, or feeds it a value derived from another server rather than from human input. These bugs don’t show up in code review, and they won’t appear in tests unless your test suite happens to exercise that specific, unplanned chain. They tend to surface in production, once, for a particular customer — the kind of edge case cheap to dismiss until it affects the wrong person.
The protocol is evolving in response to production pain
MCP has changed in ways that reveal where real deployments strained the protocol. The July 28, 2026 specification is the largest revision since the protocol’s November 2024 launch, and the major changes map to problems operators actually hit:
- The core moved to statelessness. The earlier MCP-Session-Id approach worked for a single instance but became painful once fleets were horizontally scaled; removing protocol-level sessions allows ordinary load balancers to route any request to any instance.
- Tasks formalize long-running work (document processing, multi-step approvals, human-in-the-loop flows) that previously needed hand-rolled polling and webhook patterns, each with their own edge cases.
- MCP Apps allow a server to return interactive UI instead of just structured data — important when a tool’s outcome requires human review before action.
- Authorization was hardened to align with OAuth 2.1 and OpenID Connect.
- A deprecation policy now governs the legacy HTTP+SSE transport with a 12-month offramp.
These changes are operational fixes — the “scars” of running a protocol in production — and they matter more than adoption counts alone. The adoption data itself is striking: the official registry tracks close to 10,000 distinct servers, Tier 1 SDK downloads are in the tens of millions per month, and both the TypeScript and Python SDKs have individually crossed a billion total downloads. Competitors adopted the protocol within months of its launch. That combination — real scale plus a spec that shifts in response to failure modes — is a strong signal of durability.
Governance hasn’t caught up; real vulnerabilities exist
Independent security research through 2026 shows a worrying ecosystem picture. Scans of thousands of publicly registered servers found the majority exhibiting file-operation patterns vulnerable to path traversal. A meaningful share were susceptible to command injection or server-side request forgery (SSRF). There are documented cases of tool description poisoning — attacks embodied in the text a model reads to decide what to do, not in the code the tool executes. Configuration poisoning is a related mode: stealthy changes to a server’s operational baseline (permissions or defaults) that persist across sessions and evade normal code review because the malicious logic lives in configuration state.
Multiple high-severity vulnerabilities have been disclosed and patched, including at least one missing-authentication flaw in a major vendor’s production package.
This is not a reason to avoid MCP; it’s a reason to treat it like any protocol that grants an autonomous caller real privileges: skeptically, and with controls in place before the agent gains access, not after an incident teaches you why you needed them.
Practical mitigations include an explicit, enforced allowlist of vetted tools per agent rather than open discovery; authentication on every remote endpoint with no quiet exceptions for "internal" traffic; centralized, immutable audit logging of every tool call; and dynamically retrieved secrets from a real secrets manager rather than stored in a server’s local config where configuration-poisoning attacks can find them. None of this is exotic — it’s the same discipline experienced integration teams apply to systems with real privileges, applied here to an agent that can improvise its own sequence of actions.
Recommendations for teams starting today
- Treat tool descriptions as reviewed engineering output, not last-minute documentation. Test them against how agents actually behave when given the description, not just against human reviewer nods.
- Assume unplanned composition will occur; design tools to fail safely and to make failures legible rather than assuming an untested call chain won’t happen.
- Put governance in front of capability: allowlist, auth, audit log and secrets manager should be enforced from day one given current vulnerability data.
- Build against the current specification baseline (for example, the July 28, 2026 spec), not an older example repo. The stateless core and authorization changes are not cosmetic; targeting an older baseline is immediate technical debt.
Conclusion
MCP earned the “not just another API standard” label honestly. It didn’t get there by being a cleaner REST, a nicer SDK, or a better-documented function-calling convention — those are incremental. It got there by changing who or what does the integration work at runtime: models. The things the protocol doesn’t standardize — how well you describe a capability, how safely tools behave when composed in unanticipated ways, and how seriously you treat governance before granting an agent privileges — are exactly the parts worth addressing before you bet production traffic on MCP.



