MCP server
Authentication
Three methods, one resulting identity.
OAuth 2.1 (interactive clients). Your MCP host discovers the
authorization server from /.well-known/oauth-protected-resource and
completes an Authorization Code + PKCE flow with WorkOS. Tokens carry your
role, scopes and workspace allowlist.
Personal access token (people connecting an assistant). Created in the
studio at /o/<org>/connect — pick the assistant, tick up to five client
workspaces, and copy the one-time apt_… secret into your assistant's
config. Sent as Authorization: Bearer apt_…. See
connect-your-assistant.md for the walkthrough. Two differences from an
API key:
-
Scopes come from your role, never from a picker. Every connection — personal access token or OAuth alike — carries the read set (
org:read,workspace:read,brand:read,prompt:read,answer:read,usage:read,run:read) plus whatever your current membership role adds, re-derived from the live membership on every call (never from the token itself):Role Grant organization_owner,organization_adminreads + content writes ( brand:write,prompt:write,report:write,workspace:write) + run (run:execute,run:cancel)agency_analystreads + content writes — no run scopes client_admin,client_viewerreads only The write and run rows are switched on per deployment. They apply only where the operator has set
MCP_CONNECTION_WRITES_ENABLED=trueon the MCP service. Where it is off (the default), every connection carries the read set only, whatever the role, and write and run tools are not listed. The setting is re-read on every call, so turning it on or off reaches existing connections without reconnecting.get_account_contextandtools/listalways show what your connection can do right now.Never granted over MCP, to any role:
member:manage,billing:manage,org:manage,webhook:manage,workspace:archive,report:approve,report:send. Those stay console-only.Spending the organization's allowance is owner/admin-only regardless of scope.
run_question_now,retry_failed_executions,onboard_brand,generate_buyer_questions,discover_keywords,get_keyword_volumesandexpand_topicsrefuse any connection whose role is notorganization_owner/organization_admin, even one that otherwise holdsrun:executeorprompt:write— an API key's own stored scopes are unaffected by this rule.A destructive write still requires
request_approval's two-call preview/confirm exchange regardless of role (seewrites.md); the role table above governs whether a tool is reachable at all, not whether an approval is still required once it is. -
The workspace allowlist is chosen at creation, capped at 5, and always narrower than or equal to your own. The connect page offers every workspace of your organization that your own membership lets you see; picking one you cannot see yourself (outside your own
memberships.workspace_allowlist) is refused, never silently widened. Effective visibility is the intersection of what you picked and what your own membership already allows — the same "never wider than the creator" rule an API key's own workspace scoping follows.
Like an API key, the plaintext secret (apt_<32 hex>) is shown exactly
once, at creation, and stored only as a SHA-256 hash thereafter — losing it
means creating a new token and disconnecting the old one, not recovering it.
API key (agents, CI, servers). Create a key in Settings → API keys and
send it as Authorization: Bearer awk_…. Keys are shown once and stored
only as a hash. A key has an explicit scope list; a key with no scopes can
see the documentation tools and nothing else.
Scopes are granular: answer:read, prompt:write, run:execute,
billing:manage and so on. Authorization is the intersection of your plan's
entitlements, your role, your token's scopes and its workspace allowlist —
a scope never widens a role.