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).
| Domain | Read scope | Write scope | Min plan for write |
|---|---|---|---|
| Identity | identity:read | — | — |
| Links | links:read | links:write | Pro |
| Smart routes | smart_routes:read | smart_routes:write | Pro |
| Collab links | collab_links:read | collab_links:write | Pro |
| Analytics | analytics:read | analytics:export | Pro |
| Apps | apps:read | apps:write | Pro |
| Theme | theme:read | theme:write | Pro |
| SEO | seo:read | seo:write | Pro |
| Verification | verification:read | verification:request | Free |
| Trust | trust:read | trust:report:write, trust:reports:manage | Free |
| Webhooks | webhooks:read | webhooks:write | Pro |
| Goals | goals:read | goals:write | Pro |
| Plan | plan:read | — | Free |
| Vault export | — | vault:export | Pro |
| Workspace (T3) | workspace:read, workspace:analytics:read, workspace:trust:read | workspace:switch | Enterprise |
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_exportrequest_verificationsubmit_fake_reportrevert_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.