Part 7 of the MCP in Practice Series · Back: Part 6 — Your MCP Server Worked Locally. What Changes in Production?
Why This Part Exists
You can build an MCP server locally and never think much about transport or authentication. The host launches the server, communication stays on the same machine, and trust is inherited from that environment. But once the same server needs to be shared, deployed remotely, or accessed by more than one client, two design questions appear immediately: how will clients connect to it, and how will it know who is calling?
Part 6 gave you the production map — every component, every boundary, every ownership split. This part zooms into the first two practical layers of that map: transport and auth. Not as protocol theory, but as deployment decisions that shape how your server operates.
This is not about implementing OAuth from scratch. It is about understanding what changes when your MCP server becomes remote, and where the SDK helps versus where your application logic begins.
Two Transports, One Protocol
Left side: local, simple, no network. Right side: remote, shared, everything changes. The protocol between them is identical.
The MCP specification defines two official transports: stdio and Streamable HTTP. Both carry identical JSON-RPC messages. What differs is how those messages travel and what operational responsibilities come with each choice.
The decision between them is almost always made by deployment shape, not by preference. If the server runs on the same machine as the client, stdio is the natural choice. If the server is a shared remote service, Streamable HTTP is usually the practical option. Most developers do not choose a transport — the deployment chooses it for them.
When stdio Is Enough
With stdio, the host launches the MCP server as a child process on the same machine. There is no network involved, and trust is largely inherited from the local host environment. For single-user tools, local development, and desktop integrations, this is the right default.
Stdio stops being enough when a second person needs access to the same server, or when the server needs to run somewhere other than the user's machine. At that point, the deployment shape changes, and the transport has to change with it.
When Streamable HTTP Becomes Necessary
Once the TechNova order assistant needs to serve the whole support team, it moves off a single laptop and onto a shared server. Instead of stdin and stdout, it exposes a single HTTP endpoint — something like https://technova-mcp.internal/mcp — and accepts JSON-RPC messages as HTTP POST requests. From the team's point of view, the change is simple: instead of everyone running their own copy, everyone connects to one shared deployment.
If you already work with HTTP services, this should feel familiar. Streamable HTTP is not a new web stack — it is the MCP protocol carried over the same HTTP deployment model your infrastructure already understands. The difference from a regular HTTP API is that you do not design the request contract yourself — MCP standardizes the endpoint, the message format, and the capability discovery so every client and server speaks the same language. It uses a single endpoint for communication and can optionally stream responses over time, which makes it a good fit for shared remote deployments without changing the MCP protocol itself. The server can assign a session ID during initialization — but a session ID tracks conversation state, not caller identity.
Once that happens, your MCP server stops being a local integration and starts behaving like shared infrastructure. The server now listens on a network, multiple clients connect concurrently, and nobody inherits trust from the operating system anymore. The messages are still the same JSON-RPC payloads — but everything around them has changed.
What Changes Once You Go Remote
The moment MCP crosses a network boundary, the server has to start verifying who is calling. Locally, the operating system controlled access. On a network, that implicit trust has no equivalent. Someone or something has to prove the caller's identity before the server processes a request — and even after identity is established, you still need to decide what each caller is allowed to do.
Going remote also introduces backend credential separation — your server's credentials for reaching downstream systems must stay distinct from the user's token. If you pass the user's token through to a backend API, you blur the line between caller identity and server privilege, which is exactly how access-control mistakes happen. Part 6 mapped out the broader operational concerns. For this part, we are focusing on the first and most immediate: how auth actually works when a client connects to your remote MCP server.
How Auth Works in Practice
Three phases, three colors. Red: rejected without a token. Blue: gets a token from the auth server. Green: retries with the token and gets through.
In practice, remote MCP auth has three phases.
First, the client sends a request to the MCP server without a token. The server responds with a 401 and tells the client where to find the authorization server. This is the rejection phase — the server is saying: I cannot let you in without proof of identity.
Second, the client redirects the user to the authorization server. The user logs in, consents to the requested access, and the authorization server issues an access token. The MCP server is not involved in this step at all. It never sees the user's password. The login happens entirely between the client, the user's browser, and the authorization server.
Third, the client retries the request, this time carrying the token. The MCP server validates the token: was it issued by a trusted authorization server? Has it expired? If the token passes validation, the server processes the request.
The key architectural point: the authorization server issues tokens. The MCP server validates them. These are separate systems, typically managed by separate teams. The MCP server's role is to protect its own resources — not to manage user identity.
And here is the gap that catches developers by surprise: the token proves who the caller is. It does not decide what each tool call is allowed to do. A token might carry a scope like tools.read, but deciding whether that scope maps to get-order-status, cancel-order, or both is entirely your responsibility.
This is where the confusion usually starts: a valid token feels like the end of the problem, but it only solves identity.
What the SDK Handles vs What You Still Build
The left column is what you get for free. The right column is what you build. The line between them is the most important boundary in this article.
The MCP SDK and standard auth libraries handle the authentication machinery. On the client side, the SDK provides the OAuth client, detects the 401, discovers the authorization server, and runs the authorization code flow with PKCE. It also handles token storage and refresh. On the server side, the SDK provides integration points for token validation. This is the plumbing that makes the three-phase flow work without you building it from scratch.
What the SDK does not handle — and what remains your responsibility — is everything after the token arrives. You still have to interpret what that caller identity means in your application, map scopes to specific tools, and decide whether this caller can invoke cancel-order or only get-order-status. You also own the backend credentials your server uses to reach downstream systems, and you need to enforce least privilege so the server accesses only what it needs.
Here's the line that matters: authentication is proving who you are. The SDK handles that. Authorization is deciding what you are allowed to do. You build that.
Practical Decision Guide
Six questions that will get you to the right deployment decision.
Single user, same machine?
→ Start with stdio. There is no reason to add network complexity for a local tool.
Shared team, remote deployment?
→ Move to Streamable HTTP. One shared endpoint replaces duplicated local copies.
Handles user-specific data or actions?
→ Add auth. Use an external authorization server — do not build token issuance into the MCP server.
Different users need different tool access?
→ Design scope-to-tool authorization. This is application logic, not something the SDK provides.
Server calls backend APIs or databases?
→ Manage those credentials separately from user tokens. Never pass a user's token through to a backend service.
Need audit trails, rate limiting, or centralized monitoring?
→ Consider a gateway or proxy. This is typically a platform team decision.
Three Takeaways
First, transport is a deployment decision, not a protocol decision. Stdio for local, Streamable HTTP for remote. The messages stay the same. Everything else changes.
Second, auth is not a feature you add — it is a consequence of going remote. The MCP server validates tokens but never issues them. And the hardest part is not authentication. It is authorization: deciding what each caller is allowed to do with each tool.
Third, don't assume the SDK solved the whole problem for you. It handles the auth flow. You still own the access decisions, and that boundary is the part most teams get wrong when they move from local to production.
Next: MCP Security in Practice — the threats that are unique to MCP and what they mean for your deployment.
If a hands-on follow-up on building and deploying a Streamable HTTP server would be useful, let me know in the comments.



Top comments (0)