# Agent SDK CLI

The Agent SDK is published as `@grok/agent-sdk` with the `agent-sdk`
CLI.

```text
agent-sdk <command>
```

The command catalog below documents the `agent-sdk` binary from
`@grok/agent-sdk`. Run it with Node 22.13 or newer; Bun is not
supported. Commands exit nonzero when validation or a synchronous request fails;
asynchronous commands can exit `0` once work is accepted. Project paths use the
[Agent SDK project layout](/docs/reference/project-layout.md).

Install `@grok/agent-sdk` to obtain the binary. Bare `agent-sdk` prints the command
index. `agent-sdk <command> --help` prints one command's help, `agent-sdk help --all`
prints every command, and `agent-sdk help <command> --json` prints one command's
arguments and flags as JSON.

The `bdk` command remains available.

Commands are named `agent-sdk <resource> <verb>`, such as `agent-sdk deployment list`,
except for a few workflow verbs (`init`, `dev`, `run`, `eval`, `deploy`, and
others). Older names, such as `agent-sdk deployments` or `agent-sdk secrets set`, still
run for now. Each one prints the new form on stderr.

## Command catalog

### Development commands

| Command | Contract |
| --- | --- |
| `help`, `--help`, `-h` | Print the command index, or one command's help |
| [`serve`](#serve) | Serve one or more agents over HTTP |
| [`dev`](#dev) | Serve agents with local-development behavior |
| [`docs`](#docs) | Serve the documentation bundled with `@grok/agent-sdk` |
| [`init`](#init) | Scaffold an Agent SDK project, or convert an Automation |
| [`migrate`](#migrate) | Move an early-alpha project to architecture v2 through a pull request |
| [`skills list`](#skills-install) | List the bundled coding-agent skills and their installed versions |
| [`skills install`](#skills-install) | Refresh the bundled coding-agent skills |
| [`info`](#info) | Print the discovered project surface or its deployment manifest |
| [`validate`](#validate) | Check project diagnostics and set the exit status |
| [`version`](#version) | Print the installed package version |
| [`update`](#update) | Upgrade the installed CLI |
| [`login`](#login-logout-whoami) | Sign the host in to Cursor |
| [`logout`](#login-logout-whoami) | Remove the stored Cursor credential |
| [`whoami`](#login-logout-whoami) | Show the active Cursor credential |

### Session and eval commands

| Command | Contract |
| --- | --- |
| [`chat`](#chat) | Talk to a running agent, or reattach a session |
| [`run`](#run) | Run turns on a local or running agent |
| [`tool call`](#tool-call) | Call a server tool without a model turn |
| [`logs`](#logs) | Follow or dump agent logs |
| [`session list`](#session-list) | List sessions, or their costs |
| [`session get`](#session-get) | Inspect a session or a saved trace, or open it in the playground |
| [`session create`](#session-create) | Start or continue a hosted session |
| [`eval`](#eval) | Run evals, or list, get, and cancel remote eval batches |

### Managed hosting commands

| Command | Contract |
| --- | --- |
| [`deploy`](#deploy) | Deploy one or more agents |
| [`deployment list`](#deployment-list) | List deployments |
| [`deployment get`](#deployment-get) | Inspect one deployment |
| [`deployment stop`](#deployment-stop) | Stop a deployment |
| [`deployment start`](#deployment-start) | Start a stopped deployment |
| [`deployment kill`](#deployment-kill) | Stop a deployment, cancel its runs, and lock deploys |
| [`deployment unlock`](#deployment-unlock) | Let a killed deployment deploy again |
| [`deployment cancel-runs`](#deployment-cancel-runs) | Cancel active runs and reminder wakes |
| [`deployment update`](#deployment-update) | Replace a deployment's event repositories |
| [`deployment upgrade`](#deployment-upgrade) | Move a deployment to a release |
| [`deployment rollback`](#deployment-rollback) | Move a deployment to an earlier release |
| [`deployment delete`](#deployment-delete) | Delete a deployment |
| [`deployment rotate-token`](#deployment-rotate-token) | Replace a deployment's alias token |
| [`secret`](#secret) | Set, list, or delete deployment secrets |
| [`image`](#image) | Push, list, or delete container images for persistent agents |

### Connection and channel commands

| Command | Contract |
| --- | --- |
| [`mcp serve`](#mcp) | Proxy an agent's MCP endpoint over stdio |
| [`mcp install`](#mcp) | Add the agent to an MCP client config |
| [`mcp oauth`](#mcp-oauth) | Authorize an MCP connection |
| [`slack`](#slack) | Provision and check Slack channel apps |
| [`github`](#github) | Forward, replay, and inspect GitHub webhooks |
| [`gitlab`](#gitlab) | Replay and inspect GitLab webhooks |
| [`bitbucket`](#bitbucket) | Replay and inspect Bitbucket webhooks |
| [`pagerduty`](#pagerduty) | Replay and inspect PagerDuty webhooks |

## Targets {#choose-a-target}

Commands that send requests support these targets:

| Target | Selection | Commands |
| --- | --- | --- |
| Ephemeral local server | Omit `--url` and `--prod` | `run`, `tool call`, `eval` |
| Running server | Pass `--url <baseUrl>` | `chat`, `run`, `tool call`, `eval`, `logs`, `session list`, `session get`, `mcp serve` |
| Default local server | Omit `--url` and `--prod`; uses `http://127.0.0.1:3000` | `chat`, `logs`, `session list`, `session get` |
| Hosted deployment | Pass `--prod` | `session list`, `session get`, `logs`; `session create` is always hosted |

An explicit `--url` must include the slug for a multi-agent server, such as
`http://127.0.0.1:3000/pr-approver`. `--slug` never changes an explicit URL.
`--url` and `--prod` are mutually exclusive.

| Option | Contract |
| --- | --- |
| `--dir <path>` | Select the project root. The default is the current directory. |
| `--url <baseUrl>` | Use a running agent instead of local project discovery. |
| `--prod` | Use the hosted deployment. `session list`, `session get`, and `logs` accept it; `chat`, `run`, `tool call`, `mcp serve`, and `eval` exit with a usage error. |
| `--slug <slug>` | Select an agent from a multi-agent project or a hosted deployment. Under `--prod`, the default is the `--dir` basename. |
| `--team <id>` | Select a Cursor team. The signed-in account's team is the default. |
| `--api-key <key>` | Override the Cursor credential for commands that authenticate with Cursor. |
| `--bearer-token <token>` | Authenticate to a running server with a bearer token. |
| `--state-root <path>` | Select local state for `serve` and ephemeral `run`, `tool call`, and `eval` targets. Running and `--prod` targets ignore it. |
| `--json` | Print one JSON document on stdout, errors included (`{"error":{"code","message","hint"}}`). Without it, output is text, also when piped. `agent-sdk help` lists the exit codes. |

## Local servers

### Serve agents {#serve}

`serve` mounts every project discovered under `--dir`:

```text
agent-sdk serve [--dir <path>] [--port <n>] [--host <host>]
  [--mode multi|single] [--dev] [--api-key <key>]
  [--state-root <path>]
  [--bearer-token <secret> | --allow-anonymous]
  [--allow-anonymous-cursor-github]
  [--allow-anonymous-cursor-account-mcp]
  [--public-url <url>]
  [--peers-url <url>]
  [--no-schedules] [--no-playground] [--no-docs]
  [--cursor-events --repo <owner/name>]...
```

| Option | Contract |
| --- | --- |
| `--port <n>` | Listen on this port. The default is `3000`; `0` selects an available port. When an omitted default is occupied, the CLI selects the next port. An occupied explicit port fails with a next-port hint. |
| `--host <host>` | Bind this host. The default is loopback-only `127.0.0.1`. |
| `--mode multi` | Mount projects at `/<slug>/v1/*` and `/<slug>/playground`, with an index at `/`. This is the default. |
| `--mode single` | Mount one project at `/v1/*` and `/playground`. Declared peers are omitted with a warning unless `--peers-url` is set. |
| `--dev` | Disable automatic schedule and reminder firing, admit unsigned loopback GitHub deliveries, and enable local playground access. |
| `--state-root <path>` | Store local sessions, streams, workspaces, and channel state here. Keep durable state outside a repository whose rules shouldn't reach agent workspaces. |
| `--bearer-token <secret>` | Require this token on routes without authored authentication. Mutually exclusive with `--allow-anonymous`. |
| `--allow-anonymous` | Admit callers as one anonymous principal. Use it only behind a trusted network boundary. |
| `--allow-anonymous-cursor-github` | Let anonymous callers use sessions with a Cursor account's repository-scoped GitHub credential. Requires an authenticating proxy. |
| `--allow-anonymous-cursor-account-mcp` | Let anonymous callers use Cursor account MCP connections. Requires an authenticating proxy. |
| `--public-url <url>` | Set the externally reachable base URL that turns running off the serve host use for peer MCP and host bridges. It does not expose the server; configure the listener or proxy separately. Local-runtime peers use loopback. |
| `--peers-url <url>` | Resolve declared peers for a single-agent serve through a hosting-provided peer service. Cursor-managed hosting sets this option automatically; a multi-agent serve rejects it. |
| `--no-schedules` | Disable schedule firing outside dev mode. |
| `--no-playground` | Skip the playground. |
| `--no-docs` | Skip the documentation site at `/docs`. |
| `--cursor-events` | Receive SCM events through Cursor. Requires sign-in and one or more repeatable `--repo owner/name` values. |

Multi-agent slugs start with a letter or digit and contain only letters,
digits, `_`, or `-`. The reserved slugs are `v1`, `playground`, and `docs`.

### Develop locally {#dev}

`dev` is `serve --dev` with an optional positional project path:

```text
agent-sdk dev
agent-sdk dev ./agent-sdk-pr-reviewer --port 3000
```

It accepts every `serve` option. Pass at most one positional path, and don't
combine it with a different `--dir`.

### Serve the docs {#docs}

```text
agent-sdk docs [--port <n>] [--host 127.0.0.1] [--print]
```

`docs` serves the bundled documentation without an agent project. It uses an
available loopback port by default and stays open until Ctrl-C. `--print`
prints the URL without opening a browser.

## Sessions and turns

### Chat with an agent {#chat}

`chat` connects to a running server and never starts an ephemeral one:

```text
agent-sdk chat [--url <baseUrl>] [--message <text>]
  [--session <id> | --resume] [--continuation-token <token>]
  [--slug <slug>] [--json] [--text] [--no-color]
```

| Input mode | Behavior |
| --- | --- |
| TTY without `--message` | Open an interactive REPL. |
| TTY with `--message` | Send the message, then keep the REPL open. |
| Non-TTY with `--message` | Send one turn and exit. |
| Non-TTY without `--message` | Read newline-delimited messages until EOF. |
| `--json --message <text>` | Send one turn and print `{ ok, sessionId, continuationToken, trajectory }`. |
| `--json --text --message <text>` | Print a compact trajectory instead of JSON. |

Use `--session <id>` to reattach a stored session and replay its transcript.
If you omit `--continuation-token`, the CLI looks it up from the target's
session list. `--resume` selects the most recently updated session with a
continuation token. An explicit session ID takes precedence.

### Run turns {#run}

`run` sends messages to an ephemeral local server or a running server:

```text
agent-sdk run [--dir <path>] [--message <text>]...
  [--messages-file <path>] [--url <baseUrl>]
  [--session <id>] [--continuation-token <token>]
  [--trace <file> | --no-trace] [--json]
  [--timeout-ms <n>] [--no-stream] [--slug <slug>] [--dry-run]
```

| Option | Contract |
| --- | --- |
| `--message <text>` | Send a user message. Repeat it for a multi-turn run. |
| `--messages-file <path>` | Read a JSON array of strings. File messages run before repeated `--message` values. |
| `--session <id>` | Continue a session on the target. |
| `--continuation-token <token>` | Continue the session selected by `--session` on a running server. |
| `--trace <file>` | Write the raw NDJSON stream to this path. |
| `--no-trace` | Skip the NDJSON trace. |
| `--json` | Print the JSON result instead of the compact trajectory. |
| `--timeout-ms <n>` | Stop waiting after a positive number of milliseconds. There is no default timeout. |
| `--no-stream` | Hide live tool and reply progress on stderr. |
| `--dry-run` | Send nothing. Print the request the run would send. A local run starts no server. |

Text output is the compact trajectory, with the session ID and trace path on
stderr. `--json` output contains `ok`, `sessionId`, `continuationToken`,
`trace`, `playgroundUrl`, `visualize`, and `trajectory`. The `trace` value
gives the saved path when trace output is enabled.

For the hosted deployment, use [`session create`](#session-create).

### Call a tool {#tool-call}

`tool call` invokes a server tool without a model turn:

```text
agent-sdk tool call <tool> [--input <json>]
  [--dir <path> | --url <baseUrl>]
  [--session <id>] [--slug <slug>] [--json]
```

`--input` accepts JSON and defaults to `{}`. `--session` uses the session
workspace and records the call on its event stream. The command prints the
tool result (a string as is, anything else as JSON). `--json` prints the
server's full JSON response. The command succeeds only when the HTTP response
succeeds with `ok: true`.

Hosted deployments don't support `tool call`. Start a model turn with
[`session create`](#session-create) and check it with
[`session get`](#session-get).

See [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn) for validation,
busy-session behavior, and error codes.

### List sessions {#session-list}

```text
agent-sdk session list [--url <baseUrl> | --prod] [--since <duration|instant>]
  [--view sessions|cost] [--slug <slug>] [--team <id>]
  [--bearer-token <token>] [--limit <n>] [--page-token <token>] [--json]
```

Text output shows the session ID, channel, mode, turn count, running status,
and update time. `--json` prints `{ items, nextPageToken }`, including
continuation tokens. `--limit` (default 20, at most 100) sets the page size,
and `--page-token` reads the next page.

With `--prod`, `session list` lists the hosted deployment's most recently
active sessions; `--since 24h` (or an ISO-8601 date or instant) reads back to
that cutoff, up to `--limit` sessions. Check one with
[`session get <sessionId> --prod`](#session-get).

`--view cost` prints token usage per session and a total from a running
server. Turns saved before cost tracking appear as unpriced. `--json` prints
`{ sessions }`. `--prod` doesn't support this view.

### Inspect a session {#session-get}

```text
agent-sdk session get <sessionId> [--url <baseUrl> | --prod]
  [--slug <slug>] [--team <id>]
  [--view trajectory|events|cost] [--json [--fields <a,b.c>]]
  [--trace <file.ndjson>] [--web [--print]]

agent-sdk session get --file <file.ndjson> [--json]
```

| Option | Contract |
| --- | --- |
| No option | Print a compact trajectory. |
| `--json` | Print the trajectory object. |
| `--view events` | Print `{ sessionId, events }` with the raw event list. With `--prod`, each tool call is an `actions.requested` or `action.result` event. The tool name is `toolName`. |
| `--view cost` | Print per-turn token usage and estimated cost from a running server. `--prod` doesn't support it. |
| `--trace <file.ndjson>` | Write the raw NDJSON trace instead of printing it. Not available with `--prod`. |
| `--file <file.ndjson>` | Read a saved trace (for example from `run --trace`) instead of a server, and print its trajectory. |
| `--web` | Open the session in the playground, or the [Deployed Agents](https://cursor.com/dashboard/deployed-agents) playground with `--prod`. `--print` prints the URL only. A running target with credentials that a browser can't send starts a loopback proxy and stays open until Ctrl-C. |

Use [`chat --session`](#chat) to continue a conversational session on a
local or running server.

With `--prod`, the command prints the session's hosted executions: one line
per run with its status, duration, and time, followed by that run's log
lines. When the deployment stored process output, a `Process output:` section
prints that output next. `--json` prints `{ sessionId, executions, startLog, ... }`.
`startLog` is the process output, or `null` when none is stored.
`--view events` prints `{ sessionId, events }`. `--trace` isn't available.
While a turn is running, text output also reports the event count and latest
event type and prints `Turn running; runs may lag behind events.` A run can
show `running` with no logs until the turn ends, so a growing event count is
the progress signal.

### Start a hosted session {#session-create}

```text
agent-sdk session create --message <text> [--session <ses_...>]
  [--slug <slug>] [--team <id>] [--wait [--timeout-ms <n>]]
  [--read-only] [--as-of <instant>] [--component <key>] [--dry-run] [--json]
```

| Option | Contract |
| --- | --- |
| `--message <text>` | The message to send. Exactly one. |
| `--session <ses_...>` | Continue a hosted session that `session create` started. Slack, SCM, and custom-route sessions can't be continued this way. |
| `--wait` | Poll the delivery until it names the session, then print it. Exit `3` (`pending`) when `--timeout-ms` (default 120000) passes first. A skipped or failed delivery exits `1`. |
| `--read-only` | Run read tools and stub write tools. |
| `--as-of <instant>` | Freeze the session's clock at an ISO-8601 instant with a timezone, such as `2026-08-31T12:00:00Z`. |
| `--component <key>` | Wake a named component instead of the deployment's default. A continued session must belong to that component. |
| `--dry-run` | Send nothing. Print the request it would send. |

Without `--wait`, the command returns as soon as the deployment accepts the
turn and exits `0` before the turn outcome is known. It prints the delivery
ID, the session ID when known, and the session mode. `--json` prints
`deliveryId`, `workflowId`, `sessionMode`, and `sessionId` when known, plus
`dryRun` and `asOf` when set. A fresh start has no `sessionId` until the turn
starts; a continuation echoes the `--session` value. See
[Verify a hosted turn](/docs/deployment.md#verify-a-hosted-turn).

### Follow logs {#logs}

```text
agent-sdk logs [sessionId] [--url <baseUrl> | --prod]
  [--session <sessionId>] [--slug <slug>] [--team <id>] [--once] [--json]
```

The command follows logs until Ctrl-C. `--once` prints the current buffer and
exits; `--json` emits newline-delimited JSON events. With `--prod`, it follows
the output of the hosted deployment's most recent sessions on every channel
and prints a `==> <sessionId> (<channel>) <==` header each time the output
switches sessions. A persistent deployment also prints a `==> process-output <==`
block. That block is the process log that [Deployed Agents](https://cursor.com/dashboard/deployed-agents) shows for the deployment.
Pass a session ID, as an argument or with `--session`, to follow only that
session; a session ID requires `--prod`. The session view starts with
`Process output:` for the deployment process log and `Session process output:`
for that session's own process output, and a `tool <name>` line for each tool
call. Hosted log events
carry `sessionId` and `line` rather than `index` and `at`.

## Evals

### Run evals {#eval}

`eval` discovers `evals/**/*.eval.{ts,js}` and runs selected cases:

```text
agent-sdk eval [--dir <path>] [evalId...]
  [--list] [--tag <tag>]... [--json] [--verbose]
  [--timeout-ms <n>] [--strict] [--max-concurrency <n>]
  [--junit <path>] [--artifacts <dir> | --no-artifacts]
  [--skip-report] [--out <file.json>] [--no-stream]
  [--url <baseUrl>] [--no-wait] [--slug <slug>]
```

Select an exact case such as `weather/nyc`, a file prefix such as `weather`,
several IDs, or no IDs for every case. Repeat `--tag` for OR matching. To run
an eval whose ID is a subcommand word (`list`, `get`, `cancel`), put it after
`--`, as in `agent-sdk eval -- list`. Projects must provide
`evals/evals.config.{ts,js}` with `maxConcurrency` between 1 and 200.

| Option | Contract |
| --- | --- |
| `--list` | List matching cases without running them. `--list --json` prints an array. |
| `--tag <tag>` | Select a tag. Repeat the option for OR matching. |
| `--json` | Print `{ ok, passed, failed, scored, skipped, strict, results }`. |
| `--verbose` | Stream `t.log` lines and reply snippets. |
| `--no-stream` | Hide live progress on stderr. |
| `--strict` | Treat a scored case below its soft threshold as a failure. |
| `--max-concurrency <n>` | Override `maxConcurrency` from the eval config for an ephemeral local run. |
| `--timeout-ms <n>` | Override the per-case timeout. A case timeout takes precedence, followed by this option, the config timeout, and the 180-second default. |
| `--junit <path>` | Write JUnit XML for an ephemeral local run. |
| `--artifacts <dir>` | Select the artifact directory for an ephemeral local run. The default is a timestamped directory under the project's state directory. |
| `--no-artifacts` | Skip artifacts for an ephemeral local run. |
| `--skip-report` | Ignore reporters from eval files and the eval config during an ephemeral local run. |
| `--out <file.json>` | Also write the full result JSON. |
| `--no-wait` | Return after a running server accepts the batch. |

A running server prints its Eval ID when it accepts the batch.
Use these commands to inspect or cancel it:

```text
agent-sdk eval list --url <baseUrl> [--limit <n>] [--page-token <token>]
agent-sdk eval get <evalId> --url <baseUrl> [--out <file.json>]
agent-sdk eval cancel <evalId> --url <baseUrl>
```

`eval list` lists recent remote runs. `eval get` exits `3` (`pending`) while
the batch still runs. All three require `--url`.

## Projects

### Scaffold a project {#init}

```text
agent-sdk init [directory] [--template <name>] [--var id=value]... [--json] [--dry-run]
agent-sdk init [directory] --from automation:<url-or-uuid> [--dry-run] [--json]
```

Without a directory, `init` prints the setup guide and changes no files. With
a directory, it preserves existing scaffold files, adds missing files,
installs dependencies, and tries to put `agent-sdk` on `PATH`.

| Option | Contract |
| --- | --- |
| `--template <name>` | Use a bundled template: `agentic-owners`, `agents-md`, `code-wiki`, `demo`, `security-help`, `security-reviewer`, `thermo-quality-review`, `thermo-review`, `triage-jira`, or `triage-linear`. `triage` selects `triage-linear`. |
| `--var id=value` | Answer a template question without a prompt. Repeat for multiple answers. |
| `--json` | Skip interactive login and skill prompts, then print `{ ok, directory, template, created, skipped, installed, installError, cliOnPath, cliLinkError, next }`. `ok` reflects dependency installation. |
| `--dry-run` | List the files `init` would create. Write and install nothing. |

On a TTY, templates with questions run their interview before writing files.
`init` also offers to refresh the bundled coding-agent skills and starts login
when the host has no Cursor credential.

`--from grokbot:<recipe.json>` and `--from-grokbot <recipe.json>` exit with
`Grok Bot import was removed.`

#### Convert an Automation {#convert-automation}

```text
agent-sdk init [directory] --from automation:<url-or-uuid> [--dry-run] [--json]
```

The input can be a Cursor Automation dashboard URL or its UUID. The output
directory defaults to `./<automation-name>`; a positional directory overrides
it. Generated files don't overwrite
existing paths, and missing scaffold files are added. The following
`npm install` can update lockfiles and run lifecycle scripts from an existing
`package.json`.

`--json` prints
`{ ok, directory, files, warnings, setupSteps, installed, installError, mcpConnections }`.
Conversion warnings and dependency-install failures remain visible in this
output without failing a completed file conversion. Invalid input,
authentication, export, and file-write failures exit nonzero.

See [Convert a Cursor Automation](/docs/guides/convert-automation.md) for the
generated project and features that need manual review.

### Migrate an early-alpha project {#migrate}

```text
agent-sdk migrate [--dir <path>] [--repo <https-url>] [--ref <git-ref>]
  [--path <agent-path>] [--no-wait] [--json] [--api-key <key>]
```

`migrate` applies only to projects deployed on architecture v1 during the Agent SDK
early alpha. It reads a pushed Git ref, starts a Cursor cloud agent with the
bundled `bdk-migrate-v2` skill, and opens a pull request with the source
changes. The command waits for the pull request by default and never deploys
the project or changes deployment secrets. When it starts an agent, it prints
a checklist for the steps after the first deploy: deploy each agent folder,
create the prod Slack app, unset peer URL secrets, and set secrets a new slug
still needs.

The command prints `Nothing to migrate.` and exits 0 only when `--dir` shows
no migration work. That check looks for a legacy early-alpha package dependency,
`architecture: "v1"`, `bot/storage.ts` or `agent/storage.ts`,
`runtime: "grokbot"`, a literal SCM credential, a `/v1` continuation selector,
an anonymous reminder, and a `*_MCP_URL` peer connection. When any of those is
present, or a project file cannot be read, the command starts the agent. It
does not read the hosted deployment.

| Option | Contract |
| --- | --- |
| `--dir <path>` | Select the local Agent SDK project. The default is the current directory. |
| `--repo <https-url>` | Select the source repository. The CLI infers the current checkout's origin when possible. |
| `--ref <git-ref>` | Select the pushed ref for the cloud agent. The CLI infers the current branch when possible. |
| `--path <agent-path>` | Select the project path relative to the repository root. The CLI infers it from `--dir` when possible. |
| `--no-wait` | Print the cloud agent URL and return after the agent starts. |
| `--json` | Print a report with `ok`, `status`, `agentId`, and the cloud agent `url`. A completed migration adds `prUrl` and `checklist`. A run with nothing to migrate uses `status: "current"` and `message` (no agent started) or `summary` (the agent reported it). A blocked or failed migration adds its explanation. |
| `--api-key <key>` | Override the Cursor credential. |

Credential resolution follows the normal order: `--api-key`,
`CURSOR_API_KEY`, `CURSOR_API_KEY_FILE`, `CURSOR_SERVICE_ACCOUNT_KEY`, then
the stored `agent-sdk login` credential. See
[Migrate an early-alpha project](/docs/guides/migrate.md) for detection, pull
request review, and deployment cutover.

### Install and list coding-agent skills {#skills-install}

```text
agent-sdk skills install [--dry-run] [--json]
```

The command replaces the installed Agent SDK skills with the package copy and
removes stale bundled skills. `--dry-run` previews the change. `--json` prints
`{ ok, dryRun, directory, firstInstall, skills, removed }`.

Package installation performs this copy.
Run `agent-sdk skills install` when you need to refresh it.

Each installed `SKILL.md` records the package release in `metadata.version`.

```text
agent-sdk skills list [--json [--fields <a,b.c>]] [--limit <n>] [--page-token <token>]
```

`skills list` prints each bundled skill and the version of its installed
copy. `--json` prints `{ items, nextPageToken }`. Each item has `name`,
`description`, `version`, `installedVersion`, and `path`. `installedVersion`
is `null` when the skill is not installed, and `"unknown"` when the installed
copy has no `metadata.version`. When `installedVersion` differs from
`version`, run `agent-sdk skills install`.

The `bdk-cli` skill holds the rules for agents that run `agent-sdk`: `--json`,
exit codes, `--fields`, `--dry-run`, `--yes`, secrets, and untrusted output.

### Inspect a project {#info}

```text
agent-sdk info [--dir <path>] [--view info|manifest] [--json [--fields <a,b.c>]]
```

Text output lists the model, instructions, tools, skills, extensions,
connections, subagents, channels, schedules, hooks, hosting settings, and
diagnostics. For one unslugged project, `--json` prints its project info
object. For slugged or multi-agent discovery, it prints `{ agents }`.

`--view manifest` validates one project and prints a summary of its
deployment metadata without a network request. `--json` then prints the full
manifest JSON.

### Validate a project {#validate}

```text
agent-sdk validate [--dir <path>]
```

`validate` prints every discovered project's diagnostics and exits nonzero
when any diagnostic has error severity. Use it instead of `info --json` when a
script needs validation status.

Hosting checks run here too. On architecture v2 (the default), a
`githubChannel` credential or a GitLab or Bitbucket `webhookSecret` written
as a literal or a `() => process.env.NAME` getter is an error whose message
quotes the `{ secretName: "NAME" }` form to use instead. A project that still
sets `architecture: "v1"` gets the same finding as a warning, so run
`validate` before removing the pin to list what the migration must rename.

## Credentials and versions

### Manage credentials {#login-logout-whoami}

```text
agent-sdk login [--api-key <key>] [--key-name <name>]
agent-sdk whoami [--json]
agent-sdk logout
```

| Command | Contract |
| --- | --- |
| `login` | Use a supplied API key, or open browser sign-in and store the resulting dashboard-revocable key in the CLI config directory. `--key-name` labels a browser-created key. |
| `whoami` | Show the active credential and its source. `--json` returns the same identity as JSON. |
| `logout` | Remove the stored credential file. It doesn't revoke the API key; revoke the key in the Cursor dashboard. |

### Print the version {#version}

```text
agent-sdk version [--json]
agent-sdk --version
agent-sdk -V
```

Text output is the version number. `--json` prints
`{ name, version }`.

### Update the CLI {#update}

```text
agent-sdk update
```

`update` checks npm's `latest` tag and upgrades a recognized global or project
install with its package manager. If it can't identify an installed copy, it
prints a manual upgrade command instead.

Published installs check for updates at most once every 24 hours. Set
`AGENT_SERVE_NO_UPDATE_CHECK`, `NO_UPDATE_NOTIFIER`, or `CI` to disable the
automatic warning.

## Managed hosting

Every command here that changes a deployment takes `--dry-run`. It runs the
reads (sign-in, team, and deployment checks), then stops at the first request
that would change state and prints that request, with secret values
redacted. Nothing is sent, and the command exits `0`.

`deployment kill`, `deployment cancel-runs`, `deployment delete`,
`deployment rotate-token`, `secret delete`, `image delete`, and
`slack destroy` cannot be undone. On a terminal they ask you to type the
slug or name. Without a terminal, as when an agent runs them, they exit `2`
unless you pass `--yes`.

### Deploy agents {#deploy}

```text
agent-sdk deploy [--dir <path>] [--slug <slug> | --all] [--team <id>]
  [--repo <https-url>] [--ref <git-ref>] [--path <agent-path>]
  [--cursor-events-repo <repo>]... [--no-wait] [--json]
```

Managed hosting requires a team with agent hosting enabled. A team
service-account API key with agent access can deploy.

| Option | Contract |
| --- | --- |
| `--slug <slug>` | Select one project and set its deployment slug. Under a folder of several agents, this deploys that one agent. |
| `--all` | Deploy every child under a folder of several agents. For a folder of v2 agents, print one deploy command for each agent. |
| `--team <id>` | Select the Cursor team. |
| `--repo <https-url>` | Set the source repository. The CLI infers it from the current checkout when possible. HTTPS is required. |
| `--ref <git-ref>` | Set the source Git ref. The CLI infers the current branch when possible. |
| `--path <agent-path>` | Set the project path relative to the repository root. |
| `--cursor-events-repo <repo>` | Watch a repository's Cursor events. Write `owner/name` for GitHub and Origin, `gitlab.com/<group>/<project>` for GitLab (subgroups allowed), or `bitbucket.org/<workspace>/<repo>` for Bitbucket. Repeat the flag for each repository; the repositories you pass replace the previous list. Without the flag, the deployment watches its GitHub source repository. |
| `--no-wait` | Return after the deployment request is accepted. |
| `--json` | Print one object for one target or an array for multiple targets. |

Deployment slugs contain lowercase letters, digits, `_`, or `-`, start with a
letter or digit, and have at most 64 characters. Egress entries are lowercase
hostnames with an alphabetic top-level domain; one leading `*.` wildcard is
allowed, or a trailing `:port` for a protocol that is not HTTPS, with at most
64 entries.

By default, `deploy` waits until the deployment is running and exits nonzero
if it reaches a failed state. A first deployment can return an alias token
once. Store it as a secret and send it as `X-Agent-Alias-Token` when calling
the stable alias URL.

When the team egress policy blocks a host, `deploy` exits nonzero and prints
`Error: agent-sdk deploy blocked.` The message names each blocked host, the team
egress mode, and two fixes: add the hosts to the team egress list, or remove
them from `hosting.egressDomains` and deploy again. The running bot does not
change.

An agent that sets `architecture: "v1"` does not deploy: `deploy` exits
nonzero and tells you to run [`migrate`](#migrate) first.

### List deployments {#deployment-list}

```text
agent-sdk deployment list [--team <id>] [--limit <n>] [--page-token <token>]
  [--json [--fields <a,b.c>]]
```

Text output shows slug, status, and update time. `--json` prints
`{ items, nextPageToken }`. `--limit` (default 20, at most 100) sets the page
size, and `--page-token` reads the next page. An empty list succeeds.

### Inspect a deployment {#deployment-get}

```text
agent-sdk deployment get <slug> [--team <id>] [--json [--fields <a,b.c>]] [--web]
```

Text output includes status, release, watched repositories, routes, secret
names, and the latest error. `--web` opens the deployment's page in
[Deployed Agents](https://cursor.com/dashboard/deployed-agents). A deployment still on architecture v1 shows its status with
`architecture: v1` and the [`migrate`](#migrate) steps; `deployment stop` and
`deployment delete` also still accept it.

### Stop a deployment {#deployment-stop}

```text
agent-sdk deployment stop <slug> [--team <id>] [--no-wait] [--cancel-runs] [--json]
```

The command waits for `stopped` by default. `--no-wait` returns after the
request is accepted. `--cancel-runs` also requests cancellation of active
runs and reminder wakes.

### Start a deployment {#deployment-start}

```text
agent-sdk deployment start <slug> [--team <id>] [--json]
```

`deployment start` resumes a stopped deployment. It doesn't remove the
deploy lock that [`deployment kill`](#deployment-kill) sets.

### Kill a deployment {#deployment-kill}

```text
agent-sdk deployment kill <slug> [--team <id>] [--yes] [--dry-run] [--json]
```

Use `deployment kill` in an emergency. It locks deploys of the slug, stops
the deployment, and requests cancellation of its active runs and reminder
wakes. To bring the deployment back, run
[`deployment unlock`](#deployment-unlock) and then
[`deployment start`](#deployment-start).

### Unlock deploys {#deployment-unlock}

```text
agent-sdk deployment unlock <slug> [--team <id>] [--json]
```

`deployment unlock` removes the lock that `deployment kill` sets, so
`deploy`, `deployment upgrade`, and `deployment rollback` work for the slug
again. It doesn't resume traffic; run `deployment start` for that.

### Cancel active runs {#deployment-cancel-runs}

```text
agent-sdk deployment cancel-runs <slug> [--team <id>] [--yes] [--dry-run] [--json]
```

The command requests cancellation for a deployment's active runs and reminder
wakes. It exits nonzero if any cancellation request fails to start.

### Replace event repositories {#deployment-update}

```text
agent-sdk deployment update <slug> (--event-repo <owner/name>... | --no-event-repos)
  [--team <id>] [--dry-run] [--json]
```

The supplied repositories replace the deployment's complete event repository
list; they aren't merged with the previous list. `--no-event-repos` clears
the list.

### Upgrade a deployment {#deployment-upgrade}

```text
agent-sdk deployment upgrade <slug> [--release <id-or-version>]
  [--team <id>] [--json]
```

`--release` accepts a release ID or version. Omit it to select the newest
ready release. `--json` prints `{ releaseId }`.

### Roll back a deployment {#deployment-rollback}

```text
agent-sdk deployment rollback <slug> [--release <id-or-version>]
  [--team <id>] [--json]
```

`deployment rollback` moves the deployment to an earlier ready release without
building a new one. Omit `--release` to select the release before the
current one. `--json` prints `{ releaseId }`.

### Delete a deployment {#deployment-delete}

```text
agent-sdk deployment delete <slug> [--team <id>] [--no-wait] [--yes] [--dry-run] [--json]
```

`deployment delete` removes the deployment and frees its slug. It waits until the
deployment is gone unless `--no-wait` is set.

### Rotate an alias token {#deployment-rotate-token}

```text
agent-sdk deployment rotate-token <slug> [--team <id>] [--yes] [--dry-run] [--json]
```

The old token stops working immediately. The replacement appears once;
`--json` prints `{ aliasToken }`.

### Manage deployment secrets {#secret}

```text
agent-sdk secret set <slug> NAME [NAME2 ...]
  [--team <id>] [--from-argv] [--json]
agent-sdk secret list <slug> [--team <id>] [--limit <n>] [--page-token <token>] [--json]
agent-sdk secret delete <slug> NAME [--team <id>] [--yes] [--dry-run] [--json]
```

Pass secret names to `set`. On a TTY, the CLI prompts for a hidden value and
ends that value at the first newline. With one name, redirected stdin is the
whole value and one trailing newline is removed, so a PEM private key keeps
its line breaks:

```bash
agent-sdk secret set my-bot GITHUB_APP_PRIVATE_KEY < ./app.private-key.pem
```

With several names, provide one line per name. `set` rejects `NAME=VALUE` on
the command line unless `--from-argv` is set, because those values can enter
shell history and captured terminals.

Names use `UPPER_SNAKE_CASE`, start with a letter, and contain at most 64
characters. Names beginning with `CURSOR_` are reserved. Values contain at
most 4096 bytes, and each deployment holds at most 32 secrets.

`list` returns names and creation times, never values. JSON output is
`{ secretNames }` for `set`, `{ items, nextPageToken }` for `list`, and
`{ removed }` for `delete`. Changes are available to subsequent hosted work.

### Manage container images {#image}

```text
agent-sdk image push <local-image> --name <name> [--team <id>] [--json]
agent-sdk image list [--team <id>] [--limit <n>] [--page-token <token>] [--json]
agent-sdk image delete <name> [--team <id>] [--yes] [--dry-run] [--json]
```

A persistent agent can run inside a container image that your team pushes
and names in `hosting.image`. `push` needs Docker on your machine. It
checks the local image first: the image must be `linux/amd64` and have
`bash`, `git`, `node` 22.13 or newer, and `npm` on `PATH`. The command
then compresses the image and uploads it under `--name`.

`list` shows the team's images and how many the team can keep (`--json`:
`{ items, nextPageToken, teamImageLimit }`), and `delete` deletes one image. A deploy pins the image it uses, so after you push a
new image under the same name, deploy again to pick it up.

## MCP connections

### Connect an MCP client {#mcp}

```text
agent-sdk mcp serve --url <baseUrl> [--bearer-token <token>]

agent-sdk mcp install --url <baseUrl> [--bearer-token <token>]
  [--name <server-name>] [--dry-run] [--json]
```

| Command | Contract |
| --- | --- |
| `mcp serve` | Read newline-delimited JSON-RPC from stdin and proxy it to the selected agent's MCP endpoint. stdout is reserved for MCP messages. A target is required. |
| `mcp install` | Add the selected agent to `~/.cursor/mcp.json`. The entry runs `agent-sdk mcp`, which older CLI versions also understand. `--name` sets the server name, `--dry-run` previews the entry, and `--json` prints a machine-readable result. A `--url` entry with `--bearer-token` contains that token; handle printed and written copies as credentials. |

Hosted deployments don't support `mcp serve` or `mcp install`; both exit with a
usage error when you pass `--prod`.

### Authorize MCP OAuth {#mcp-oauth}

```text
agent-sdk mcp oauth <connection> [--dir <path>]
  [--slug <slug>] [--team <id>]
```

`<connection>` is the basename under `bot/mcp-connections/` or
`bot/host-connections/`.

| Connection | Result |
| --- | --- |
| `defineConnection({ url, oauth: true })` | Open a browser PKCE flow and save tokens in `mcp-auth.json` under the CLI config directory. |
| `defineConnection({ cursorAccount: true })` | Exits nonzero and points at the cursor.com dashboard, where you authorize the deployment's connectors. |

URL-connection tokens are bound to the resource URL; authorize again after
changing it.

See [Host MCP OAuth](/docs/guides/mcp-oauth.md) for the full setup flow.

## Channel helpers

### Configure Slack {#slack}

```text
agent-sdk slack setup
agent-sdk slack create [--dir <path>] [--name <name>] [--env prod]
  [--slack-team <id>] [--team <id>] [--icon <url-or-file>]
  [--prefix <prefix> | --no-prefix] [--channel-posts] [--json]
agent-sdk slack destroy [--dir <path>] [--env prod] [--yes] [--dry-run]
  [--slack-team <id>] [--team <id>] [--json]
agent-sdk slack icon <url-or-file> [--dir <path>] [--env prod]
  [--slack-team <id>] [--team <id>] [--json]
agent-sdk slack init --manual [--dir <path>] [--name <name>]
  [--prefix <prefix> | --no-prefix] [--channel-posts]
  [--install | --no-install] [--slack-team <id>] [--env dev|prod]
agent-sdk slack manifest [--dir <path>] [--name <name>]
  [--env dev|prod|both] [--channel-posts] [--dry-run]
agent-sdk slack doctor [--dir <path>]
  [--prefix <prefix> | --no-prefix] [--channel <id>]... [--json]
```

| Subcommand | Contract |
| --- | --- |
| `setup` | Print the setup choices without changing files. |
| `create` | Open the signed-in [Deployed Agents](https://cursor.com/dashboard/deployed-agents) wizard, write the resulting token pair to `.env.local`, and run `doctor`. The development app is the default; `--env prod` selects production. A second create overwrites the live app manifest. |
| `destroy` | Delete the provisioned app. Existing local token entries remain but stop working. |
| `icon` | Set an HTTPS or local PNG, JPG, or GIF icon of at most 512 KB. |
| `init --manual` | Write the channel, manifests, Slack CLI project, and setup files for a manually managed app. |
| `manifest` | Generate Slack manifests. `--env` defaults to `both`; with `--dry-run`, select one environment when you need one JSON document. |
| `doctor` | Check both tokens, Socket Mode connectivity, Slack authentication, and any repeatable `--channel` values. |

The default token prefix is the project directory name normalized to upper
snake case. `--no-prefix` uses `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN`.
Channel-post subscriptions are off by default; `--channel-posts` adds message
events for public and private channels as required by
`engagement.channelPosts`.
Multi-agent servers need one token pair per agent.

See [Slack](/docs/guides/slack.md) for app consent and manual setup.

### Test GitHub webhooks {#github}

```text
agent-sdk github forward [--dir <path>] [--slug <slug>] [--channel <id>]
  [--repo owner/repo | --org <org>] [--events a,b,c]
  [--url <url>] [--host <host>] [--port <n>]
  [--secret <secret>] [--install]
agent-sdk github replay <pr-url-or-owner/repo#N>
  [--events a,b,c | '*'] [--action <action>] [--conclusion <result>]
  [--comment <body>] [--context <name>] [--dir <path>]
  [--slug <slug>] [--channel <id>] [--url <url>]
  [--secret <secret>] [--dry-run] [--fixtures-dir <dir>] [--json]
agent-sdk github events [--dir <path>] [--host <host>] [--port <n>] [--json]
agent-sdk github doctor [--install] [--json]
```

| Subcommand | Contract |
| --- | --- |
| `forward` | Forward live deliveries with `gh webhook forward`. The CLI derives URLs and events from discovered channels and can fan out to several matches. |
| `replay` | Read a pull request, synthesize selected webhook payloads, and post them to matching channels. The default event is `pull_request`; `'*'` selects the channel's supported declared events. |
| `events` | List discovered channel URLs and event sets. No channels is a successful empty result. |
| `doctor` | Check the GitHub CLI, its login, and the pinned webhook extension. `--install` installs or repairs the extension. |

Replay supports `pull_request`, `issue_comment`,
`pull_request_review_comment`, `check_run`, `check_suite`, `workflow_run`, and
`status`. `--dry-run` prints without posting. `--fixtures-dir` writes fixtures and
still posts unless combined with `--dry-run`.

Forwarding requires repository admin access, or organization owner access for
`--org`. It uses the GitHub CLI's stored login; unset `GITHUB_TOKEN` and
`GH_TOKEN` before forwarding. Replay needs only pull-request read access.

See [GitHub](/docs/guides/github.md) for channel setup and live delivery.

### Test GitLab webhooks {#gitlab}

```text
agent-sdk gitlab replay <mr-url-or-group/project!N>
  [--events a,b,c | '*'] [--action <action>]
  [--conclusion <status>] [--comment <body>]
  [--dir <path>] [--slug <slug>] [--channel <id>]
  [--url <url>] [--host <host>] [--port <n>]
  [--secret <secret>] [--dry-run] [--fixtures-dir <dir>] [--json]
agent-sdk gitlab events [--dir <path>] [--host <host>] [--port <n>] [--json]
agent-sdk gitlab forward [--dir <path>] [--host <host>] [--port <n>]
```

| Subcommand | Contract |
| --- | --- |
| `replay` | Read a merge request, synthesize selected webhook payloads, and post them to matching channels. The default event is `merge_request`; `'*'` selects the channel's declared events. |
| `events` | List discovered GitLab channel URLs and object kinds. |
| `forward` | Print the public-hook and tunnel recipe for live deliveries. |

Replay supports `merge_request`, `note`, `pipeline`, and `push`. It requires
`GITLAB_TOKEN` with project read access. `--secret` defaults to
`GITLAB_WEBHOOK_SECRET`; `--dry-run` and `--fixtures-dir` follow the GitHub replay
contract.

See [GitLab](/docs/guides/gitlab.md) for channel setup and self-managed hosts.

### Test Bitbucket webhooks {#bitbucket}

```text
agent-sdk bitbucket replay <pr-url-or-workspace/repo#N>
  [--events a,b,c | '*'] [--comment <body>]
  [--dir <path>] [--slug <slug>] [--channel <id>]
  [--url <url>] [--host <host>] [--port <n>]
  [--secret <secret>] [--dry-run] [--fixtures-dir <dir>] [--json]
agent-sdk bitbucket events [--dir <path>] [--host <host>] [--port <n>] [--json]
agent-sdk bitbucket forward [--dir <path>] [--host <host>] [--port <n>]
```

| Subcommand | Contract |
| --- | --- |
| `replay` | Read a pull request, synthesize webhook payloads in the host's Cloud or Data Center format, and post them to matching channels. |
| `events` | List discovered Bitbucket channel URLs and event keys. |
| `forward` | Print the repository-hook and tunnel recipe for live deliveries. |

Replay supports `pullrequest:created`, `pullrequest:updated`,
`pullrequest:comment_created`, and `repo:push`, along with their supported Data
Center forms. It requires `BITBUCKET_TOKEN` with pull-request read access.
`--secret` defaults to `BITBUCKET_WEBHOOK_SECRET`; `--dry-run` and `--out`
follow the GitHub replay contract.

See [Bitbucket](/docs/guides/bitbucket.md) for Cloud and Data Center setup.

### Test PagerDuty webhooks {#pagerduty}

```text
agent-sdk pagerduty replay [incident-id]
  [--events a,b,c | '*'] [--comment <body>]
  [--dir <path>] [--slug <slug>] [--channel <id>]
  [--url <url>] [--host <host>] [--port <n>]
  [--secret <secret>] [--dry-run] [--fixtures-dir <dir>] [--json]
agent-sdk pagerduty events [--dir <path>] [--host <host>] [--port <n>] [--json]
```

| Subcommand | Contract |
| --- | --- |
| `replay` | Synthesize v3 webhook bodies for one incident and post them, in order, to matching channels. The default event is `incident.triggered`; `'*'` selects every declared event replay supports. |
| `events` | List discovered PagerDuty channel URLs and event types. |

Replay supports `incident.triggered`, `.acknowledged`, `.annotated`,
`.escalated`, `.resolved`, and `.reopened`, by full name or by action alone
(`resolved`). It needs no PagerDuty account. The incident id defaults to
`PREPLAY1`, and `--comment` is the note body for `annotated`. `--secret`
defaults to `PAGERDUTY_WEBHOOK_SECRET`, signing with its first line when it
lists several; without either the deliveries are unsigned, which `agent-sdk dev`
admits from loopback. `--dry-run` and `--out`
follow the GitHub replay contract.

See [PagerDuty](/docs/guides/pagerduty.md) for channel setup.

## Environment variables

Credential resolution follows this order: `--api-key`, `CURSOR_API_KEY`,
`CURSOR_API_KEY_FILE`, `CURSOR_SERVICE_ACCOUNT_KEY`, then the stored login.

| Variable | Contract |
| --- | --- |
| `CURSOR_API_KEY` | Cursor credential. |
| `CURSOR_API_KEY_FILE` | Path to a Cursor credential file. |
| `CURSOR_SERVICE_ACCOUNT_KEY` | Team service-account credential used after the API key and key-file sources. |
| `CURSOR_API_BASE_URL` | Backend for login, account, deployment, and event commands. |
| `CURSOR_BACKEND_URL` | Backend for Agent SDK turns. Set it with `CURSOR_API_BASE_URL` when using a non-default backend. |
| `AGENT_SERVE_CONFIG_DIR` | Override the CLI config directory for stored credentials and update state. |
| `AGENT_SERVE_NO_UPDATE_CHECK`, `NO_UPDATE_NOTIFIER`, `CI` | Disable published-version checks when set to a non-empty value other than `0`. |
| `GITHUB_WEBHOOK_SECRET` | Default signature secret for GitHub forwarding and replay. |
| `GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_APP_INSTALLATION_ID` | GitHub App credentials for outbound API calls. |
| `GITHUB_TOKEN`, `GH_TOKEN` | Token credentials for outbound GitHub calls. Unset both for `github forward`. |
| `GITLAB_TOKEN` | Token for GitLab API reads and direct channel calls. |
| `GITLAB_API_BASE_URL` | Override the GitLab REST base URL for self-managed hosts. |
| `GITLAB_WEBHOOK_SECRET` | Default GitLab webhook token for replay and channel verification. |
| `BITBUCKET_TOKEN` | Token for Bitbucket API reads and direct channel calls. |
| `BITBUCKET_API_BASE_URL` | Override the Bitbucket Data Center REST base URL. |
| `BITBUCKET_WEBHOOK_SECRET` | Default Bitbucket signing secret for replay and channel verification. |
| `SLACK_BOT_TOKEN`, `SLACK_APP_TOKEN` | Slack tokens for one agent. Multi-agent servers use `<PREFIX>_SLACK_BOT_TOKEN` and `<PREFIX>_SLACK_APP_TOKEN`. |

## Exit status

Every command uses one exit code table. `agent-sdk help` prints it, and
`agent-sdk help --json` includes it as `exitCodes`.

| Code | Meaning |
| --- | --- |
| `0` | ok |
| `1` | failed (also `aborted`, `invalid_argument`, `rate_limited`, `unavailable`) |
| `2` | usage: bad command, flag, or argument |
| `3` | pending: the work is still running |
| `4` | `not_found` |
| `5` | `unauthenticated` or `permission_denied` |
| `6` | `conflict` or `failed_precondition` |

| Command | Nonzero contract |
| --- | --- |
| No command | Print the command index and exit `0`. An unknown command exits `2`. |
| `run` | For local and running-server trajectories, exit `1` when the trajectory fails. |
| `tool call` | Exit `1` unless the response succeeds with `ok: true`. |
| `session get --file` | Exit `1` when the reconstructed trajectory failed. |
| `session create --wait` | Exit `3` when the delivery names no session before `--timeout-ms`, and `1` when it was skipped or failed. |
| `eval` | Exit `1` for failed cases, or scored cases below threshold with `--strict`; exit `2` for invalid eval options or an execution with no matching cases. An empty `--list` succeeds. |
| `eval get` | Exit `3` while the batch runs, `1` when it failed or was cancelled, and `2` for invalid usage. |
| `chat`, `session get` | Exit `2` for documented option conflicts or missing required input. |
| `validate` | Exit `1` when any project diagnostic has error severity. |
| Commands that cannot be undone | Exit `2` without `--yes` when there is no terminal to ask on. Exit `1` (`aborted`) when the typed confirmation does not match. |
| Any command with `--dry-run` | Exit `0` after printing what it would do, unless a check before the first change fails. |
| `init --from automation:…` | Exit `1` for invalid input, authentication, export, or file-write failure. Warnings and dependency-install failure don't change a successful conversion exit. |
| `migrate` | Exit `0` when nothing needs a migration, after starting with `--no-wait`, or after the agent opens a pull request. Exit `1` for invalid input, authentication or agent failure, or a completed run without a pull request. |
| Other commands | Exit nonzero when validation, authentication, a request, or the requested operation fails. |

## Related

- [Project layout](/docs/reference/project-layout.md)
- [Sessions, events, and streaming](/docs/reference/sessions.md)
- [Evals](/docs/reference/evals.md)
- [HTTP API](/docs/reference/http-api.md)
- [Deployment](/docs/deployment.md)
- [Early-alpha migration](/docs/guides/migrate.md)
