---
title: "Customer agent tokens"
description: "From a signed-in account to an MCP client in one project with a scoped, expiring token: scopes, limits, revocation and every refusal."
slug: "agents"
locale: "en"
group: "agents"
order: 2
last_reviewed: 2026-10-26
---

# 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](/docs/agent-quickstart/).
Each step below carries its status; status words are defined on the
[facts page](/docs/facts/#status-words). The operator turns the token routes on with
`enroll-public-agents`; until then they answer `503`. What protects pilot data today:
[security and status](/docs/security-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_key` returns 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

```json
{"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:

```sh
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):

```json
{"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](/docs/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](/docs/hosting-sites/)).

## 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.
