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-server2. 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=codeclient_idredirect_uricode_challengecode_challenge_method=S256scopestate
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_STRING4. 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
coderedirect_uricode_verifierclient_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):
tokenclient_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_idsubjectscopes
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.