Skip to main content

Authentication (OAuth 2.1 + DCR)

Shuuka MCP uses standard OAuth 2.1 with Dynamic Client Registration (RFC 7591). There is no manual platform whitelisting — any client that can complete DCR + PKCE can authenticate.

Discovery

All endpoints are advertised at:

GET https://mcp.shuuka.com/.well-known/oauth-authorization-server
GET https://mcp.shuuka.com/.well-known/oauth-protected-resource
GET https://mcp.shuuka.com/.well-known/mcp

You should fetch the OAuth metadata document at startup and treat the URLs in it as the source of truth.

Dynamic Client Registration

Register your client once. Re-register if your metadata changes.

POST /oauth/register
Content-Type: application/json

{
"client_name": "My MCP Client",
"redirect_uris": ["https://my-app.example/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"scope": "identity:read links:read links:write analytics:read"
}

Response (excerpted):

{
"client_id": "shk_client_...",
"client_id_issued_at": 1746230400,
"redirect_uris": ["https://my-app.example/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"]
}

Public clients (CLI, desktop, mobile) use token_endpoint_auth_method: "none" and must use PKCE. Confidential clients (server-side web apps) receive a client_secret.

Verification status

Newly registered clients are unverified by default. Unverified clients:

  • Can request read scopes.
  • Cannot request scopes flagged unverified_ok: false.
  • Are subject to the lower per-client rate limits.

To upgrade your client to verified, contact [email protected] with your client_id, your domain, and a brief description of the use case. Verification unlocks write scopes and higher rate limits.

Authorization code flow with PKCE

Required for all clients (public and confidential).

1. Build the authorize URL

https://mcp.shuuka.com/oauth/authorize
?response_type=code
&client_id={client_id}
&redirect_uri={url-encoded redirect}
&scope=identity%3Aread+links%3Aread+links%3Awrite
&state={random-csrf-token}
&code_challenge={base64url(sha256(code_verifier))}
&code_challenge_method=S256

The user lands on a Shuuka-branded consent screen showing exactly which scopes you requested and what your client name + URL are.

2. Receive the code

After approval, Shuuka redirects to your redirect_uri:

https://my-app.example/callback?code=...&state=...

Verify state. Any mismatch is a CSRF attempt — abort.

3. Exchange code for tokens

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

grant_type=authorization_code
&code={code}
&redirect_uri={redirect}
&client_id={client_id}
&code_verifier={original PKCE verifier}

Response:

{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "...",
"refresh_token": "...",
"scope": "identity:read links:read links:write"
}

4. Refresh

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

grant_type=refresh_token
&refresh_token={refresh_token}
&client_id={client_id}

5. Revoke

POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded

token={access_token_or_refresh_token}
&client_id={client_id}

Users can also revoke any client at any time from Settings → Connected agents in their Shuuka account.

Scopes

Scopes are per-tool-domain and follow the convention <domain>:<verb> (e.g. links:write, analytics:read).

DomainRead scopeWrite scopeMin plan for write
Identityidentity:read
Linkslinks:readlinks:writePro
Smart routessmart_routes:readsmart_routes:writePro
Collab linkscollab_links:readcollab_links:writePro
Analyticsanalytics:readanalytics:exportPro
Appsapps:readapps:writePro
Themetheme:readtheme:writePro
SEOseo:readseo:writePro
Verificationverification:readverification:requestFree
Trusttrust:readtrust:report:write, trust:reports:manageFree
Webhookswebhooks:readwebhooks:writePro
Goalsgoals:readgoals:writePro
Planplan:readFree
Vault exportvault:exportPro
Workspace (T3)workspace:read, workspace:analytics:read, workspace:trust:readworkspace:switchEnterprise

Request only the scopes you need. Users are more likely to approve a small, well-scoped client.

Step-up authentication

A handful of high-impact tools require fresh authentication within a 5-minute window. If the user's last successful auth is older than that, the tool returns:

{
"error": {
"code": "step_up_required",
"data": {
"auth_url": "https://mcp.shuuka.com/oauth/step-up?...",
"expires_at": "..."
}
}
}

Send the user to auth_url. After they re-authenticate (password / passkey), retry the tool call within the window.

Tools that require step-up:

  • request_identity_export
  • request_verification
  • submit_fake_report
  • revert_theme

Token usage

Send the access token on every JSON-RPC request:

POST / HTTP/1.1
Host: mcp.shuuka.com
Authorization: Bearer {access_token}
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/list"}

If the token is expired or revoked, you receive invalid_token. Refresh and retry.

Idempotency for writes

Every write tool must include an Idempotency-Key header (a UUID v4 you mint client-side). Server caches the result for 24 hours; replays return the original response.

Idempotency-Key: 7f3c1e2a-...

Omitting it on a write call returns validation_failed.

Next