Skip to content
BataDB

DocsGuides

Agents quickstart

Drive BataDB from an agent: scoped keys, the bata CLI and the MCP server.

BataDB is built to be driven by agents. Everything the console does is reachable from two surfaces:

  • MCP server (@batadata/mcp): tools for agents that speak the Model Context Protocol, such as Claude Code, Claude Desktop and Cursor.
  • CLI (@batadata/cli, the bata command): for agents that run shell commands, and for CI.

Both call the same REST API with the same API key. A CI test keeps them in step: every API route has a CLI command and an MCP tool, or a written reason why not.

1. Mint a scoped key

Give each agent the smallest key that does its job. Scopes are enforced by the API.

ScopeAllows
readEvery read: projects, branches, schema, insights, usage
sqlread plus running SQL and reading connection strings
adminEverything, including creating and deleting projects, branches and keys
telemetry:writePosting Turbine telemetry for one project, nothing else

--project restricts a key to named projects. A restricted key cannot call team-level endpoints (teams, billing, keys, project create).

These keys expire (90 days by default). A key that goes into a deployed app, including a telemetry key, should be an app key instead: it never expires, rotates without downtime, and runs SQL as a Postgres role limited to the tables you grant. See app-keys.md.

Terminal
npm install -g @batadata/cli
bata login                                   # once, as a person: type the code into the browser page and approve

bata keys create --name "reviewer" --scope read --json
bata keys create --name "app agent" --scope sql --project <project-id> --json
bata keys create --name "ops agent" --scope admin --json

The key is printed once. Check what a key can do at any time:

Terminal
BATA_API_KEY=<your-api-key> bata whoami --json

1b. Or approve the machine once

On a machine a person also uses, there is no key to paste. A person runs bata login once and types the code it prints into the console page. After that, every agent on that machine uses bata and the MCP server without a person, until someone revokes the key on purpose.

  • The login saves a CLI key (cli-<hostname>, full access to the team the person approved) in ~/.batarc, mode 0600. It is never printed.
  • The key renews itself on use: it expires only after 90 days unused.
  • A password change keeps it. A password reset (account recovery) revokes it. So do bata logout, bata keys revoke <id>, and "Revoke all CLI keys" in Settings.
  • The MCP server (0.5.0 and later) uses the same login when BATA_API_KEY is unset, and picks up a new login without a restart.
  • If an agent's tool call times out while it waits for the approval, it runs bata login --json again. That resumes the same request: the first line says "resumed": true and shows the same code.
  • If the login is refused later, every command fails with AUTH_REQUIRED (exit 4). The envelope says why (reason, at) and names the fix in next (bata login --json), which needs a person.
  • To act as another account without touching the real login, use a named profile: BATA_PROFILE=qa bata login, or --profile qa on any command.

bata whoami --json shows which credential is in use: credential.source (profile, env or flag), credential.kind (cli, integration, or session for an old password login), its name and expires_at.

2a. Connect over MCP

Claude Code, on a machine that ran bata login:

Terminal
claude mcp add batadata -- npx -y @batadata/mcp

Or with a scoped key instead:

Terminal
claude mcp add batadata --env BATA_API_KEY=<your-api-key> -- npx -y @batadata/mcp

Claude Desktop and Cursor use the same mcpServers block. The @batadata/mcp README lists the config file locations.

JSON
{
  "mcpServers": {
    "batadata": {
      "command": "npx",
      "args": ["-y", "@batadata/mcp"],
      "env": { "BATA_API_KEY": "<your-api-key>" }
    }
  }
}

Two optional settings:

  • BATA_MCP_TOOLSETS=core,insights,index exposes only some tool groups. Use it for clients that cap the number of tools.
  • BATA_MCP_READONLY=1 hides every tool that changes anything.

2b. Or drive the CLI

Set the key in the environment. No login, no prompts:

Terminal
export BATA_API_KEY=<your-api-key>
bata status --json

Every command that calls the API takes --json, prints one JSON document on stdout and exits non-zero on failure. --help on any command prints its usage without touching the network.

3. A typical agent loop

The same journey on both surfaces.

StepMCP toolCLI
Who am I, what may I dowhoamibata whoami --json
Create a databaseprojects_createbata projects create my-app --json
Wait until it can servebranches_list (poll ready)bata db branches --project <id> --json
Run SQLsqlbata db query "select 1" --project <id> --json
Fork a sandbox for a taskbranches_create with expires_in: "2h"bata db branch create task-42 --expires-in 2h --project <id>
Browse a tabletable_rows with filtersbata db rows users --filter "age > 30" --project <id> --json
Gate a migrationschema_checkbata migrate check change.sql --project <id>
Find slow queriesinsights_top_queriesbata insights top --project <id> --json
Read recommendationsrecommendations_listbata insights recommendations --project <id> --json
Prove an index firstindex_trials_startbata index trial start --advice <id> --project <id>
Turn on app telemetrytelemetry_setupbata telemetry setup --project <id> --json
Get connection stringsconnection_infobata db url --project <id> --json
Clean upbranches_delete with confirm: truebata db branch delete task-42 --project <id> --yes

4. Contracts to rely on

  • Cold starts are normal. An idle branch scales to zero and wakes on the first query. The MCP sql tool retries while it wakes. The CLI waits up to 90 seconds, or exits 6 at once with --no-wait. Exit 6 and retryable: true both mean "try again in a few seconds".
  • Destructive actions need an explicit yes. MCP tools that delete or rewind take a required confirm: true. CLI commands that do the same need --yes whenever there is no terminal to ask.
  • Errors say what to do. A key without the right scope gets KEY_SCOPE_DENIED, with the scope it needs and the command to mint one. A project-restricted key outside its projects gets KEY_PROJECT_DENIED. The CLI exits 4 for both.
  • A person adds the card. A team on the Free plan runs no compute until a card is on file. Creating or starting one answers PAYMENT_METHOD_REQUIRED (HTTP 402) with an add_card_url. Hand that link to a human; bata billing card add or the MCP billing_add_card tool makes a fresh one. The free allowance stays free. A team at its spend cap gets SPEND_CAP_REACHED. The CLI exits 7 for both, and neither is worth retrying.
  • Cost is honest. A billing dimension that is not metered yet reads as not_metered_yet with a null cost, never as a priced $0.
  • Pooled or direct. Use the pooled connection string for application traffic and serverless functions. Use the direct one for migrations, pg_dump, LISTEN/NOTIFY and anything that needs session state. See Connecting.

Reference