Skip to main content

The MCP Composability Trap: When 'Just Add Another Server' Becomes Dependency Hell

· 9 min read
Tian Pan
Software Engineer

The MCP ecosystem has 10,000+ servers and 97 million SDK downloads. It also has 30 CVEs filed in sixty days, 502 server configurations with unpinned versions, and a supply chain attack that BCC'd every outgoing email to an attacker for fifteen versions before anyone noticed. The composability promise — "just plug in another MCP server" — is real. But so is the dependency sprawl it creates, and most teams discover the cost after they're already deep in integration debt.

If you've built production systems on npm, you've seen this movie before. The MCP ecosystem is speedrunning the same plot, except the packages have shell access to your machine and credentials to your production systems.

The npm Parallel Is Not a Metaphor

When people compare MCP's ecosystem growth to early npm, they usually mean it as a compliment — rapid adoption, vibrant community, composable building blocks. But the parallel extends to the failure modes too, and the failure modes are worse.

A scan of 42,000+ MCP tools across seven registries found patterns that mirror npm's worst supply chain incidents. The numbers paint a specific picture:

  • 502 configurations use npx without version pinning, pulling whatever is latest on every agent startup
  • 1,050 configurations point to remote URLs with no certificate pinning or authentication
  • 448 configurations enable auto-install flags that skip user confirmation before executing code
  • 467 tools reference mutable GitHub raw URLs that change when branches update
  • 1,679 tools include embedded pip commands; 742 include system package managers

The critical difference from npm: npm packages run in a sandbox. MCP servers execute with full machine access — your filesystem, your shell, your credentials. When the event-stream attack hit npm in 2018, one compromised maintainer reached millions of downloads. The blast radius was limited by the browser sandbox. MCP has no equivalent containment.

The postmark-mcp attack in early 2026 demonstrated this perfectly. The attacker built trust over fifteen normal-looking versions before adding code that BCC'd every outgoing email to an external address. Because the MCP server had legitimate email-sending permissions, the malicious behavior was indistinguishable from normal operation at the tool-call level.

The Trust Model Is Broken by Default

Most MCP clients implement Trust On First Use (TOFU). You approve a server once during initial setup, and subsequent updates go unverified. This creates a window where approved servers can be silently compromised — and it's the exact window attackers target.

The problem compounds at scale. Server references live in JSON config files, often checked into repositories or shared across teams. A typical agent configuration might reference eight to twelve MCP servers, each with its own version (or lack thereof), credentials, and trust assumptions. Here's what breaks:

  • Silent updates: Without version pinning, your agent's capabilities change every time it restarts. A server that worked yesterday might expose different tools today, or the same tool might behave differently.
  • Credential sprawl: 88% of MCP servers require credentials, but 53% rely on long-lived static secrets passed as environment variables. When you're running ten servers, that's ten sets of credentials with no centralized revocation.
  • Inherited permissions: The MCP spec doesn't include authorization. Every server inherits whatever permissions it's granted, and every request flows through without verification unless you add external controls.

Between January and February 2026, researchers filed over 30 CVEs targeting MCP infrastructure. The vulnerabilities ranged from trivial path traversals to a CVSS 9.6 remote code execution flaw in a package downloaded nearly half a million times. The root causes were consistent: missing input validation, absent authentication, and blind trust in tool descriptions.

Tool Conflicts at Scale: The Discovery Problem

The composability promise assumes that adding servers is additive — more servers means more capabilities. In practice, combining MCP servers creates conflicts that are invisible until they cause failures.

The most immediate problem is tool naming. When two servers expose tools with the same name, the agent has to pick one, and the resolution strategy varies by client. Some take the last-registered server. Some error. Some silently shadow one tool with another. None of these outcomes is correct when you didn't expect the conflict.

Namespacing — prefixing tools like calendar.list_events and slack.search_messages — solves the syntactic collision. But semantic conflicts are harder. Two different MCP servers might both offer a send_email tool with different parameter schemas, different authentication contexts, and different side effects. The agent sees two tools that do roughly the same thing and has to choose based on descriptions alone.

This gets worse as tool counts grow. Research on tool selection accuracy shows that agent performance degrades as tool inventories scale — what works cleanly with four tools becomes unreliable at fifty. Each MCP server you add isn't just adding capabilities; it's expanding the decision space the agent navigates on every turn.

MCP's capability negotiation — the initialization handshake where client and server declare supported features — handles protocol-level compatibility. It doesn't handle semantic compatibility between tools from different servers that operate on the same domain. That gap is left entirely to the agent's reasoning, which means it's left to chance.

