Skip to main content

phoenix-cli

Debug LLM applications using the Phoenix CLI (`px`, from @arizeai/phoenix-cli). Fetch traces, spans, and sessions, annotate them, analyze errors, measure how often a failure or pattern occurs across traces, inspect datasets, review experiments, query annotation configs, and use the GraphQL API. Use whenever the user works with a Phoenix instance from the terminal, or wants to measure or investigate something in Phoenix with the CLI.

Source facts

Repository
Arize-ai/phoenix
Last source activity
October 15, 2026 at 20:57
Detected SKILL.md language
English
Stars
11,868
Forks
1,208

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
phoenix-cli
description
Debug LLM applications using the Phoenix CLI (`px`, from @arizeai/phoenix-cli). Fetch traces, spans, and sessions, annotate them, analyze errors, measure how often a failure or pattern occurs across traces, inspect datasets, review experiments, query annotation configs, and use the GraphQL API. Use whenever the user works with a Phoenix instance from the terminal, or wants to measure or investigate something in Phoenix with the CLI.
license
Apache-2.0
compatibility
Requires Node.js (for npx) or global install of @arizeai/phoenix-cli. Optionally requires jq for JSON processing.
metadata
{"author":"arize-ai","version":"3.5.0"}
# Phoenix CLI ## Invocation ```bash px <resource> <action> # if installed globally npx @arizeai/phoenix-cli <resource> <action> # no install required ``` The CLI uses singular resource commands with subcommands like `list` and `get`: ```bash px trace list px trace get <trace-id> px trace annotate <trace-id> px trace add-note <trace-id> px trace delete <trace-identifier> px trace-annotations delete px span list px span annotate <span-id> px span add-note <span-id> px span delete <span-identifier> px span-annotations delete px session list px session get <session-id> px session annotate <session-id> px session add-note <session-id> px session delete <session-id> px session-annotations delete px dataset list px dataset get <name> px dataset delete <dataset-identifier> px experiment list px experiment get <id> px experiment delete <experiment-id> px prompt list px prompt get <prompt-identifier> px prompt delete <prompt-identifier> px project list px project get <name> px project delete <project-identifier> px annotation-config list px annotation-config get <identifier> px annotation-config create px annotation-config update <identifier> px annotation-config delete <id> px auth login px auth logout px auth status px profile list px profile show [name] px profile create <name> px profile use <name> px profile edit <name> px profile delete <name> px api graphql <query> px docs fetch px setup px self update ``` Every `delete` above is gated: it requires `PHOENIX_CLI_DANGEROUSLY_ENABLE_DELETES=true` in the environment and prompts for confirmation unless `-y`/`--yes` is passed (`px profile delete` is local-only and takes `--yes` without the env gate). Without the env var the command exits without deleting anything. ## Setup ```bash export PHOENIX_ENDPOINT=http://localhost:6006 export PHOENIX_PROJECT=my-project export PHOENIX_API_KEY=your-api-key # if auth is enabled ``` `PHOENIX_ENDPOINT` is the base URL for API access. It usually holds the same URL as `PHOENIX_COLLECTOR_ENDPOINT`; when only the collector variable is set, the CLI uses it for API access too. For interactive local use, `px auth login` stores an OAuth session in the selected profile; the session acts with the permissions of the user who logged in. API keys take precedence over OAuth tokens when both are configured. OAuth access tokens are refreshed automatically for REST, GraphQL, and PXI requests, and rotated tokens are persisted to the selected profile. Always use `--format raw --no-progress` when piping to `jq`. ### `px setup` — onboarding `px setup` connects the app in the current directory to a Phoenix deployment and writes `.env.phoenix` (mode 0600, gitignored). The interactive flow is for humans — it prompts, launches coding agents, and polls for traces. **From an agent, always pass `--no-input`:** ```bash # Register only: connection + .env.phoenix, no source changes. px setup --no-input --endpoint http://localhost:6006 --project my-app --format raw ``` Headless requires a clean git repo and, by default, stops after writing the files — it will not touch source unless you ask. If auth is enabled, also set `PHOENIX_API_KEY`. The project doesn't need to exist — Phoenix creates it on first trace. Missing inputs exit `3` with exact remediation; cancel exits `2`. To also instrument the app, name the lane — headless has no prompt to pick one from, so `--instrument` requires `--agent`: ```bash px setup --no-input --instrument --agent claude --yolo --format raw ``` `--yolo` matters: a background agent has no terminal to approve its edits on, so without it the run stalls until trace verification times out. `--language python` skips the agent's language detection. `--docs-mcp` connects the Phoenix docs MCP server to the hand-off agent (`claude mcp add` for claude, config-file merge for cursor/opencode; codex unsupported) and skips the `.px/docs` download — the agent searches docs on demand instead; any failure falls back to the download. `--no-docs-mcp` suppresses the interactive offer. `--format raw` prints `{"endpoint","project","files","instrumentation","tracesVerified","tracesUrl"}` — check `tracesVerified`, which is set only when the API confirmed a trace arriving, not when the agent claims it finished. A run whose wait ran out with no trace exits `6`, not `0`: the configuration and edits are real, but tracing is not confirmed working. Treat that as a failure to report, not a success — and do not substitute the hand-off agent's own exit code or summary for the verdict. Registering without `--instrument`, and a human answering "verify later" at the timeout prompt, both exit `0`. `tracesVerified` is `false` for a registration-only run too, so it alone can't tell "nothing to verify" from "no trace arrived". Read `verification` (`verified` / `notVerified` / `deferred`, absent when there was nothing to verify) when you need the difference. Re-runnable slices, so an already-registered repo skips the questions: ```bash px setup instrument --agent claude # instrument + verify only px setup skills # install the Phoenix coding-agent skills ``` ### `px setup mcp` — register the remote MCP server Wire the Phoenix remote MCP server (`<endpoint>/mcp`) into a coding agent so it can query Phoenix data. The endpoint is inferred from `--endpoint`, the active profile, or `PHOENIX_ENDPOINT`. Bare command prompts for scope (global default) then agent; `--agent` skips both prompts. ```bash px setup mcp --agent codex --no-input --format raw px setup mcp --agent claude --local # write this repo's .mcp.json ``` Agents: `claude`, `codex`, `gemini`, `cursor`, `opencode`, `vscode`. Scope is `--global` (default) or `--local` (repo; Codex is global-only). Auth is OAuth by default (URL-only config, browser login on first use); pass `--header "Name: value"` (repeatable) for an API-key bearer fallback — for Codex a `Authorization: Bearer ${VAR}` header becomes `bearer_token_env_var`. `--format raw` prints `{"endpoint","url","serverName","agent","scope","auth","file?"}`. ## Auth ```bash px auth login # browser-based OAuth login px auth login --no-browser # print URL for SSH/headless use px auth logout # clear OAuth tokens; leaves API keys px auth status # check connection and authentication px auth status --endpoint http://other:6006 # check a specific endpoint px auth status --profile staging # check a named profile's connection px auth status --format raw # machine-readable credential source ``` `auth status` reports the credential source (`flag`, `env`, `profile-key`, `oauth`, or `none`). OAuth status includes the token expiry. When the stored credential source is `oauth` and the authenticated probe fails, `auth status` retries once without credentials and reports anonymous access only if the server explicitly says access is anonymous. This keeps a stale or expired profile token from being reported as an auth failure against a deployment that has since switched from OAuth to anonymous access. ## Profiles Named profiles let you switch between multiple Phoenix instances (local, staging, cloud) without juggling environment variables. Profiles are stored in `~/.px/settings.json` (or `$XDG_CONFIG_HOME/px/settings.json`). Configuration priority (highest to lowest): CLI flags > env vars > active profile > nearest `.env.phoenix` file > built-in defaults. The CLI also discovers the nearest `.env.phoenix` file at or above the current working directory (the same file `px setup` writes). Credentials are resolved as one group, so a process API key is never combined with file-provided headers. Set `PHOENIX_DISCOVER_CONFIG=false` to disable discovery. ```bash px profile list # list all profiles (shows active profile) px profile show # show the active profile's settings px profile show staging # show a named profile's settings px profile create prod --endpoint https://app.phoenix.arize.com --api-key <key> --activate px profile create local --endpoint http://localhost:6006 --project my-app px profile use prod # switch the active profile px profile edit prod # open profile JSON in $EDITOR (validates on save) px profile delete prod --yes # delete a profile (--yes skips confirmation) ``` Use `--profile <name>` on any command to target a specific profile without changing the active one: ```bash px trace list --profile staging --limit 10 --format raw --no-progress | jq . px auth status --profile prod ``` `px profile create` options: `--endpoint <url>`, `--project <name>`, `--api-key <key>`, `--header <key=value>` (repeatable), `--activate`. ## Projects ```bash px project list # list all projects (table view) px project list --format raw --no-progress | jq '.[].name' # project names as JSON px project list --name-contains prod # filter by name substring (case-insensitive) px project get my-project --format raw --no-progress # single record by exact name px project get my-project --format raw --no-progress | jq -r '.id' # extract project id ``` `project list` accepts `--limit <n>` (projects fetched per page) and `--name-contains <filter>`, which filters server-side on a case-insensitive name substring. Use it instead of piping `list` through `grep` when you only know part of a project's name. `project get` exits with `ExitCode.FAILURE` (1) on a name miss and writes a `StructuredError` `{error, code: "FAILURE", hint}` to stderr in `--format json|raw`. ## Traces ```bash px trace list --limit 20 --format raw --no-progress | jq . px trace list --last-n-minutes 60 --limit 20 --format raw --no-progress | jq '.[] | select(.status == "ERROR")' px trace list --since 2025-01-15T00:00:00Z --limit 50 --format raw --no-progress | jq . px trace list --since 2025-01-15T00:00:00Z --until 2025-01-16T00:00:00Z --limit 50 --format raw --no-progress | jq . # time range (until is exclusive) px trace list --format raw --no-progress | jq 'sort_by(-.duration) | .[0:5]' px trace list --include-notes --format raw --no-progress | jq '.[].notes' px trace get <trace-id> --format raw | jq . px trace get <trace-id> --format raw | jq '.spans[] | select(.status_code != "OK")' px trace get <trace-id> --include-notes --format raw | jq '.notes' px trace annotate <trace-id> --name reviewer --label pass px trace annotate <trace-id> --name reviewer --score 0.9 --format raw --no-progress px trace annotate <trace-id> --name reviewer --label pass --identifier "<coding-annotation-id>" # tag with a coding annotation identifier px trace add-note <trace-id> --text "needs follow-up" px trace add-note <trace-id> --text "needs follow-up" --identifier "<coding-annotation-id>" # tag + upsert on identifier px trace-annotations delete --identifier "<coding-annotation-id>" --all -y # nuke every annotation tied to this coding annotation identifier ``` `px <entity>-annotations delete` requires `--all` or both `--start-time` and `--end-time` and emits `{deleted: true, target, filter}` on success. ### Trace JSON shape ``` Trace traceId, status ("OK"|"ERROR"), duration (ms), startTime, endTime annotations[] (with --include-annotations, excludes note) name, result { score, label, explanation } notes[] (with --include-notes) name="note", result { explanation } rootSpan — top-level span (parent_id: null) spans[] name, span_kind ("LLM"|"CHAIN"|"TOOL"|"RETRIEVER"|"EMBEDDING"|"AGENT"|"RERANKER"|"GUARDRAIL"|"EVALUATOR"|"DECISION"|"UNKNOWN") status_code ("OK"|"ERROR"|"UNSET"), parent_id, context.span_id notes[] (with --include-notes) name="note", result { explanation } attributes input.value, output.value — raw input/output llm.model_name, llm.provider llm.token_count.prompt/completion/total llm.token_count.prompt_details.cache_read llm.token_count.completion_details.reasoning llm.input_messages.{N}.message.role/content llm.output_messages.{N}.message.role/content llm.invocation_parameters — JSON string (temperature, etc.) exception.message — set if span errored ``` ## Spans ```bash px span list --limit 20 # recent spans (table view) px span list --last-n-minutes 60 --limit 50 # spans from last hour px span list --since 2025-01-15T00:00:00Z --limit 50 # spans since a timestamp px span list --since 2025-01-15T00:00:00Z --until 2025-01-16T00:00:00Z --limit 50 # time range (until is exclusive) px span list --span-kind LLM --limit 10 # only LLM spans px span list --status-code ERROR --limit 20 # only errored spans px span list --name chat_completion --limit 10 # filter by span name px span list --trace-id <id> --format raw --no-progress | jq . # all spans for a trace px span list --span-id <id> <id> --format raw --no-progress | jq . # fetch specific spans by ID (server >= 19.6.0) px span list --parent-id null --limit 10 # only root spans px span list --parent-id <span-id> --limit 10 # only children of a span px span list --include-annotations --limit 10 # include annotation scores px span list --include-notes --limit 10 # include span notes px span list --attribute llm.model_name:gpt-4 --limit 10 # filter by string attribute px span list --attribute llm.token_count.total:500 --limit 10 # filter by numeric attribute px span list --attribute 'user.id:"12345"' --limit 10 # force string match for numeric-looking value px span list --attribute session.id:sess:abc:123 --limit 20 # colon in value OK (split on first colon only) px span list --attribute llm.model_name:gpt-4 --attribute session.id:abc --limit 10 # AND multiple filters px span list output.json --limit 100 # save to JSON file px span list --format raw --no-progress | jq '.[] | select(.status_code == "ERROR")' px span annotate <span-id> --name reviewer --label pass px span annotate <span-id> --name checker --score 1 --annotator-kind CODE px span annotate <span-id> --name reviewer --label pass --identifier "<coding-annotation-id>" # tag with a coding annotation identifier px span add-note <span-id> --text "verified by agent" px span add-note <span-id> --text "verified by agent" --identifier "<coding-annotation-id>" # tag + upsert on identifier px span-annotations delete --identifier "<coding-annotation-id>" --all -y # nuke every annotation tied to this coding annotation identifier ``` `span list` orders by ingestion (newest first), not `start_time`; they diverge for late-arriving spans (backfills, replays). To sort by `start_time` (server >= 20.16.0), call REST and keep `sort`/`order` fixed across `next_cursor` pages: ```bash curl -s -H "Authorization: Bearer $PHOENIX_API_KEY" \ "$PHOENIX_ENDPOINT/v1/projects/my-project/spans?sort=start_time&order=desc&limit=20" ``` ### Span JSON shape ``` Span
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub