Skip to main content

MCP Implenda Est

ยท 9 min read
Chris Dickinson
Principal Engineer @ Dylibso

or, a critical look at "A Critical Look at MCP."โ€‹

I came across this post shortly after finishing support for the HTTP Streaming transport on mcp.run. After a week or so of banging my head against the matrix of OAuth RFC support and client transport support, I should be in a sympathetic frame of mind to receive a critique of the protocol. And yet! Here I am, defending MCP. After all, the good that interfaces do oft lay interred with their blemishes.

And MCP has blemishes.

But I don't think we're in agreement about what they are, and where they pose actual danger to the value that MCP creates.

level settingโ€‹

First, a bit about mcp.run, to level-set. (And, well, because it's business that sells goods and services -- and, not to put too fine a point on it, one which employs me.)

mcp.run is a website that lets you build your own MCP server combined out of a number of other MCP servers. Those servers might be remote connections or they might be written in Wasm and run on your computer, or a combination of the two. Our goal is to make it easier for businesses to adopt MCP tools by handling the configuration and co-location of servers. We're sort of a "virtual" MCP server, in that sense.

This is important for two reasons:

One, MCP started out as a primarily "on-device" protocol, which meant that your MCP servers would run as a sub-process spawned by your client (Cursor, Windsurf, Claude Desktop, etc.) So delivering MCP servers in Wasm meant that these sub-processes couldn't take unexpected action. It also meant that we could safely host these servers ourselves in preparation for remote server transports to gain client adoption.

Two, as we can host all of these servers for you, we can also hold onto any necessary configuration -- OAuth client data, tokens, configuration -- and ensure that it is only accessed on our servers using separately stored encryption keys. As adoption of MCP begins to tilt from "local servers" to "connecting to remote servers", this becomes more appealing: you can bring your configuration with you across multiple clients and ensure that only trusted tools can be run together. (This sidesteps one of the big security pain points of MCP, which is that MCP is only as secure as the least secure tool you've made available in your client.)

And here's the important thing: MCP is an interface that's ratcheting up to meet the needs of its users. For a while, clients and servers were both local, then servers got more advanced via the SSE transport and tools like mcp-remote. Then clients caught up: see Claude's recent integration of OAuth and SSE transports. Now servers are on the move again: mcp-remote started to make the new HTTP transport available just this last week.

transports are not the bottleneckโ€‹

Let me underline this: transports are the least important part of MCP. Whether communicating via STDIO, SSE+POST requests, WebSockets, carrier pigeons or HTTP streaming, it doesn't really matter, because they can change (and they have!) JSON-RPC over a bidirectional stream is an incidental choice. You can cobble a bidirectional stream together out of most household items in a pinch, depending on your tolerance for pain. JSONRPC is a useful framing but doesn't really affect the core information carried across that bidirectional wire once you've connected it.

That said, the existing transports can be a pain. Keeping a stateful HTTP connection around (or mapping sessions to outgoing connections) isn't fun: it means having to keep some sticky information around about what server process is executing which client requests, and propagating that mapping down through layers of reverse proxies and CDNs. But even if MCP moved to WebSockets, that's still a long-lived, stateful connection -- and one of those parties between that client and your server is going to eventually close the connection. So the idea that you can map the statefulness of MCP to the statefulness of the transport is an illusory benefit. You must plan for disconnects and reconnects. And yeah, there's probably going to be a queue involved to shuttle messages from ingress MCP route A to egress MCP SSE connection B. But that's life! I fully expect the transports to change to address pain points in hosting MCP servers.

oauth has been going great, reallyโ€‹

I turn now to something that MCP is doing really well: baking the concept of authentication into the protocol via OAuth 2.0 standards. This may come as a surprise, because OAuth can be Kind of A Lot -- but it makes a lot of sense if you look at why MCP is valuable in the first place. The promise of MCP -- of any of the tool calling standards -- is that tool-use promotes LLMs from "content printers" to agents that can affect the real world. And you really want a standard around tool use -- how to describe the tool to the LLM for use, how to format the arguments and what to expect from the output. The value there is so great that it really doesn't matter what else you get wrong so long as people -- client and server authors alike -- vote with their feet and implement your protocol.

And that's happening! And at this stage of the protocol's evolution, these servers are starting to be predominately remote. And the easiest way for server authors to give an LLM something to affect the world is to give it access to existing, OAuth 2.0-authenticated APIs. Server authors working on large systems likely already have an OAuth 2.0 API. So they're not stuck reinventing the wheel to get clients authenticated to talk to their APIs.

real problemsโ€‹

So what are the problems with MCP as it stands?

Well, I alluded to one before: the idea that a client will allow you to mix and match connections to various servers is a neat one, but ultimately it works against users. Tools in MCP work by contributing to the system prompt, which gives them privileged access to the LLM and the other tools they work with. So you have a dual problem of managing token usage and ensuring that tools are only used in secure contexts.

(You can see where mcp.run slots into this system: consider paying us money to solve the problem for you!)

Further, one of the issues with remote servers is tenancy -- whether servers are single or multi-tenant, and what that means for the tool descriptions they provide. As I mentioned before, MCP servers started out as predominantly local processes: mapping one server to one client via one connection. As remote transports mature, we're seeing that MCP servers may be multi-tenant -- one server to many client connections, dispatching MCP-level operations like tool listing and calling based on authentication information sent along with the request.

There's an immediate problem: early MCP servers were written with single tenants in mind. It will require work to make them multi-tenant. But that's just typing work. And, I hear, we've got new-fangled tools to make the cost of typing work disappear to zero.

The more subtle problem is that there's no longer a guarantee that an MCP server will advertise the same list of tools or resources to different users (or even the same user over time!) From a certain frame, this is a good thing: servers have control over tool lists, and control makes servers flexible. But consider that the descriptions from tool lists contribute to the system prompt in MCP-enabled sessions! This starts to become a problem: how do you surface that a server became malicious to end users?

(Well, we're working on this solving this over at mcp.run -- and if you're interested, we sell goods and servic-cough. You've heard the spiel enough at this point.)

Finally: the last issue is that there are features in MCP that are less widely implemented. In particular, I'm thinking of resources, prompting, and server sent events. These are features that represent more of a lift for LLM clients. That is to say: while Claude has support for resources, how do other LLMs fare with the concept? The idea of a resource is that you can create a chunk of data that doesn't have to appear in the context window, but the tradeoff is that the LLM has to know to faithfully convey the URI representing that resource to subsequent tool calls. This requires training, which is a far cry from the simple adapter one can write to translate from ChatGPT tool use calls to MCP tool calls.

Similarly, server-sent events requires servers proactively keep a sideband connection to clients open in case they want to send a notification for tool list or resource changes. Many clients don't implement support for this, so it's kind of a dicey proposition for servers to support. Not supporting these notifications costs nothing, while supporting the feature is not guaranteed to pay off.

There's a risk that features like these are annealed out as adoption of the protocol grows, and that's a shame because there are benefits to them.

our jobโ€‹

in which chris spins a sign pointing you to mcp.run for memorial day dealsโ€‹

So if I were to leave you with something, it would be this: MCP's problems aren't caused by a lack of WebSockets. A lot of what you see about the protocol today is the result of rapid adoption, and the danger of rapid adoption is that useful features can be shaved off or implemented in insecure fashion. It's easy to look at STDIO servers and identify a thousand-and-one ways in which they're insecure, but that's not where the puck is heading. Our collective job, as the folks voting with our feet, is to try to shift the protocol's momentum with our implementations so that the when the protocol sets up, it's easy for everyone to use safely.