The Operational Tax Nobody Budgets For

Teams that successfully ship MCP-powered agents in production share a common trait: they treat MCP servers as operational dependencies, not plug-and-play components. The gap between these mindsets is where integration debt accumulates.

Version management is the foundation. Every MCP server reference should be pinned to a specific version or commit hash. Not a branch. Not latest. A specific, immutable artifact. This is table stakes in traditional dependency management, but the MCP ecosystem's default patterns actively encourage the opposite — npx some-mcp-server without a version is the norm in documentation and examples.

Health monitoring for MCP servers requires different instrumentation than traditional services. An MCP server can be "up" in the HTTP sense while returning degraded tool descriptions, missing capabilities, or stale data. You need to monitor not just availability but behavioral consistency — does this server still expose the tools your agent expects, with the schemas your agent was built against?

Credential rotation across ten MCP servers is a different problem than rotating credentials for one. Most teams solve this with environment variables per server, which means rotation requires redeployment. A centralized credential gateway that handles secrets at the proxy layer lets you rotate without touching agent configurations, but building that gateway is work nobody planned for when they heard "just add another MCP server."

Audit logging should capture which agent called which tool, on which server, with what parameters, and what happened. Without centralized logging, debugging a failure in a ten-server MCP setup means correlating logs across ten different services, each with its own format and retention policy.

The Architecture That Actually Works

The teams running MCP reliably in production have converged on a similar architecture, even if they arrived at it from different starting points.

Gateway pattern: A thin orchestration layer sits between the agent and MCP servers. This gateway handles authentication, credential injection, tool discovery caching, request routing, and audit logging. Individual MCP servers don't need to implement these concerns because the gateway handles them uniformly. This is the same pattern that emerged for microservices (API gateways) and for cloud APIs (service meshes) — the lesson just needs to be relearned for MCP.

Tiered tool provisioning: Not every agent needs every tool. Organization-level policies define which servers are available at all, and workspace-level scoping controls which agents can discover which tools. If a tool is disabled at the org level, it's invisible everywhere. This prevents the ambient authority problem where an agent scoped to customer queries discovers and invokes internal admin tools.

Explicit dependency manifests: Rather than configuring MCP servers inline, define them in a manifest file with pinned versions, required capabilities, and health check endpoints. Treat this manifest like a package-lock.json — it's a contract that describes exactly what your agent depends on, and changes to it should go through review.

Graceful degradation: When one MCP server fails, the agent should continue operating with reduced capabilities rather than failing entirely. This requires the agent to know which tools are essential versus optional, which means you need to classify your MCP dependencies the same way you'd classify any service dependency — is this on the critical path or not?

A Dependency Maturity Checklist

Before adding another MCP server to your agent's configuration, run through this checklist:

  • Version pinned? Specific version or commit hash, not latest or branch reference
  • Credential scoped? Minimum necessary permissions, with a rotation plan
  • Tool conflicts checked? No naming collisions with existing servers; semantic overlaps documented
  • Health monitored? Behavioral checks, not just uptime pings
  • Audit logged? All tool calls captured with parameters and outcomes
  • Degradation planned? Agent behavior defined for when this server is unavailable
  • Supply chain verified? Maintainer reputation, update frequency, known vulnerabilities scanned

If you can't check all seven boxes, you're not adding a capability to your agent. You're adding a liability.

The Path Forward

The MCP protocol itself is sound. The initialization handshake, capability negotiation, and transport abstraction are well-designed primitives. The problem isn't the protocol — it's the ecosystem practices that grew up around it before production discipline caught up.

The MCP registry is moving toward namespace authentication and metadata hosting, but it deliberately leaves security scanning to the broader ecosystem. This is the right architectural decision — a registry shouldn't be a security boundary — but it means teams must build their own vetting pipeline rather than trusting that "it's in the registry" means "it's safe."

The parallel to npm's maturation is actually encouraging. npm went through the same growing pains — left-pad, event-stream, dependency confusion attacks — and emerged with lockfiles, audit commands, provenance attestations, and a security culture that didn't exist in the early days. MCP will likely follow the same arc, but you don't have to wait for the ecosystem to mature to build disciplined MCP deployments today.

The teams that will look back on this era without regret are the ones treating MCP servers like what they are: third-party code running with elevated privileges inside their production systems. Not plugins. Not extensions. Dependencies — with all the operational discipline that word implies.

References:Let's stay in touch and Follow me for more thoughts and updates