Remote servers: sessions, identity, and OAuth
Everything you have built so far trusts its transport: a stdio server serves whoever spawned it, and that is a sound assumption exactly once — on your own machine. The moment a server lives behind a URL, "whoever can reach the port" replaces "whoever spawned the process", and the questions this lesson answers become the deployment. It is also where the ecosystem's centre of gravity moved through 2025: hosted MCP servers, org-wide, many users, real identity.
What the wire looks like over HTTP, captured
The Paper Trail server, run with transport="http" and sent the same initialize from lesson 4 — this time via curl, with the HTTP layer left visible:
HTTP/1.1 200 OK
server: uvicorn
content-type: text/event-stream
mcp-session-id: b99e1ca7d2314ef6ae42c6056fdfac48
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18",
"capabilities":{"logging":{},"prompts":{"listChanged":true}, …},
"serverInfo":{"name":"paper-trail","version":"3.4.5"}}}Three things in those bytes carry the lesson. The body is an SSE event stream — the "streamable" in streamable HTTP: a plain request gets a plain JSON reply, and anything that may need to push (progress, notifications, sampling) upgrades to a stream on the same endpoint. The data: payload is the identical initialize result stdio produced — the protocol genuinely does not know which transport it is riding, which is why sixteen lessons of material transferred here unchanged. And the header the transport added — mcp-session-id — is how a stateless protocol gets a stateful conversation over HTTP: the client echoes that id on every subsequent request, and the server routes it to the right session.
Now the sentence that separates working from safe: a session id is continuity, not identity. It says "same conversation as before", never "this user is allowed here". This server, as captured, will hand a session to anything that can POST to the port. On localhost that is fine. On a URL, it is an open bookstore back office.
Identity: OAuth 2.1, and which role you are
MCP's authorization spec settled on OAuth 2.1, and the part worth engraving is the role assignment: *an MCP server is a resource server. It receives requests bearing an access token, validates the token, and serves or refuses. It is not* supposed to be an identity provider — issuing tokens, holding passwords, running login pages is the authorization server's job (your org's IdP, or a service in front). Early remote servers routinely collapsed the two roles into one hand-rolled login, and that design is both more work and less secure; the spec's separation lets a bookstore server outsource "who is this" to something that does nothing else.
The flow, compressed to its five useful sentences: the client calls the server without a token and receives 401 Unauthorized plus a pointer to protected resource metadata — a well-known document saying "here is the authorization server that vouches for me". The client (the host, really — this is a user-facing flow) runs the OAuth dance with that authorization server: browser opens, user logs in and consents, client receives an access token. Every subsequent MCP request carries Authorization: Bearer <token>. The server validates the token on each request — signature, expiry, audience — and now knows who is asking, which means tools can finally behave per-user: Meera sees Meera's orders. Dynamic client registration covers the "the server has never met this client before" case, which for a protocol whose whole point is any-client-any-server is the common case, not the edge.
You will implement almost none of this by hand — SDKs and gateways carry the mechanics — but you will configure all of it, and misconfiguration here is the new "server never appears". The debugging instinct from lesson 11 transfers directly: a 401 with metadata is the handshake working, not failing; a 403 means authenticated but not authorised; a token that validates nowhere usually has the wrong audience.
The remote-server checklist
Four decisions before any MCP server gets a URL, each one a lesson you already have. Who may connect — bearer auth at minimum, OAuth for anything multi-user; the session id is not an answer to this question. What each identity may do — per-user scoping inside tools, now that tools know who is asking; least privilege (lesson 14) becomes per-token instead of per-server. What the blast radius is — the lesson 15 container, plus network egress rules, because a remote server is on nobody's laptop and its trifecta letters (lesson 13) are now shared by every connected user. What changes without consent — a remote server can deploy new descriptions to every user at once; the rug-pull surface (lesson 13) scales with your user count, so change logs and pinned versions stop being courtesy and become policy.
Try this: run your Paper Trail copy with transport="http" and send the lesson 4 frames through curl. Finding the mcp-session-id header yourself, then noticing that nothing asked who you were, is this lesson in two minutes of terminal time.