Customer agent tokens
From a new account to an AI agent (Claude Code, Cursor, any MCP client) that inspects
your schema, applies migrations and deploys Edge Functions in one project with a
scoped, expiring token instead of the project's service-role key. A token with
sql.execute or migration runs SQL as the database owner and can read whatever that
database holds, including the project's own secrets: give it only to an agent you would
trust with that key. Every route below is in the OpenAPI 3.1 document the identity
service serves at https://<host>/platform/identity/openapi.yaml.
These tokens are created by the customer on the signed-in page. The tokens the private
console issues today are described in the agent quickstart.
Each step below carries its status; status words are defined on the
facts page. The operator turns the token routes on with
enroll-public-agents; until then they answer 503. What protects pilot data today:
security and status.
1. Create your account
Status: Available in pilot (access.signup)
Open https://<host>/public-identity/ and choose Create account: email, the
6-digit code we send (valid 15 minutes), a password of 12 to 72 characters, an
authenticator app, then save the recovery codes. The signed-in page shows your
organization, its projects and, below them, your agent tokens.
2. Create a project
Status: Available in pilot (access.self-service-project)
On the signed-in page, name the project and choose Create project. The row shows
each step until the project is ready. The pilot plan allows one project per
organization. Scripted, with the browser session:
POST /platform/api/dashboard/platform/organizations/{organization}/projects with
{"name": "...", "idempotency_key": "<uuid>"}, then poll the operation it returns.
3. Create an agent token
Status: In development (agents.tokens)
On the signed-in page: New token, name it after the agent, select the projects, the access and the expiry, then Create token. Copy the token or the ready MCP configuration and choose I stored this token. The token is never shown again; the server keeps only its SHA-256.
| Scope | Tools | Roles that can grant it |
|---|---|---|
read (always included) | get_project, get_project_url, get_publishable_keys, get_agent_operation, list_migrations, list_edge_functions, get_edge_function | every project role |
database.read | list_tables, get_policies | every project role |
migration | apply_migration | owner, admin |
sql.execute | execute_sql | owner, admin |
deploy | deploy_edge_function, invoke_edge_function | owner, admin |
migration and sql.execute both run SQL as the database owner and can bypass RLS;
migration adds the name and checksum ledger. Grant them only to agents you would
let administer the database.
Rules the server enforces:
- A token belongs to you, one organization and the projects you selected.
- Expiry is required: 1 to 30 days by default; the operator sets the maximum, from 1 to 90 days. The page offers 1, 7, 30 and 90 days up to that maximum, and the maximum.
- At most 10 active tokens and 10 creations per hour per account.
- What a token can do is its scopes intersected with your role at each call: if your role drops to developer, its SQL and deploy tools disappear, and they return if your role does.
- If you leave the organization or lose access to one of the token's projects, the token is revoked at that moment and stays revoked if you are invited back or granted the project again: create a new one.
- Repeating a creation with the same
idempotency_keyreturns the same token with"secret": null; revoke that one and create another.
Scripted, with the browser session (Origin, X-Plurabase-Request: 1 and the session
CSRF token): POST /platform/api/dashboard/platform/agents/tokens with
{"organization": "<uuid>", "name": "Claude Code", "projects": ["<project ref>"],
"scopes": ["read", "database.read", "migration"], "expires_in_days": 7,
"idempotency_key": "<uuid>"}
The 201 answer carries secret once; GET .../agents/tokens lists tokens without it.
4. Connect your MCP client
Status: In development (agents.tokens)
Endpoint, one per project:
https://<host>/platform/api/dashboard/platform/agents/projects/<project ref>/mcp,
with the header Authorization: Bearer pbt_.... Keep the token in your client's
secret store or an environment variable, never in a repository.
Claude Code:
claude mcp add --transport http plurabase-<project ref> \
https://<host>/platform/api/dashboard/platform/agents/projects/<project ref>/mcp \
--header "Authorization: Bearer $PLURABASE_MCP_TOKEN"
Clients configured with JSON (the page copies this with your token filled in):
{"mcpServers": {"plurabase-<project ref>": {"type": "http",
"url": "https://<host>/platform/api/dashboard/platform/agents/projects/<project ref>/mcp",
"headers": {"Authorization": "Bearer <token>"}}}}
Clients that only start local stdio servers: run the connector in
agents/simplybase-mcp (npm ci --ignore-scripts once) as
node connector.mjs, with PLURABASE_MCP_URL, PLURABASE_MCP_TOKEN and
PLURABASE_MCP_HOSTS (your <host>, comma-separated if several) in its environment.
It sends the token only over HTTPS, to the MCP path of a project, on a host that
PLURABASE_MCP_HOSTS lists. Many clients start stdio servers with little more than
the server's env block, so set all three there, in your user-level MCP
configuration, never in a file inside a repository.
The host list catches a mistyped URL or a look-alike host. It does not protect your
token from an MCP configuration you did not write. A repository's MCP file can set
PLURABASE_MCP_URL and PLURABASE_MCP_HOSTS to its own host, and its env block takes
precedence over the environment your client passes on, so a token in your shell
environment goes there. Any command it starts can also read your files and environment,
wherever you keep the token. Approve project MCP servers only in repositories you trust.
Start every session with tools/list and get_project: the tool list is exactly
what this token may do now. Limits: 60 calls per minute per token, 600 per
10 minutes per network, 2 calls in flight per server; a 429 carries
Retry-After. The endpoint answers JSON only (no SSE) and refuses browser
requests from other origins.
5. Migrate your schema
Status: In development (agents.tokens)
Ask the agent to apply each change with apply_migration (name, query,
operation_id). It runs in one transaction and records the name and checksum:
the same name and content again is a safe replay, changed content under the same
name is refused. list_migrations shows the latest 200 migrations you applied
through agents on this project, oldest first.
Every mutating call needs a fresh operation_id UUID. After a timeout, call
get_agent_operation with the same id; never resend the mutation with a new id. An
unknown outcome blocks further mutations on that project until the operator
reconciles it.
Status: In development (ops.migration)
Moving rows, Auth users and Storage objects from an existing Supabase project is a separate migration job, not an agent tool; migration describes it and how a project moves today.
6. Deploy
Status: In development (agents.tokens)
Edge Functions: deploy_edge_function with the source files, entrypoint, verify_jwt
and expected_revision (absent for a new function, else the revision from
list_edge_functions), then invoke_edge_function with the public key or no key.
Not yet verified against the pilot's project runtime.
Status: In development (sites.git)
Frontends are not deployed with tokens: they publish from Git to Sites (frontend hosting).
7. Revoke
On the signed-in page, Revoke then Revoke token, or
POST /platform/api/dashboard/platform/agents/tokens/{token_id}/revoke with {}.
The token's next request is refused; a call already running completes. Signing out
or changing the password does not revoke tokens: after recovering your account,
review the list.
Refusals
Answers are {"error": "PUBLIC_AGENT_REJECTED", "reason": "<reason>"}.
| Status | Reason | What to do |
|---|---|---|
| 400 | rejected | Fix the request (fields, expiry over the maximum, MCP-Protocol-Version) |
| 401 | unauthorized | Unknown, expired or revoked token, or signed out: create a token or sign in |
| 403 | forbidden | Your role does not allow that scope on a selected project, or a header check failed |
| 403 | step_up_required | Sign in again with your authenticator |
| 404 | not_found | Not your token, or the project is not bound to this token or no longer yours |
| 409 | token_limit | Revoke a token first |
| 409 | conflict | Same idempotency_key with a different body |
| 409 | unsupported | This project's runtime does not offer agent tools |
| 413 | too_large | MCP body over 512 KiB |
| 429 | try_later | Wait Retry-After seconds |
| 503 | unavailable | The agent service is not running here yet, or did not answer |
Tool refusals inside MCP (for example AGENT_SCOPE_DENIED or PROJECT_NOT_READY)
come back as a tool result with isError: true.
OPERATION_HISTORY_LIMIT means this project's operation journal is full: 10,000
operations, applied migrations not counted. MIGRATION_HISTORY_LIMIT means it holds
1,000 applied migrations; only apply_migration stops then. Finished entries older than
90 days make room by themselves; before that, ask the operator to clear the project's
journal. Other projects are not affected.