Skip to content

docs(mcp): point examples at the HTTP transport - #18

Merged
lakhansamani merged 13 commits into
mainfrom
docs/mcp-http-transport
Aug 11, 2026
Merged

docs(mcp): point examples at the HTTP transport#18
lakhansamani merged 13 commits into
mainfrom
docs/mcp-http-transport

Conversation

@lakhansamani

Copy link
Copy Markdown
Contributor

Points the examples at the remote MCP transport shipped in authorizerdev/authorizer#757.

with-mcp

Its bonus section described authorizer mcp (stdio) as Authorizer's built-in MCP server. That is now the deprecated one. It leads with --mcp-enabled, shows the discovery chain a client walks unaided, and states the two client-registration facts that otherwise surface as an undiagnosable 401:

  • there is no RFC 7591 dynamic client registration, so a pre-created client ID is the path
  • Claude Code needs both loopback redirect URIs registered, because it binds an ephemeral port

The main body of the example — protecting your own MCP server with Authorizer-issued tokens — is unchanged and still correct.

with-agent-permissions stays on stdio, deliberately

This one cannot move, and the README now says why rather than leaving it looking neglected.

It drives the MCP tools with an RFC 8693 delegated token, and the HTTP surface does not accept those: delegated tokens are stateless, so they fail the session check the HTTP path requires. Widening that path gives up the byte-for-byte comparison against a stored token and is a separate decision, tracked in the transport spec.

So agent-delegation over MCP is a stdio-only story until that lands. Worth knowing before anyone tries to "modernise" the example and finds it 401s.

Verification

Docs-only — no example code changed. The stdio path these examples depend on still works: make smoke in the server repo exercises it end to end (mcp_stdio), and I confirmed the binary still serves it, now printing the deprecation notice.

Companion PRs: authorizerdev/authorizer#757 (server), authorizerdev/docs#85, authorizerdev/authorizer-js#54.

- Go examples: import path -> authorizer-go/v2, pin v2.2.0-rc.4;
  drop local `replace` directives now that the tag ships the
  client_credentials/client_assertion APIs (pulls authorizer-proto-go).
- Python examples: bump authorizer-py to 0.3.0rc3 (pulls authorizer-proto).
- with-rag-fga: PaginatedRequest was removed in 0.3.0rc3; use ListUsersRequest.
- with-agents-python: switch editable-local install to released authorizer-py.
- Refresh stale README/Dockerfile notes about the removed replaces.
Bump every example depending on authorizer-js or authorizer-react to
the released RCs, matching each file's existing caret pin style.

- authorizer-js ^3.2.1 -> ^3.3.0-rc.4: with-nextjs, with-nextjs-13,
  with-react-native-expo, with-express-js (direct); with-vanilla-js
  and with-vanilla-js-custom-ui via the unpkg UMD CDN URL.
- authorizer-react ^2.0.7 -> ^2.2.0-rc.3: with-nextjs, with-nextjs-13,
  with-react, with-gatsbyjs. Transitive authorizer-js dedupes up to
  3.3.0-rc.4 in each lockfile.
- No code changes needed: no example calls the flattened admin
  pagination API, sets is_multi_factor_auth_enabled on signup, or
  composes AuthorizerVerifyOtp directly, so none of the RC breaking
  changes apply.
# Conflicts:
#	with-agents-python/README.md
#	with-agents-python/demo.py
#	with-express-js/package-lock.json
#	with-express-js/package.json
#	with-gatsbyjs/package.json
#	with-go/go.mod
#	with-go/go.sum
#	with-nextjs-13/package-lock.json
#	with-nextjs-13/package.json
#	with-nextjs/package-lock.json
#	with-nextjs/package.json
#	with-python/README.md
#	with-react-native-expo/package-lock.json
#	with-react-native-expo/package.json
#	with-react/package-lock.json
#	with-react/package.json
#	with-vanilla-js-custom-ui/index.html
#	with-vanilla-js-custom-ui/login.html
#	with-vanilla-js/index.html
rc.18 DeleteUserRequest dropped `email` for `id` (a phone-only signup has
no email). The three raw-GraphQL callers now resolve the id via _user first.
The vendored gen/ stubs predated the DeleteUserRequest email->id change, so
they no longer matched rc.18. Depend on authorizer-proto-go v0.2.0-rc.1 --
the same module the Go SDK uses -- and drop ~10k lines of vendored code.

