Authenticating MCPs: three ways we do it – Matthew Johnston – Retail Data Science and Biology, PhD
Authenticating MCPs: three ways we do it
MCP authentication gives us more than just security: we use it for dynamic tool registration and personalisation. Inspired by auth errors on the Kaggle MCP.
TL;DR
Claude.ai connectors only take a URL - no auth-header field, and no way to start OAuth themselves.
At Jollyes we authenticate Claude.ai three ways: OAuth 2.0 over SSO (the main one), no auth (when we’re happy to share), or a hash in the URL.
Knowing who is calling allows more than security. It unlocks my favourite MCP trick: dynamic tool registration - a different set of tools, and personalised tool descriptions, per user.
I’ve subscribed to Kaggle’s emails for over ten years and only entered a competition once. Yesterday, one caught my eye - cell tracking during development - because it’s closely aligned to a postdoc friend’s work. She’s spent the last several years counting and measuring plant cells in MorphoGraphX, and in the last month has made huge strides building custom viewers and human-in-the-loop tools with Claude Code (more on that idea in another post soon). I thought I’d try the same approach in this Kaggle competition.
Naturally, the first job - after being amazed I’d bagged the biologist username years ago - was downloading the competition data. I was very excited to see Kaggle ships an MCP to allow agents to do this for me.
Installing the MCP into Claude.ai - which nicely syncs connectors and auth across the web app, the phone app, and Claude Code CLI - was plain sailing. Until I asked, “download the competition data”: 403, no access. Every non-OAuth endpoint worked; every authenticated one didn’t.
To be fair to Kaggle, I hadn’t read their docs. When I did:
If your client is not OAuth 2.0 compliant, you can also use token authentication.
So I made a token, because neither Claude.ai nor Claude Code will drive Kaggle’s OAuth for me:
Some resources or endpoints require authorization. To unlock full access you can authorize using OAuth 2.0.
Gemini CLI - simply run: /mcp auth kaggle
Other clients/IDEs - for clients without a command to initiate auth discovery, you can call the authorize tool.
From here I had two choices. Reinstall the MCP locally in Claude Code with the token baked into a header - and lose the cross-platform sync:
"mcpServers": {<br>"kaggle": {<br>"command": "npx",<br>"args": [<br>"mcp-remote",<br>"https://www.kaggle.com/mcp",<br>"--header",<br>"Authorization: Bearer YOUR_TOKEN"
Or just let Claude Code use the Kaggle CLI - that was easiest.
Now, this is really a Claude.ai limitation, not a Kaggle one. But Claude.ai is what we use at Jollyes Pets, and what I use personally to reach my own MCP from web Claude Code linked into GitHub repos (also another post to write). Thus, I thought it would be useful to share how to access MCPs using Claude.ai.
1. The main one - OAuth 2.0
This runs exactly opposite to the example above. Claude.ai can do OAuth - it just won’t kick the flow off itself the way Kaggle expects (a command, or calling an authorize tool). Instead it responds to a challenge: your MCP returns a 401 pointing at its /.well-known/ metadata1, and Claude discovers the rest. If your identity provider supports Dynamic Client Registration, that’s all it takes - paste the URL and go. We use Entra, whose dynamic registration is locked down, so we pre-register the app and hand Claude a Client ID and Secret (which, wisely, Kaggle don’t hand out). Either way, the auto-discovery and OAuth machinery lives on your server.
This works fantastically well for Jollyes. We already SSO into Claude.ai through Entra, so the natural flow is to use that same SSO to reach the MCP. The huge benefit, beyond security, is that we can see exactly who is using the MCP and for what.
The catch is non-domain users - subcontractors on other Entra tenants - who still need access. We could have built multi-tenant auth on the backend; more simply, we add their accounts as guest users in our own Entra.
There’s a side benefit too. Because the MCP sits behind SSO in Claude.ai, every session a user spawns - whether or not they touch the MCP that session - triggers a /validate call. This means we can measure overall AI usage across the business, aside from MCP usage.
2. The easy one - no auth
Sometimes we’re happy to share!
3. The neat one - a token or a hash
You might say this is exactly what Kaggle offers by adding a token into a header at set-up time, and you’d be right. I hit the same wall I did with the Kaggle MCP: I really wanted my personal MCP available to web Claude Code2 (as it links into Github directly, meaning you can make changes to any repo in the phone app), and Claude.ai connectors take only a URL. The work around was to allow the token as a query parameter3:
https://mcp.matthew-johnston.com/mcp?token=XXX
We follow the same pattern at Jollyes wherever the tools are quick, short-lived, or...