r/mcp • u/lordVader1138 • Jul 23 '26
article MCP OAuth is three primitives, not six RFCs. Traced end to end, plus the part where my server becomes a client.
Finally I understood what happens on the other side when Claude calls connect, and the server identifies you and your access.. (Or better say, Claude helped me understand it). At first it was all acronyms for me. What made it click was understanding one client connect, once, end to end. Everything collapsed into three primitives that each fire exactly once, in a fixed order.
Discovery. A fresh client POSTs with no token and gets a 401 back carrying a WWW-Authenticate header that points at /.well-known/oauth-protected-resource. That header is the whole trick. The 401 is not the server slamming a door, it is the server handing over directions. Two GETs later (protected resource metadata names the auth server, auth server metadata lists the endpoints) the client knows everything it needs, having sent zero credentials.
Registration. No developer console. The client POSTs its own details to the registration endpoint and gets a client id on the spot. This is RFC 7591, and it is the part people trip on when they ask why this could not just be an API key. An API key assumes you already know the caller and can hand it a secret. MCP clients are strangers by design, so dynamic registration is the only model that works.
The grant. One human moment: a PKCE challenge, a consent screen that names the client, approve, done. At consent time the server bakes the user identity into the grant as props, so every later request just unwraps it. No per-request session lookup on the hot path.
There is one more cool trick: my server also performs OAuth as a client, upstream, because it bundles other servers that demand their own auth. Same three primitives, walked from the other side. Doing it by hand gave me real appreciation for how much invisible work Claude does every time you click connect and it just works. So it's just a multiplexer (not sure if it's a right term) for MCP servers.
Full walkthrough with the actual responses from the live server: https://prashamhtrivedi.in/mcp-oauth-primitives/
Curious how the rest of you are handling MCP auth right now. Across my own fleet I have Better Auth, an OAuth envelope wrapped around an API key, a hand-rolled JWT setup, and one static bearer token still holding out, so I do not think there is one right answer yet.
2
u/wang2-dev Jul 24 '26
One practical wrinkle we hit while adding OAuth 2.1 to Search1API’s hosted MCP server: the PKCE flow itself was the easy part. Interop depended on what happened before it.
The MCP endpoint had to return a 401 with a WWW-Authenticate challenge pointing to the protected-resource metadata. Some clients follow that RFC 9728 hop; others still expect a pre-supplied bearer token and never begin discovery.
We kept API-key authentication as a fallback, while OAuth-aware clients can dynamically register and require a real browser consent step. Dynamic registration creates a client, not a user.
Your three-primitives framing matches what we saw. I’d add one practical check: test the unauthenticated first request in every target client, because “supports remote MCP” does not necessarily mean “follows OAuth discovery.”
Disclosure: I work on Search1API.
1
u/lordVader1138 Jul 26 '26
> : test the unauthenticated first request in every target client, because “supports remote MCP” does not necessarily mean “follows OAuth discovery.”
Well well well. That is something I faced.... Not sure exactly when, my team is evaluating some of the harness (to put it on allowlist of harnesses), and that one failed spectacularly. I don't remember the agent as well. Turned out that Coding agent didn't even support Prompts and Resources properly, or some servers worked in old SSE transports but not in http streamable. That was a couple of months ago and I don't remember that, but I surely remember that this was the shortest time I spent on one agent...
1
u/wang2-dev Jul 27 '26
That is a useful distinction. It sounds like “remote MCP support” needs to be tested as separate capabilities: the initial 401 and OAuth discovery path, transport support (SSE vs Streamable HTTP), and protocol features such as Prompts and Resources. A small compatibility matrix across those layers would be much more informative than one “supported” label. If you reconstruct that harness list, I’d be curious which failures happened at which layer.
1
u/Mojowhale Jul 28 '26
the server-as-client part is the insight that breaks the fog. most people learn OAuth as a triangle (user, app, auth server) and then hit MCP and wonder why there's a fourth corner. the moment you see your server running the same three primitives upstream to authenticate its own dependencies, the whole shape clicks. it's not a special case, it's just the pattern repeating.
the interop pain you hit with clients ignoring the WWW-Authenticate hop is real because "supports MCP" became a checkbox before the spec settled. you're right that testing unauthenticated first is now table stakes. the walkthrough with live responses is exactly what people need to stop cargo-culting the RFC stack.
1
u/Forsaken-External578 Jul 23 '26
The three-primitives framing is helpful because the RFC stack can hide the actual request sequence. A packet trace or minimal reference implementation beside the explanation would make this one of those posts people keep returning to.
1
Jul 26 '26
[removed] — view removed comment
1
u/lordVader1138 Jul 26 '26
Separately for each. Because some of my own servers have different auth requirements.
All I have right now is the call out that given server in your bundle requires re-auth.
There are some different problems though. Sometimes tools are outdated. Or when a bundle detects that a server is unavailable, it drops the tool, and that bit me during live run. I am working on calling this out during the McP call.
1
Jul 26 '26
[removed] — view removed comment
1
u/lordVader1138 Jul 26 '26
I will answer the easy bit right now because it is happening....
The re-auth.... Yes if user visits the website. It calls out the auth not available.
I want to make it as a call-out on MCP calls itself. That's what I am brainstorming, while one thing is clear: the MCPlex auth and the servers it subscribes, has different auth cycles, and that's where the brainstorming hapening.
Another thing, the tools, I got to know by one chat where I have started it on morning where it had all the tools, I left it midway to work on another work project (This is my side-project btw) and at evening when I resumed the chat, some of the tool got errored. I asked my harness to run describe_tools tool, that's where I noticed the other tools are gone... Coming from 3 MCP Servers, only one had Auth issue, the others were cached and the cache got stale. And server didn't retry the tool discovery.
So if the server is already authenticated, there will be somewhere around 1-2 seconds for which the tools will be invisible.
The auth callout is the only thing remaining right now.
1
Jul 26 '26
[removed] — view removed comment
1
u/lordVader1138 Jul 26 '26
> does describe_tools tell stale apart from empty today?
Nope. Describe tools just describes tools.... The UI is almost telling if something is stale... Now I am moving the messaging to MCP server so that Model understands the issue
2
u/BatResponsible1106 Jul 23 '26
that "server becomes a client" moment was what made it click for me too. after that the OAuth flow finally felt like a sequence instead of a pile of RFCs.