quickoauth
← Guides

Add OAuth to your MCP server with Stytch Connected Apps

Stytch as the authorization server for an MCP connector: magic-link signup, the Connected Apps DCR + CIMD toggles, the project-domain metadata trap, and validating Stytch JWTs in a Worker. Executed live, screenshots included.

stytch · updated 2026-08-22


Stytch’s Connected Apps feature turns a Stytch project into an OAuth 2.0/OIDC identity provider, exactly what an MCP server needs in front of it. We signed up fresh, enabled it, registered a client the way Claude would, and validated a real Stytch-issued JWT in a Cloudflare Worker. The testbed lives at https://qo-test-stytch.rough-disk-9b56.workers.dev/mcp.

stytch.com/start-now: enter your email, click the magic link it sends, create a workspace. No card, no phone number.

Stytch signup

Workspace creation form

You land in a dashboard with a default project and a Test environment.

Stytch dashboard

2. Enable Connected Apps

Sidebar → Connected AppsSettings tab:

  • Authorization URL: a page your app hosts where Stytch’s IdentityProvider UI component renders login + consent. Stytch delegates the authorize step to you, like Hydra does; unlike Auth0, there is no hosted consent page.
  • Enable dynamic client registration: turn it on. This is what lets Claude, ChatGPT, and Grok register themselves.
  • Enable CIMD: client ID metadata documents, the mechanism ChatGPT prefers over DCR.

Connected Apps settings

3. Use the project domain, not the legacy API path

Every Stytch project has two metadata surfaces, and only one of them is MCP-ready:

legacy path (test.stytch.com/v1/public/{project_id}) project domain (https://….customers.stytch.dev)
/.well-known/openid-configuration
/.well-known/oauth-authorization-server ❌ 404
registration_endpoint in metadata ❌ missing (even with DCR on!) /v1/oauth2/register
issuer stytch.com/project-test-… (not a URL) the domain itself ✅
refresh_token in grant types

Find your project domain under Project Settings → Project Domain (ours: orchid-legend-8092.customers.stytch.dev). Advertise the project domain in your protected-resource metadata’s authorization_servers; an MCP client pointed at the legacy path will never find the registration endpoint.

Verify DCR works before wiring anything else:

curl -X POST https://YOUR-DOMAIN.customers.stytch.dev/v1/oauth2/register \
  -H 'content-type: application/json' -d '{
  "client_name": "probe",
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "token_endpoint_auth_method": "none"
}'
# → 201 with connected-app-test-… client_id, no null fields

4. Validate Stytch tokens in your MCP server

Stytch access tokens are RS256 JWTs against the project JWKS (https://YOUR-DOMAIN.customers.stytch.dev/.well-known/jwks.json), but two claims differ from what audience-checking middleware expects:

  • iss on tokens is the legacy string stytch.com/project-test-… (not the project domain). Accept both forms.
  • aud is the project or client ID, never your MCP URL. Don’t enforce aud === resource; validate issuer + signature + expiry, and use scopes for authorization.

Everything else is the standard contract: serve /.well-known/oauth-protected-resource naming the project domain, answer with 401 + WWW-Authenticate: Bearer … resource_metadata="…", reject bad tokens with 401.

5. Headless testing with an M2M client

For CI, mint real project JWTs without a browser using a machine-to-machine client (created with your project secret, then exchanged via the same /oauth2/token endpoint):

curl -X POST https://test.stytch.com/v1/m2m/clients \
  -u "$PROJECT_ID:$SECRET" -H 'content-type: application/json' \
  -d '{"client_name":"ci"}'

curl -X POST https://test.stytch.com/v1/public/$PROJECT_ID/oauth2/token \
  -d grant_type=client_credentials -d client_id=$M2M_ID -d client_secret=$M2M_SECRET

The token that comes back is signed by the same JWKS your resource server trusts; our RL suite uses exactly this to prove the testbed validates Stytch-issued JWTs end to end on every deploy.

Next steps

To finish the setup:

  1. Host the authorization page (IdentityProvider component) at the URL you configured, and add your app’s domain under Frontend SDK → authorized domains.
  2. Run the four-curl self-test from the connector checklist, but against the project domain for the metadata steps.
  3. Connect in claude.ai under Customize → Connectors, then repeat for ChatGPT and Grok per one server, three connectors.

The Stytch Connected Apps prompt (stytch-connected-apps-mcp) packages all of this for your agent. If registration fails, debugging connector failures has the probe order; for a contrast in trade-offs, see the Auth0 and Supabase walkthroughs.

Metadata

Commit
c6eb99c
Browser
Current Time
Dimensions
Source
guides
Last Updated
2026-08-22