Shipping a Remote MCP Server: The Parts the Docs Skip
The MCP docs show a local server. Shipping a remote one is a different job. Here are the parts I learned the hard way: transport, auth, state, testing.
The MCP tutorials are great at one thing: a local server running on your laptop, talking to one client over standard input and output. Twenty minutes, done.
Then you try to ship a remote one — a real service, on the internet, that agents you do not control connect to — and you find the tutorial stopped exactly where the interesting problems begin. This post is the gap. Here is what I had to work out the hard way after deciding which tools to expose.
First: local and remote are different animals
A local MCP server trusts its world. It runs on your machine, for one user, over a pipe. You can be relaxed.
A remote MCP server trusts nothing. It is a public service. Anyone might connect. Requests arrive over the network, in parallel, from clients with different versions and different intentions. Almost everything that bit me came from forgetting that difference.
So the real list of concerns is not "how do I define a tool." It is transport, authentication, state, and testing. The MCP spec covers the shapes; the operational reality is yours to learn.
Transport: it is HTTP, and that changes things
A local server uses stdio. A remote one speaks over HTTP — in current MCP, the streamable HTTP transport. That sounds minor. It is not.
Over HTTP you now have to think about:
- Many clients at once. stdio is one caller. HTTP is many, in parallel. Your handlers have to be safe when two requests land together.
- Connections that drop. Networks are rude. A long-running call can be cut mid-stream, and your server has to not fall over when it is.
- Partial reads. Anything that reads a request body has to assume it might arrive in pieces, or be truncated. Never rebuild a value from a chunk you assumed was whole.
None of this shows up on the laptop. All of it shows up in production.
Authentication: the docs wave at this
The local tutorials barely mention auth, because locally there is nothing to protect. Remotely it is the whole game: a public endpoint that can do things on your behalf is exactly what you must not leave open.
What I learned:
- Decide who may connect before you ship, not after. An open MCP endpoint is an open door to every tool behind it.
- Scope the token to the caller. A connecting agent should only reach the tools it is allowed to, not the whole surface.
- Keep the dangerous tools behind more than a connection. As I wrote before, reads can be generous; anything that changes state — and anything near money — needs a real check, not just "you reached the endpoint."
Treat the connection as the start of trust, not proof of it.
State: the quiet trap
Locally, keeping a bit of state in memory is fine — one process, one user. Remotely it is a trap. If you hold per-session data in process memory and then run more than one instance (which you will, the moment you care about uptime), a client's second request can land on a different instance that has never heard of it.
The fix is boring and correct: keep session state somewhere shared, or design the tools to not need sticky memory at all. I leaned toward stateless tools wherever I could — each call carries what it needs — because stateless things are so much easier to run and scale. The less a request depends on "the same box answered me last time," the less there is to break.
Testing: you cannot click through it
With a local server you poke it by hand. A remote server needs real tests, because the failure modes are the ones you cannot see by clicking: a dropped connection, two requests racing, a half-read body, a client speaking a slightly different version.
What helped me:
- Test the tools directly, with the awkward inputs — empty, huge, malformed — not just the happy path.
- Simulate the network being unkind: cut a connection mid-call and confirm the server stays healthy.
- Test concurrency on purpose: fire overlapping requests and check nothing bleeds between them.
The agent on the other end will eventually do all of these by accident. Better that your test suite does them first, deliberately.
The short version
Shipping a remote MCP server is not the tutorial plus a deploy button. It is a public service, and the parts the docs skip are the ones that matter:
- Transport — HTTP means concurrency, dropped connections, partial reads.
- Auth — a public endpoint must be closed by default and scoped per caller.
- State — do not trust in-process memory; prefer stateless or shared state.
- Testing — test the unkind cases, because the agents will.
This is the kind of unglamorous infrastructure work going into Sarvex, because a flaky or open MCP server is worse than none at all — it fails in public, in front of a paying agent. Get it boring and solid, and everything above it gets to be interesting.
For how I chose which tools to put on this server in the first place, see MCP for ecommerce; for where MCP sits in the stack, see the protocol stack.
Next time: what happened when I let an agent run a checkout from end to end.