Documentation Index
Fetch the complete documentation index at: https://docs.lucenthq.com/llms.txt
Use this file to discover all available pages before exploring further.
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.
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"]
}
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"
}
| Field | Required | Notes |
|---|
redirect_uris | yes | At least one. Each must be in the allowlist. HTTPS or loopback only. |
client_name | no | ≤ 64 chars; letters, numbers, spaces, -_.(). Defaults to Unnamed Client. |
token_endpoint_auth_method | no | none (public client + PKCE, default) or client_secret_post. |
grant_types | no | Subset of ["authorization_code", "refresh_token"]. Defaults to both. |
response_types | no | Must include code. Defaults to ["code"]. |
scope | no | Defaults 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.
| Type | Allowed 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:
| Param | Required | Notes |
|---|
response_type | yes | Must be code. |
client_id | yes | From registration. |
redirect_uri | yes | Must exactly match one of the registered redirect_uris. |
code_challenge | yes | 43–128 chars, base64url-encoded SHA-256 of the verifier. |
code_challenge_method | yes | Must be S256. |
state | rec. | Opaque value the client uses for CSRF protection. |
scope | no | Defaults 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"
}
| TTL | Default | Env var |
|---|
| Access token | 1 hour | OAUTH_ACCESS_TOKEN_TTL_SECONDS |
| Refresh token | 30 days | OAUTH_REFRESH_TOKEN_TTL_DAYS |
| Authorization code | 60 seconds | OAUTH_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.
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.