DEV Community

Kiell Tampubolon
Kiell Tampubolon

Posted on

What actually changes when an MCP server leaves your laptop

An MCP server that works locally is not the same as an MCP server that runs behind a gateway other people can reach. Every tutorial shows the local case because the local case is easy. The interesting problems only appear the moment another client, another tenant, or another team needs to talk to it.

I built a small local lab to make those problems visible. This post is what I learned building it.

The local-to-hosted gap

On a laptop, an MCP server talks to one client. There is one user, one workspace, and one trust boundary: your own machine.

Behind a gateway, that changes fast:

  • Multiple clients, each with a different token.
  • Multiple tenants, each with data that must not leak.
  • Write tools that need approval before they run.
  • Failure modes that a single-user local demo never exercises.

None of this is exotic. It is just the ordinary shape of anything that gets handed to a team.

Four controls that make the difference

1. Per-request authentication

A gateway cannot trust a long-lived session. Every request carries a bearer token, and the gateway validates it before the tool registry is even consulted. Wrong token means 401. Wrong path means 404. That sounds obvious. A surprising number of demos skip both.

2. Tenant isolation at the gateway

Tenant scoping belongs at the edge, not inside each tool. If every tool has to remember to check the tenant, one tool will forget. When the gateway enforces it before the tool is called, a tool physically cannot return another tenant's data.

The pattern is simple: the token carries the tenant, the gateway attaches the tenant to the request, the tool only reads from that tenant's fixtures. The tool never sees the raw tenant ID from the caller.

3. Write tools require a request-bound approval

Read tools can run without ceremony. Write tools cannot. The approval is bound to a specific request ID and tool name, and it expires. A stale approval does not work. An approval for documents.archive does not authorize documents.delete.

This is what turns a demo into something a team can actually ship.

4. Structured logs with a request ID per call

Every call gets a request ID. Every log line carries it. Secrets are redacted before the line is written. When something goes wrong, the log shows the sequence of decisions that led to the failure, not just that the failure happened.

What the lab does

A streamable HTTP gateway sits in front of a two-tool registry: documents.list (read) and documents.archive (write, requires approval). A target smoke client talks to the gateway the same way a real MCP client would.

Deterministic failure injection lets a reviewer trigger each failure class on demand:

  • 401 for a bad token.
  • 404 for a wrong path.
  • 408 for a slow tool.
  • 503 for an unavailable tool.

The test suite is small: 13 runtime tests. They cover authentication, tenant boundary, approval binding, and every failure status. No production dependencies, no external services, no deployment.

What this pattern gives you

If you are moving MCP from a proof-of-concept to something a team can use, this is the checklist:

  • Auth per request, not per session.
  • Tenant at the edge, not inside tools.
  • Approvals that expire and are bound to a request.
  • Logs that answer "what did the agent try to do and why was it allowed".
  • Failure classes tested, not just discovered.

None of this is a framework. It is four decisions and a runbook.

The code is at github.com/glatinone/mcp-local-to-hosted-deployment-fix. If your team is stuck on this exact transition, I take short implementation sprints on it. kielltampubolon.id

Top comments (0)