Both Go examples hit the 2.4.0 MFA offer and neither settled it: authorizer-go
keeps no cookie jar and gRPC has no cookies at all, so skip_mfa_setup could
not find its session. Each now relays the mfa_session by hand (net/http
response cookies; set-cookie response metadata over gRPC).
0.3.0rc4 has the typed skip_mfa_setup and the flattened admin pagination,
so with-python drops both 0.2.0 escape hatches and uses AuthorizerAdminClient
.users() directly.

with-agents-python and with-rag-fga never settled the 2.4.0 MFA offer, so
neither could get a token against a default server: httpx keeps the
mfa_session cookie in its jar but will not replay a Secure cookie over plain
http (--app-cookie-secure defaults to true). Both now pass it by hand, as
with-python already did.
authorizer-js ^4.0.0-rc.0 (deleteUser takes id, matching the server's
DeleteUserRequest), authorizer-react ^2.2.0-rc.7 (which requires that js
major, and moves @storybook/preset-scss out of runtime deps), authorizer-vue
1.0.0-beta.2. Locks regenerated -- each resolves a single deduped copy.
…not allow

Section 8 asserted the pre-2.4.0 behaviour: no agent type in the model and
the agent inherits the user's full authority. rc.18 fails closed instead --
the agent half of perms(agent) n perms(user) cannot be evaluated, so the
check is denied, and --fga-allow-unconstrained-agents is the opt-out for
deployments still migrating.

Also make SERVER_DIR overridable in run-server.sh and mcp-agent.mjs, so the
demos can be pointed at a specific server checkout.
rc.18 enforces RFC 6238 5.2 -- a code the server already accepted is
rejected on a second attempt. Enrollment and the login challenge run
seconds apart, inside one 30s step, so the recipe was replaying the same
code and failing with 'invalid otp'.

Also make SERVER_DIR overridable in run-server.sh.
The authorizer image runs as uid 1000. A named volume mounted at /data is
created root-owned, so the process could not create the SQLite file and
every one of these stacks died at boot with 'unable to open database file'.
Put the db under /authorizer, which the image chowns to that uid.

with-microservices: the gateway forwarded X-User-Email from req.auth.email,
but an access token carries no email claim -- profile claims are in the ID
token and at /userinfo -- so it was always empty. Forward the sub only and
say why.
with-mcp's bonus section described `authorizer mcp` (stdio) as the built-in
option. It now leads with `--mcp-enabled`, the discovery chain a client walks on
its own, and the client-registration steps that actually matter in practice —
Authorizer has no RFC 7591 dynamic registration, so a pre-created client ID is
the path, and Claude Code needs both loopback redirect URIs registered because
it binds an ephemeral port.

with-agent-permissions deliberately STAYS on stdio, and now says why: it drives
the tools with an RFC 8693 delegated token, and the HTTP surface does not accept
those. Delegated tokens are stateless, so they fail the session check the HTTP
path requires; widening it is a separate decision. Silently leaving the example
on a deprecated transport with no explanation would read as an oversight.
Tested against Claude Code 2.1.226. The OAuth path does NOT work — the client
refuses with "Incompatible auth server: does not support dynamic client
registration" and offers no manual client-id fallback. The previous text
described registering redirect URIs for an OAuth flow that cannot start.

Replaced with the static-token path, which is verified working end to end:
claude mcp list reports Connected, and tools/list and tools/call both function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant