OAuth Endpoint Reference
Every OAuth 2.1 endpoint with request/response shapes and curl examples — discovery, registration, authorize, token, refresh, revoke.
All endpoints live on the API domain. Well-behaved clients never hard-code these
paths — they read them from the discovery documents. Errors follow RFC 6749 §5.2:
a top-level {"error", "error_description"} body. Every endpoint is rate limited;
429 responses carry a Retry-After header.
| Method | Path | Purpose |
|---|---|---|
| GET | /.well-known/oauth-protected-resource | Which AS governs the MCP endpoint |
| GET | /.well-known/oauth-authorization-server | AS capabilities + endpoint URLs |
| POST | /api/mcp-oauth/register | Dynamic client registration |
| — | tokenvault.uk/oauth/authorize | Browser consent page (authorization endpoint) |
| POST | /api/mcp-oauth/token | Code exchange + refresh |
| POST | /api/mcp-oauth/revoke | Token revocation |
| POST | /api/mcp-oauth/introspect | Token introspection — see Introspection |
Discovery
curl https://api.tokenvault.uk/.well-known/oauth-protected-resource{
"resource": "https://api.tokenvault.uk/api/agents/mcp",
"authorization_servers": ["https://api.tokenvault.uk"]
}curl https://api.tokenvault.uk/.well-known/oauth-authorization-server{
"issuer": "https://api.tokenvault.uk",
"authorization_endpoint": "https://tokenvault.uk/oauth/authorize",
"registration_endpoint": "https://api.tokenvault.uk/api/mcp-oauth/register",
"token_endpoint": "https://api.tokenvault.uk/api/mcp-oauth/token",
"revocation_endpoint": "https://api.tokenvault.uk/api/mcp-oauth/revoke",
"code_challenge_methods_supported": ["S256"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"],
"token_endpoint_auth_methods_supported": ["none"]
}Note token_endpoint_auth_methods_supported: ["none"] — Token Vault only issues
public clients. There is no client_secret, ever. PKCE S256 is mandatory.
Register (RFC 7591)
curl -X POST https://api.tokenvault.uk/api/mcp-oauth/register \
-H 'Content-Type: application/json' \
-d '{
"client_name": "my-agent-runtime",
"redirect_uris": ["http://127.0.0.1:43117/callback"]
}'{
"client_id": "kX3…16-byte-urlsafe…",
"redirect_uris": ["http://127.0.0.1:43117/callback"],
"token_endpoint_auth_method": "none"
}Redirect URIs must be loopback (127.0.0.1, ::1, localhost — any port) or a
custom scheme (myapp://callback). Plain http to any other host is rejected
with invalid_redirect_uri.
Authorize (browser)
Open the authorization endpoint in a browser with standard OAuth 2.1 query params:
https://tokenvault.uk/oauth/authorize
?client_id=<from register>
&redirect_uri=http://127.0.0.1:43117/callback
&response_type=code
&code_challenge=<BASE64URL(SHA256(verifier))>
&code_challenge_method=S256
&state=<random>The user signs in, picks an agent, and consents. The browser lands on your
redirect_uri with ?code=…&state=….
Authorization codes are single-use and expire after 60 seconds. Exchange
immediately. A second exchange of the same code fails with invalid_grant.
Token — code exchange
Accepts application/x-www-form-urlencoded (spec) or application/json:
curl -X POST https://api.tokenvault.uk/api/mcp-oauth/token \
-d grant_type=authorization_code \
-d code=<code> \
-d code_verifier=<verifier> \
-d client_id=<client_id> \
-d redirect_uri=http://127.0.0.1:43117/callback{
"access_token": "tvsess_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tvrefresh_…",
"scope": ""
}client_id, redirect_uri, and the PKCE verifier must all match the values the
code was minted against, or the exchange fails with invalid_grant.
Token — refresh
curl -X POST https://api.tokenvault.uk/api/mcp-oauth/token \
-d grant_type=refresh_token \
-d refresh_token=tvrefresh_…{
"access_token": "tvsess_…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": ""
}Every refresh re-checks the agent before minting: a suspended agent returns
403 invalid_grant "Agent is suspended", a deleted agent 403 "Agent not found",
and an agent with MCP access switched off 403 "MCP not enabled". This is the
kill-switch re-check — revoking an agent in the console stops token issuance
immediately.
Revoke (RFC 7009)
curl -X POST https://api.tokenvault.uk/api/mcp-oauth/revoke \
-H 'Content-Type: application/json' \
-d '{"token": "tvsess_…", "token_type_hint": "access_token"}'Always returns 200, whether or not the token existed — callers can't probe token
validity through revocation. token_type_hint (access_token | refresh_token)
only optimizes lookup order; both stores are tried.
Using the session token
A tvsess_ works anywhere a tvagent_ key works on the MCP surface:
curl -X POST https://api.tokenvault.uk/api/agents/mcp \
-H 'Authorization: Bearer tvsess_…' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The agent's grants and ABAC policies apply identically. In webhook mode,
get_credential returns a signed URL and the client fetches the credential
directly from your webhook — Token Vault never touches it.
Error reference
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | Missing/malformed params, non-S256 PKCE |
| 400 | invalid_grant | Bad/expired/reused code, PKCE failure, unknown refresh token, binding mismatch |
| 400 | invalid_client | Unknown client_id |
| 400 | invalid_redirect_uri | Unregistered or disallowed redirect URI |
| 400 | unsupported_grant_type | Anything but authorization_code / refresh_token |
| 401 | invalid_token | Bad Firebase session on the consent page |
| 403 | access_denied / invalid_grant | Agent suspended, deleted, or MCP-disabled |
| 429 | rate_limited | Slow down; honor Retry-After |
Ready to try it?
Sign up free with Google — your credentials stay on your own webhook, and the quickstart gets an agent fetching its first credential in about ten minutes.