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, thebatacommand): 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.
| Scope | Allows |
|---|---|
read | Every read: projects, branches, schema, insights, usage |
sql | read plus running SQL and reading connection strings |
admin | Everything, including creating and deleting projects, branches and keys |
telemetry:write | Posting 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.
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 --jsonThe key is printed once. Check what a key can do at any time:
BATA_API_KEY=<your-api-key> bata whoami --json1b. 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_KEYis 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 --jsonagain. That resumes the same request: the first line says"resumed": trueand 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 innext(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 qaon 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:
claude mcp add batadata -- npx -y @batadata/mcpOr with a scoped key instead:
claude mcp add batadata --env BATA_API_KEY=<your-api-key> -- npx -y @batadata/mcpClaude Desktop and Cursor use the same mcpServers block. The @batadata/mcp README lists the config file locations.
{
"mcpServers": {
"batadata": {
"command": "npx",
"args": ["-y", "@batadata/mcp"],
"env": { "BATA_API_KEY": "<your-api-key>" }
}
}
}Two optional settings:
BATA_MCP_TOOLSETS=core,insights,indexexposes only some tool groups. Use it for clients that cap the number of tools.BATA_MCP_READONLY=1hides every tool that changes anything.
2b. Or drive the CLI
Set the key in the environment. No login, no prompts:
export BATA_API_KEY=<your-api-key>
bata status --jsonEvery 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.
| Step | MCP tool | CLI |
|---|---|---|
| Who am I, what may I do | whoami | bata whoami --json |
| Create a database | projects_create | bata projects create my-app --json |
| Wait until it can serve | branches_list (poll ready) | bata db branches --project <id> --json |
| Run SQL | sql | bata db query "select 1" --project <id> --json |
| Fork a sandbox for a task | branches_create with expires_in: "2h" | bata db branch create task-42 --expires-in 2h --project <id> |
| Browse a table | table_rows with filters | bata db rows users --filter "age > 30" --project <id> --json |
| Gate a migration | schema_check | bata migrate check change.sql --project <id> |
| Find slow queries | insights_top_queries | bata insights top --project <id> --json |
| Read recommendations | recommendations_list | bata insights recommendations --project <id> --json |
| Prove an index first | index_trials_start | bata index trial start --advice <id> --project <id> |
| Turn on app telemetry | telemetry_setup | bata telemetry setup --project <id> --json |
| Get connection strings | connection_info | bata db url --project <id> --json |
| Clean up | branches_delete with confirm: true | bata 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
sqltool retries while it wakes. The CLI waits up to 90 seconds, or exits 6 at once with--no-wait. Exit 6 andretryable: trueboth 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--yeswhenever 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 getsKEY_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 anadd_card_url. Hand that link to a human;bata billing card addor the MCPbilling_add_cardtool makes a fresh one. The free allowance stays free. A team at its spend cap getsSPEND_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_yetwith 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/NOTIFYand anything that needs session state. See Connecting.
Reference
- CLI reference: every command, generated from the CLI's command registry.
@batadata/mcpon npm: every MCP tool, grouped by toolset.- App keys: the key to put in a deployed app.