Endpoint reference

mcpauth exposes a standard OAuth 2.1 authorization server surface, plus one mcpauth-specific endpoint for server-to-server token minting. Every endpoint below is real, built, and tested — this page documents exact request and response shapes, not aspirational API design.

All examples use https://your-issuer.example.com as the issuer base URL — replace it with your mcpauth instance URL (find yours in the dashboard).

1. Discovery document

GET /.well-known/oauth-authorization-server — RFC 8414. Returns the URLs of every endpoint below, so an MCP client only needs to know your issuer URL to find the rest.

Auth: none. Params: none.

Response: a JSON discovery document with fields including issuer, registration_endpoint, authorization_endpoint, token_endpoint, revocation_endpoint, and introspection_endpoint.

curl https://your-issuer.example.com/.well-known/oauth-authorization-server

2. Register a client

POST /api/oauth/register — RFC 7591 Dynamic Client Registration. This is the endpoint almost no other OAuth provider implements, which lets MCP clients register themselves programmatically instead of a human pre-configuring a client ID.

Auth: Authorization: Bearer <project registration secret>.

Body (JSON):

  • redirect_uris (required) — array of allowed redirect URIs.
  • client_name (optional) — human-readable client name.
  • token_endpoint_auth_method (optional) — client authentication method for the token endpoint.

Response: the registered client record, including a client_id to use in the authorize and token requests below.

curl -X POST https://your-issuer.example.com/api/oauth/register \
  -H "Authorization: Bearer $MCPAUTH_REGISTRATION_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": ["https://client.example.com/callback"],
    "client_name": "My MCP Client",
    "token_endpoint_auth_method": "none"
  }'

3. Authorize

GET /oauth/authorize — the browser-facing OAuth 2.1 authorize endpoint. A human is redirected here, signs in via GitHub, sees a consent screen, and approves or denies access.

Auth: none from the client (the human authenticates interactively in the browser).

Query params:

  • response_type=code
  • client_id
  • redirect_uri
  • code_challenge
  • code_challenge_method=S256
  • scope
  • state

PKCE (code_challenge / code_verifier) is mandatory, not optional — OAuth 2.1 removes the plain authorization-code flow without PKCE that OAuth 2.0 allowed. Every authorize request must include code_challenge_method=S256.

Response: a redirect to your redirect_uri with code and state query params on success.

GET https://your-issuer.example.com/oauth/authorize?
  response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https://client.example.com/callback
  &code_challenge=CODE_CHALLENGE
  &code_challenge_method=S256
  &scope=mcp
  &state=RANDOM_STATE_STRING

4. Exchange or refresh a token

POST /api/oauth/token — form-encoded (application/x-www-form-urlencoded). Supports two grant types.

Auth: per request body (client credentials embedded in the form fields, per grant type below).

grant_type=authorization_code

  • code
  • redirect_uri
  • code_verifier
  • client_id
curl -X POST https://your-issuer.example.com/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTH_CODE" \
  -d "redirect_uri=https://client.example.com/callback" \
  -d "code_verifier=CODE_VERIFIER" \
  -d "client_id=CLIENT_ID"

grant_type=refresh_token

  • refresh_token
curl -X POST https://your-issuer.example.com/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=REFRESH_TOKEN"

Response (both grant types, JSON):

{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "scope": "mcp"
}

5. Introspect a token

POST /api/oauth/introspect — RFC 7662 token introspection. Your MCP server's backend calls this to check whether an access token presented by a client is still valid, and to read the identity and scopes attached to it.

Auth: Authorization: Bearer <project registration secret>.

Body (form-encoded):

  • token

Response (JSON):

{
  "active": true,
  "scope": "mcp",
  "client_id": "...",
  "sub": "...",
  "exp": 1735689600,
  "token_type": "Bearer"
}

If you're using the Express SDK, you generally don't call this endpoint directly — the mcpAuth() middleware handles verification (with caching) for you.

curl -X POST https://your-issuer.example.com/api/oauth/introspect \
  -H "Authorization: Bearer $MCPAUTH_REGISTRATION_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=ACCESS_TOKEN"

6. Revoke a token

POST /api/oauth/revoke — RFC 7009 token revocation.

Body (form-encoded):

  • token
  • client_id
curl -X POST https://your-issuer.example.com/api/oauth/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=ACCESS_TOKEN" \
  -d "client_id=CLIENT_ID"

7. Server-to-server token minting

POST /api/oauth/token/exchange — mcpauth-specific, not an RFC grant type. Lets a Project's own backend, which already knows who its logged-in user is, mint an access token directly, skipping the GitHub-login and consent-screen flow entirely.

Auth: Authorization: Bearer <project registration secret>.

Body (JSON):

  • client_id
  • subject
  • scopes

Response: the same token shape as /api/oauth/token.

curl -X POST https://your-issuer.example.com/api/oauth/token/exchange \
  -H "Authorization: Bearer $MCPAUTH_REGISTRATION_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "CLIENT_ID",
    "subject": "user_12345",
    "scopes": ["mcp"]
  }'

For the full explanation of when to use token minting instead of the interactive authorize flow, see server-to-server token minting.