MCP Servers Are Dependencies, Not Plugins
Models, vendors, and MCP servers form a layered stack. Treating each server as a production dependency prevents auth, quota, and trust failures.
The interesting part of an AI coding assistant is no longer the model. It is everything the model is allowed to touch.
The Model Context Protocol (MCP) is the open standard that defines that reach, and it has been adopted quickly enough that most teams now run several MCP servers without anyone having deliberately decided they should. Understanding how the pieces fit together is the difference between a useful setup and a quietly expanding attack surface.
What Sits Between the Model and Your Systems
Four distinct layers are involved, usually owned by four different parties.
The model vendor supplies reasoning. The host application — an editor, a terminal client, a desktop app — owns the conversation and decides which tools are offered. Inside the host, an MCP client maintains a dedicated one-to-one connection to each MCP server, and the server exposes tools and data from a real system: a documentation index, a ticket tracker, a database.
Transport depends on where the server lives. As the MCP architecture overview describes, local servers typically use stdio and serve a single client, while remote servers use Streamable HTTP, can serve many clients at once, and authenticate over standard HTTP methods.
The payoff is real decoupling: your model vendor is no longer your integration vendor. Change models without rewriting the ticket-tracker integration. Change that integration without touching the model.
The Vendor Boundary Moved, It Did Not Disappear
Decoupling redistributes responsibility rather than removing it.
The model vendor governs reasoning and refusal behavior. The host vendor decides what consent looks like and which tools reach the model. The server author decides what a tool named update_record actually does. You supply the credentials it runs with.
The specification is unusually direct about the consequences: MCP cannot enforce security principles at the protocol level. Consent flows, authorization, and access controls are implementation responsibilities, not protocol guarantees. It also tells clients to treat tool annotations as untrusted unless they come from a trusted server, and to keep a human in the loop who can deny any tool invocation.
So "is this safe?" cannot be answered by naming your model. Safety is a property of the whole chain.
Configuration Sprawl Is the First Failure Mode
Before anything exotic goes wrong, configuration does.
We hit this ourselves this week. A documentation-lookup server had been defined in four separate places across our workspaces — three workspace files and one repository-level config. The definitions had drifted apart: three launched a local process through npx, one pointed at the vendor's hosted HTTP endpoint. None of them carried an API key.
Every request therefore went out unauthenticated, and all four definitions drew from the same shared anonymous rate limit. Lookups failed for an entire working session. It presented as a broken server. It was a configuration problem.
The fix was to delete all four and define the server once, globally, authenticated, with the key held in the operating system keychain rather than in a file. Editor-level MCP configuration is not like lint configuration. A server that is useful in every project belongs in one user-level file, not copied into each repository where it can drift.
Treat Each Server Like a Production Dependency
The plugin framing is what causes trouble. Plugins feel disposable. These are dependencies that hold credentials.
- Pin the supply chain.
npx -y some-mcp-serverresolves and executes the latest published version on every launch. That is an auto-updating remote script running with your local permissions. Pin versions, or prefer a hosted endpoint from a party you already trust. - Scope credentials narrowly. A server inherits whatever token you hand it. A read-only token for a read-only server costs nothing.
- Know what is installed. Keep an inventory of the servers, where each one is defined, and what each can reach. Sprawl stays invisible until it fails.
- Keep approval meaningful. Blanket auto-approval quietly removes the human-in-the-loop control the specification assumes is there.
None of this is novel. It is ordinary dependency hygiene, of the kind we describe on our security practices page, applied to a category most teams have not yet classified as dependencies at all.
Where This Nets Out
MCP is a genuine improvement. Before it, every assistant-to-system integration was bespoke and vendor-specific. Now the plumbing is standard and the layers are swappable, which is exactly the kind of boundary that ages well.
But a standard for how things connect is not a judgment about whether they should. The protocol deliberately leaves consent, authorization, and trust to implementers, which means those decisions land on whoever configures the system. Adding a server is a small action with a large radius: it grants a model, running inside someone else's host application, a path into a real system using credentials you supplied.
Keep the list short. Define each server once. Give it the narrowest credential that works. Do that, and the standardization pays off, because the interesting question moves off the plumbing and back onto what you are actually trying to build.