Here's a problem I didn't expect to be the blocker.
I had three model seats — ChatGPT, Gemini, Copilot — already working against one MCP server (my Canonical Vault, which does no transport credential check; governance lives server-side in branch rules). Those surfaces connect URL-only.
My newer MCP server, the Commons, accepted exactly one presentation: Authorization: Bearer <token>.
Result: three credentialed seats, zero connections. Not a permissions problem. A presentation problem.
OAuth was the obvious textbook answer — and the wrong first move. The seats didn't need a new identity system. They needed a door shaped like the one they already walk through.
Ship a per-seat secret that can be presented in whatever shape the client can actually send, all resolving through one identity function:
| Presentation | Shape |
|---|---|
| Bearer (original) | `Authorization: Bearer <secret>` |
| URL path | POST /mcp/s/<secret> |
| URL query | `POST /mcp?k=<secret>` |
| API key header | `X-Api-Key: <secret>` |
| Basic password | `Authorization: Basic <user>:<secret>` |
Rules that make this safe enough to ship:
003_door_credentials). The plaintext is shown once, at mint time, in a connection sheet the operator hands to that seat's surface.authenticate() path. No shape gets a weaker check. A bogus secret returns unauthenticated in issue-door / revoke-door. Revoking a seat's door does not touch its root token, and vice versa. I verified this on a throwaway door: revoked door → revoked, root token unaffected.
Commons v0.5.0, in production:
/mcp/s/<secret>, and ?k= through the one identity functionwhoami through the door itself + every alternate shape, per seat0.5.0
Remaining step is deliberately manual: add each door URL in that seat's own surface. Credentials cross into another vendor's UI only by the operator's hand.
Use door credentials when: Don't use them when:
I demoted OAuth and a REST shim to optional hardening, in that order. The unblocking move was meeting the clients at the presentation they already had — without weakening the one rule that matters: identity is a credential, never a claim, and the server derives it the same way no matter which door you came through.
Part of the "Building Governed Agents" series. Previous: "The Commons: Claims, Never Commands." Code context: the Commons is an append-only MCP pool where models exchange claims under governance — source currently in a private repo, protocol described in the series. Paper: doi.org/10.5281/zenodo.23242126 · Writing: liminaljermo.substack.com
Hit the same wall with an MCP client that can't send headers? I want to hear which shape you shipped.