Skip to main content
Lucent’s MCP endpoint is fronted by an OAuth 2.1 authorization server that conforms to the MCP specification. For the user-facing setup walkthrough, see the quickstart. This page documents the protocol surface for client implementers.

Discovery

Both metadata documents are publicly readable and CORS-open.

Protected resource metadata

GET https://app.lucenthq.com/.well-known/oauth-protected-resource Returns the resource server’s identity and which authorization servers issue tokens for it.
{
  "resource": "https://app.lucenthq.com/api/mcp",
  "authorization_servers": ["https://app.lucenthq.com"],
  "scopes_supported": ["read:lucent"]
}

Authorization server metadata

GET https://app.lucenthq.com/.well-known/oauth-authorization-server Returns the endpoints, supported parameters, and capabilities.
{
  "issuer": "https://app.lucenthq.com",
  "authorization_endpoint": "https://app.lucenthq.com/oauth/authorize",
  "token_endpoint": "https://app.lucenthq.com/api/oauth/token",
  "registration_endpoint": "https://app.lucenthq.com/api/oauth/register",
  "revocation_endpoint": "https://app.lucenthq.com/api/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_post"],
  "revocation_endpoint_auth_methods_supported": ["none", "client_secret_post"],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["read:lucent"]
}
The only supported grant flow is authorization code with PKCE (S256). Implicit, password, and client_credentials are not supported. plain PKCE is rejected.

Dynamic Client Registration

POST https://app.lucenthq.com/api/oauth/register Implements RFC 7591. Rate-limited per IP — busy clients should cache the resulting client_id rather than re-register. Request
{
  "client_name": "Acme Connector",
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "read:lucent"
}
FieldRequiredNotes
redirect_urisyesAt least one. Each must be in the allowlist. HTTPS or loopback only.
client_nameno≤ 64 chars; letters, numbers, spaces, -_.(). Defaults to Unnamed Client.
token_endpoint_auth_methodnonone (public client + PKCE, default) or client_secret_post.
grant_typesnoSubset of ["authorization_code", "refresh_token"]. Defaults to both.
response_typesnoMust include code. Defaults to ["code"].
scopenoDefaults to read:lucent.
Response (201 Created)
{
  "client_id": "luc_client_…",
  "client_secret": "…",
  "client_name": "Acme Connector",
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "read:lucent"
}
client_secret is only present when token_endpoint_auth_method is client_secret_post. Public clients (the default for native MCP clients) get client_id only and authenticate with PKCE.

Allowed redirect URIs

The redirect allowlist defends against open-redirect and auth-code injection. Registration rejects URIs outside this list with an InvalidClientMetadataError and an opaque message.
TypeAllowed values
Hosted (HTTPS)https://claude.ai, https://claude.com
Loopback (HTTP)http://127.0.0.1, http://[::1], http://localhost — any port and path
URIs with embedded credentials (https://user:pass@…) or fragments (#…) are rejected per RFC 6749 §3.1.2. To add another hosted origin, email [email protected].

Authorization request

GET https://app.lucenthq.com/oauth/authorize?… Standard OAuth 2.1 authorization-code request with PKCE:
ParamRequiredNotes
response_typeyesMust be code.
client_idyesFrom registration.
redirect_uriyesMust exactly match one of the registered redirect_uris.
code_challengeyes43–128 chars, base64url-encoded SHA-256 of the verifier.
code_challenge_methodyesMust be S256.
staterec.Opaque value the client uses for CSRF protection.
scopenoDefaults to read:lucent.
If the user isn’t signed in to Lucent, they’re routed through /login and returned to the consent screen after authentication. If a consent record already exists for this (client_id, scope) and hasn’t been revoked, the consent screen is bypassed and the browser is redirected back to redirect_uri with ?code=…&state=… immediately. If anything is wrong (unknown client, revoked client, redirect_uri mismatch, malformed PKCE), the user sees a single opaque error page — no detail is leaked that would let an unauthenticated probe enumerate client state.

Token endpoint

POST https://app.lucenthq.com/api/oauth/token Content-Type: application/x-www-form-urlencoded. Returns Cache-Control: no-store.

grant_type=authorization_code

POST /api/oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=luc_oac_…
&redirect_uri=https://claude.ai/api/mcp/auth_callback
&client_id=luc_client_…
&code_verifier=
&client_secret=…   (only if token_endpoint_auth_method=client_secret_post)
The redirect_uri must match the value sent at authorize time (RFC 6749 §4.1.3 / OAuth 2.1 §4.1.3). Codes are single-use and expire after 60 seconds. Reuse of an already-redeemed code revokes any tokens previously issued from it.

grant_type=refresh_token

grant_type=refresh_token
&refresh_token=luc_ort_…
&client_id=luc_client_…
&scope=read:lucent          (optional, must be ⊆ originally granted)
&client_secret=…            (only if confidential client)
Refresh tokens rotate on every exchange — the old refresh token is marked rotated and a new pair is returned. If a rotated refresh token is presented again, the entire token chain (access + refresh) for that session is revoked as a reuse-detection cascade.

Response

{
  "access_token": "luc_oat_…",
  "refresh_token": "luc_ort_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:lucent"
}
TTLDefaultEnv var
Access token1 hourOAUTH_ACCESS_TOKEN_TTL_SECONDS
Refresh token30 daysOAUTH_REFRESH_TOKEN_TTL_DAYS
Authorization code60 secondsOAUTH_AUTHORIZATION_CODE_TTL_SECONDS

Revocation endpoint

POST https://app.lucenthq.com/api/oauth/revoke Implements RFC 7009 and is idempotent — always returns 200 even for unknown tokens.
POST /api/oauth/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded

token=luc_oat_… or luc_ort_…
&token_type_hint=access_token | refresh_token   (optional)
&client_id=luc_client_…
&client_secret=…   (only if confidential client)
Use this when a user signs out of your client. Lucent also exposes a Connected apps section in the dashboard so end users can unilaterally revoke any registered client.

Tools and scopes

Every token currently carries the single read:lucent scope. The four read-only tools (list_signals, list_issues, get_issue, list_insights) are gated behind that scope at the MCP handler level via withMcpAuth({ requiredScopes: ["read:lucent"] }). See the tool reference for arguments and return shapes.

Errors

OAuth-protocol errors follow the RFC 6749 §5.2 JSON shape:
{ "error": "invalid_grant", "error_description": "PKCE verification failed" }
User-visible authorize-page failures collapse to a single opaque message. Token-endpoint client-auth failures are also opaque (no distinction between “unknown client_id” and “wrong secret”) — both defenses are deliberate and prevent enumeration of registered clients.