For any automated agent or script using pscale. Always pass --format json. Substitute placeholders from the user's request or from prior command output (org list, database list, branch list).
If you only have the installed pscale binary, start here:
pscale agent-guide --format json
pscale auth check --format jsonUse direct CLI automation for shell commands and scripts. Use the hosted PlanetScale MCP server for MCP clients.
This file documents how to invoke pscale. For database assessment, safety review, and operational workflows, install the PlanetScale skills pack (14-pscale-cli-automation covers CLI automation; 00-safe-orchestrator runs the full review). In application repositories, add a separate project AGENTS.md with org, database, branch, and approval rules (see skill 09-mcp-agent-operating-model in that repo).
| Placeholder | Meaning |
|---|---|
<org> |
Organization name |
<database> |
Database name |
<branch> |
Branch name (pick one with "ready": true from branch list) |
--orgis a flag on resource subcommands (database,branch,sql,api, …). It is not on rootpscale—pscale --org …fails.--format jsonis a global flag. It can go onpscaleor on the subcommand.- Commands with positional args (
sql,branch list, …): put positionals first, then flags.
# Correct
pscale auth check --format json
pscale org list --format json
pscale database list --org <org> --format json
pscale branch list <database> --org <org> --format json
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"
# Also valid — global --format
pscale --format json database list --org <org>
# Wrong — unknown flag: --org
pscale --org <org> database list --format json-
Guide — discover machine-readable conventions when you do not have this file:
pscale agent-guide --format json
-
Auth — check before anything else:
pscale auth check --format json
"status": "ok"and"authenticated": truewith no blockingissuesmeans proceed."status": "action_required"exits non-zero — log in, pick an org, or fix credentials (seeissuesandnext_steps). -
Login (when not authenticated):
pscale auth login --format json
Pending JSON is written to stderr while waiting; stdout has a single final JSON object when login completes (
status: okoraction_requiredif org setup fails after credentials are saved). Fields includeverification_url,user_code, andbrowser_opened. Openverification_urlmanually if the browser does not open. Do not retry login in a loop without browser access. -
Organization — use
"organization"fromauth check, ask the user, or list orgs:pscale org list --format json
Pass
--org <org>on resource commands (database,branch,sql,api, …). Not onorg list. -
Discover resources before SQL:
pscale database list --org <org> --format json pscale branch list <database> --org <org> --format json
-
Query (read-only default):
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"
| Flag | Purpose |
|---|---|
--format json |
JSON on stdout |
--org <org> |
Organization (on resource subcommands only) |
--api-url |
Non-production API base URL — pass on every command when not using production |
With --format json, any command that fails prints exactly one JSON envelope on stdout:
{
"status": "error",
"error": "human-readable message",
"issues": [{ "code": "NOT_FOUND", "message": "human-readable message" }],
"next_steps": ["pscale org list --format json", "pscale database list --org <org> --format json"]
}statusis"error"or"action_required".action_requiredmeans an agent can recover by followingnext_steps(log in, ask the user for approval, fix the invocation). Exit code is1foraction_requiredand2forerror.issues[].codeis stable and machine-readable; branch on it, not on message text.next_stepsare concrete commands or instructions, ordered by likelihood.
Some commands add fields to this envelope (for example query_kind on destructive SQL or migration_id on imports) but status, issues, and next_steps are always present on failure.
| Code | Meaning |
|---|---|
NO_AUTH |
Not authenticated or token expired; run pscale auth login --format json |
AUTH_INVALID |
Stored credentials rejected by the API; log in again |
SERVICE_TOKEN_INVALID |
Service token id/secret rejected; verify the values |
NO_ORG |
Authenticated but no organization configured |
INVALID_FLAG_PLACEMENT |
--org was passed on pscale root; move it to the subcommand |
INVALID_USAGE |
Missing arguments or required flags; the message names them |
UNKNOWN_COMMAND |
Command does not exist; check pscale --help |
UNKNOWN_FLAG |
Flag does not exist on this command; check --help |
TTY_REQUIRED |
Command needs an interactive terminal; use the JSON alternative in next_steps |
CONFIRMATION_REQUIRED |
Destructive or gated action; ask the user, then re-run with --force |
DESTRUCTIVE_SQL |
Query would delete data or schema; ask the user, then re-run with --force |
NOT_FOUND |
Org, database, branch, or resource does not exist; run the discovery commands |
NETWORK_ERROR |
Transport-level failure; check connectivity and --api-url, then retry |
COMMAND_FAILED |
Unclassified failure; read error and rule out auth first |
pscale auth login stores credentials in the OS keychain; agents on the same machine reuse them.
Headless / CI: pass --service-token-id and --service-token on the subcommand that needs auth.
Non-interactive queries. Default --role is reader (unlike pscale shell, which defaults to admin). Use pscale shell for interactive sessions.
# Read (default)
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"
# Read from replica
pscale sql <database> <branch> --org <org> --format json --replica --query "SELECT 1"
# PostgreSQL — optional --dbname (default postgres)
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"
# MySQL multi-keyspace — optional --keyspace (default @primary)
pscale sql <database> <branch> --org <org> --format json --keyspace <keyspace> --query "SELECT 1"| Flag | Purpose |
|---|---|
--role |
reader (default), writer, readwriter, admin — same names as pscale shell |
--replica |
Route reads to replicas |
--dbname |
PostgreSQL database name (default postgres) |
--keyspace |
MySQL keyspace (default @primary) |
--force |
Allow destructive SQL after explicit user approval |
--role by engine (same as pscale shell):
--role |
MySQL (Vitess) | PostgreSQL |
|---|---|---|
reader |
Branch password, reader role | Ephemeral role inheriting pg_read_all_data |
writer |
Branch password, writer role | Role inheriting pg_write_all_data |
readwriter |
Branch password, readwriter role | Role inheriting read + write |
admin |
Branch password, admin role | Role inheriting postgres |
DELETE, DROP, and TRUNCATE anywhere in a query are blocked by default (word match, not substring — deleted_at is fine). Returns "status": "action_required" with "query_kind": "destructive".
- Ask the user to approve the query.
- Re-run with
--forceonly after they approve:
pscale sql <database> <branch> --org <org> --format json --force --query "DELETE FROM ..."Never use --force without explicit user approval.
Success: status, database, branch, kind (mysql or postgresql), role, row_count, columns, rows; replica when --replica was used.
MySQL may return synthetic column names (e.g. :vtg1 /* INT64 */). PostgreSQL may use names like ?column?.
Error: one JSON object on stdout with status: "error", error, issues, and next_steps (see JSON errors above).
Destructive SQL without --force: status: "action_required", query_kind: "destructive", issues, and next_steps (includes --force retry command).
pscale import d1 migrates a Cloudflare D1 (SQLite) export into a PlanetScale Postgres branch. Every subcommand supports --format json and returns status, issues, and next_steps; stateful steps return a migration_id — pass it back with --migration-id to resume.
pscale import d1 doctor --format json # check prerequisites (pgloader, psql)
pscale import d1 lint --input <file> --format json # pre-import checks; errors block import
pscale import d1 start <database> --org <org> --input <file> --dry-run --format json # plan + migration ID, no writes
pscale import d1 start <database> --org <org> --input <file> --format json # run the import
pscale import d1 verify <database> --org <org> --migration-id <id> --sqlite <file> --format json
pscale import d1 complete <database> --org <org> --migration-id <id> --format jsonBranch is an optional second positional (defaults to the default branch). status --migration-id <id> shows saved migration state; convert-schema --input <file> converts schema only.
start in JSON mode does not prompt. The confirmation prompt is human-format only; with --format json, start loads data immediately. Run start --dry-run first, show the user the plan, and only run the real start after they approve.
pscale api --org <org> organizations/<org>/databasesFor MCP clients, use the hosted PlanetScale MCP server:
https://mcp.pscale.dev/mcp/planetscale
See the current MCP docs: https://planetscale.com/docs/connect/mcp
Do not use the deprecated local pscale mcp server path unless you explicitly need backward compatibility with an old setup.
Operational workflows (inventory, safety review, Insights, schema recommendations, Traffic Control) live in the public skills repo — not in this file.
git clone https://github.com/planetscale/skills.git && cd skills && script/setup
# or: npx skills add planetscale/skills -g -yAfter installing skills, load 14-pscale-cli-automation for CLI conventions (or re-run pscale agent-guide --format json from any pscale binary that includes agent onboarding). Use 00-safe-orchestrator when the user asks for a full PlanetScale assessment.