Skip to content

Tokens & scoping

The MCP endpoint accepts two auth styles. Both end up at the same scope check on the server — the difference is purely in how Claude obtains the bearer.

Issued by the OAuth Custom Connector flow. Stored only in Claude Desktop; the worker keeps a hash plus rotation metadata. Format: oat_<random>.

AttributeValue
LifetimeAccess token 1 h, refresh token 30 d, rotated on every refresh
Granted scopeAlways mcp:full — full surface visible to the user
Per-site scopeBound to the user’s user_sites whitelist (DB-side, not in the token)
User bindingLinked to a real user account (oauth_access_tokens.user_id)
Refresh strategyAutomatic — Claude refreshes silently
RevokeClick Remove in Claude → calls /oauth/revoke. Or master-admin can wipe rows in oauth_access_tokens table
Audit trailEvery tools/call records actor_user_id from the access token

Get one: Add SEO Navigator as a Custom Connector in Claude Desktop — see Quick start.

Plaintext static strings minted in the admin dashboard. Format: sn_prod_<32 hex>. Used by the mcp-remote bridge when OAuth isn’t an option, or by CLI/CI workflows.

AttributeValue
LifetimeNone (live until revoked)
Granted scopeExplicit per-token allowed_sites + allowed_tools arrays
Per-site scopeWhatever the creator ticked when minting
User bindingtokens.owner_user_id — NULL means super-admin-issued
Refresh strategyNone — recreate manually
Revoke/admin/tokens → red Revoke button
Audit trailactor_user_id set from tokens.owner_user_id
  1. Open /admin/tokens and click + Create token.
  2. Name: descriptive (eg “CI runner”, “blog writer staging”).
  3. Allowed sites:
    • Super-admin: pick specific sites or * for all.
    • Regular user: only your own sites are listed. Pick * to expand to everything you own at this moment (locks at create time — later grants don’t widen the token).
  4. Allowed tools:
    • The built-in list is fetched from the live registry (GET /admin/api/tools), so newly shipped tools always appear; read-only (T0) tools carry a green read badge.
    • Quick-pick Select all ticks every safe built-in + proxy tool.
    • Quick-pick Read-only ticks only risk-tier-0 built-ins plus proxy tools whose name is a read verb (get/list/search/fetch/check) — the fastest way to mint a reporting-only token.
    • Quick-pick All except destructive filters out proxy delete-*, publish-* tools.
    • Quick-pick Built-in only skips the wp-mcp-adapter proxy surface.
    • Super-admin-only tools (eg delete_astro_route) are hidden from regular users entirely.
  5. Max risk (super-admin): cap the token to a risk tier — eg read-only (T0) for an analytics dashboard, or drafts-only (T1) for a content bot. Leave blank for no cap.
  6. Skip write-confirm (super-admin): tick only for trusted automation tokens that should bypass the write-confirm gate. Off by default; regular-user tokens can never set it.
  7. Create. Copy the plaintext — it’s shown once.

If you lose the plaintext, revoke and create a new one. The worker only stores SHA-256 of the bytes.

You can change a token’s allowed_sites, allowed_tools, max_risk, and (super-admin) skip_write_confirm without re-issuing the secret — click Edit on a row in /admin/tokens. The plaintext stays valid; only the scope changes. Regular users may only edit their own tokens, can only grant sites they own, and cannot add super-admin-only tools or flip skip_write_confirm.

This is the safe way to widen or tighten a long-lived CI/automation token: no rotation, no redeploying the secret to your runner.

Independently of which tools a token lists, every tool also carries a risk tier that two account-safety controls read:

TierMeaning
T0read-only — no state change
T1reversible content / draft write (never publishes or deletes)
T2external write (CRM / Drive) — also super-admin flag-gated
T3production publish / push — also flag-gated
T4destructive / admin — super-admin only

Write-confirm gate. Any tool at tier ≥ T1 must be called with confirm: true. Without it the tool returns a needs_confirmation preview and changes nothing — a guard against a write being triggered by mistake. A token with skip_write_confirm bypasses the gate (for trusted automation only).

Per-account risk cap. A max_risk ceiling on a token (or on the owning user) refuses any call above that tier before it runs — the refusal is audited as risk_capped. If a token leaks, a low cap contains the blast radius: a max_risk = 0 token can only ever read, a max_risk = 1 token can only ever touch drafts.

Dynamic proxy tools (GoHighLevel, wp-mcp-adapter) are tiered by action verb — a write counts as T2, a read as T0. The full per-tool tier table is in the tool reference.

Use casePick
Claude Desktop on a personal laptopOAuth
Claude Desktop on a teammate’s machine — handover-friendlyOAuth (per-user token, not shared)
Old Claude Desktop without Custom Connector UIBearer + mcp-remote
CI runner posting to the MCP from CLIBearer (long-lived, no browser)
Test fixtures or curl explorationBearer
Multi-tenant ops where each team has its own userOAuth (cleaner audit)

You can have BOTH on the same worker for the same user — the OAuth token operates on the user’s user_sites, while a bearer token mints whatever scope the user explicitly picked. Each leaves a separate audit trail.

Sites are NOT pinned to bearer tokens by ID alone — they’re filtered against the calling user’s user_sites whitelist on every request:

  • OAuth: token has user_id. Tool calls intersect that user’s sites with the requested site_id. Out-of-scope → 403 E_SCOPE_DENIED.
  • Bearer: token has explicit allowed_sites. If owner is a regular user, the worker additionally intersects with their current user_sites. So removing a site from a user’s grant also locks down their bearer tokens.

This means a super-admin can revoke a user’s access by removing site grants in /admin/users — no need to revoke individual tokens.

No token, of either type, can publish-now or delete a WordPress / Duda post. The check happens at the tool registration layer — those tools simply don’t exist on the server. A token with allowed_tools: ["*"] still cannot call delete_post because there’s no delete_post to call.

The one documented exception — delete_astro_route — is super-admin-only at runtime regardless of the token’s allowed_tools value.

Every successful or failed tool call writes a row in audit_log:

ts INTEGER -- unix ms
actor_user_id TEXT -- from OAuth user_id or token owner_user_id
token_id TEXT
site_id TEXT
tool TEXT
status TEXT -- ok | denied | error
duration_ms INTEGER
args_json TEXT -- truncated to 1KB
error TEXT -- when status != ok
via TEXT -- "default" | "wp-mcp-adapter"

Browse and filter at /admin/audit. Regular users see only their own rows; super-admin sees everything.