@cantinasecurity/apex-cli
v0.1.17
Published
Standalone CLI and MCP server for Apex.
Downloads
849
Readme
Apex CLI
Standalone CLI client for Apex.
Installing And Updating
For a public install, use a global package manager install:
npm install -g @cantinasecurity/apex-cli
# or: pnpm add -g @cantinasecurity/apex-cliThen run:
apex setupapex setup is the lowest-friction path for agent clients. It:
- registers Apex as an MCP server in any installed Codex CLI, Claude Code, and GitHub Copilot CLI clients
- installs the Codex skill into
$CODEX_HOME/skills/apex-cli - installs the Claude project skill into
.claude/skills/apex-cliin the current repository - installs the GitHub Copilot CLI skill into
$COPILOT_HOME/skills/apex-clior~/.copilot/skills/apex-cli
If you only want one client, run:
apex setup codex
apex setup claude
apex setup copilotIf one client is not installed yet, apex setup skips it automatically. If you target a client explicitly, its CLI must already be installed.
Update a global install with:
apex updateYou can also update directly with your package manager:
npm install -g @cantinasecurity/apex-cli
# or: pnpm add -g @cantinasecurity/apex-cliIf you are running Apex CLI from a local checkout instead, update it with:
git pull --ff-only
pnpm installMCP registrations keep working across package updates because they point at the stable apex-mcp shim. Re-run apex setup after upgrading if you want to refresh copied skill files, and run apex setup claude in each repository where you want the Claude project skill.
When apex is run in an interactive terminal, it checks for updates periodically and offers to install them.
Local Development
- Install dependencies:
pnpm install- Run the CLI:
pnpm apexBy default, the CLI targets https://ai.cantina.xyz/.
Use APEX_BASE_URL only when targeting a non-default Apex host. For on-prem deployments,
set it to the same external HTTPS Bedrock origin configured as ONPREM_PUBLIC_BASE_URL;
browser handoffs such as apex connect github and apex-connect-provider need that
public origin for GitHub callback and success redirects.
APEX_BASE_URL=https://bedrock.customer.example pnpm apexInteractive Shell
Bare apex opens the interactive shell:
$ apex
Apex CLI
Connected to https://ai.cantina.xyz/
Type /scan to start a scan for this directory, /workspaces to browse workspace names, /workspace use "<name>" to switch, /help for commands.
apex>In interactive terminals, Apex now shows a loading indicator while it resolves workspaces, loads scans, and starts commands.
If Apex asks for a workspace name, that is the Apex workspace name for the current directory. Press Enter to accept the current folder name, or pass --workspace-name <name> explicitly.
Supported shell commands:
/credits/scan [standard|audit|lite]/scan pr <pr-number>/scans/findings [scan-id]/findings comment <finding-id|finding-identifier> <comment>/findings feedback <finding-id|finding-identifier> valid [comment]/findings feedback <finding-id|finding-identifier> invalid <false-positive|by-design|not-relevant> [comment]/findings fix-review <finding-id|finding-identifier>/export [scan-id]/workspaces/cancel-scan [scan-id]/status [scan-id]/doctor/update/logout/repos/workspace/workspace use <workspace-name|workspace-prefix|workspace-id>/workspace name <name>/company [id|handle]/connect github/connect gitlab/open/clear/help/exit
/workspace use accepts a workspace name, prefix, or ID. Quote workspace names that contain spaces, for example /workspace use "Core Platform".
Scripted Commands
apex creditsapex scanapex scan --mode liteapex scan --mode pr --pr <number> [--pr <number>] [--pr-path <path>]apex scansapex findings [--scan <scan-id>]apex findings comment <finding-id|finding-identifier> --content <markdown> [--parent-comment <comment-id>] [--scan <scan-id>]apex findings feedback <finding-id|finding-identifier> <valid|invalid> [comment] [--comment <markdown>] [--scan <scan-id>] [--suggested-severity extreme|critical|high|medium|low|informational] [--dismissal-reason false-positive|by-design|not-relevant] [--label acknowledged|fixed] [--fix-pr-url <github-pr-url>]apex findings fix-review <finding-id|finding-identifier> [--scan <scan-id>] [--review-target pull-request|repository-current] [--fix-pr-url <github-pr-url>]apex export findings [--scan <scan-id>] [--format markdown|json|gitlab-sast] [--output <path>]apex workspacesapex workspaceapex workspace use <workspace-name|workspace-prefix|workspace-id>apex cancel-scan [scan-id]apex status [--scan <scan-id>]apex doctorapex loginapex logoutapex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>] [--expires-at <ISO-8601>] [--json]apex service-key list [--company <id-or-handle>] [--json]apex service-key revoke <key-id> [--company <id-or-handle>] [--json]apex setup [all|codex|claude|copilot]apex telemetry [status|enable|disable]apex updateapex connect githubapex connect gitlab
Helpful workspace flags:
--company <id-or-handle>to choose the Apex company when more than one is available--workspace-name <name>to set the Apex workspace name for this directory
apex credits shows standard, audit, fix review, and Lite scan entitlements when the server returns them.
Service Keys And The Hosted API
Use the hosted Apex API for HTTP-only, headless integrations such as serverless functions, cron jobs, and internal audit hubs. Use the CLI or stdio MCP server for local and interactive agent workflows. The full HTTP contract is in the Apex API documentation.
An organization manager can create a service key with an existing Apex device-login session. Service-key commands never start device login implicitly, so run apex login first if needed:
apex service-key create \
--name internal-audit-hub \
--scope scans:create,scans:read,findings:read \
--expires-at 2027-01-01T00:00:00ZThe CLI automatically uses your company when only one is available. If you belong to multiple companies, it prompts you to choose one. Pass --company <id-or-handle> to skip that prompt, or when a non-interactive or --json command cannot prompt.
The secret is returned only by create and cannot be retrieved later. Store it directly in a secret manager and do not put it in source control, prompts, chat messages, or logs. --json also includes the shown-once secret so it can be captured programmatically; handle that output as a credential.
Service keys are organization-level credentials. Grant only the scopes the integration needs: scans:create triggers scans, scans:read polls status, and findings:read retrieves findings. Omit --scope to grant all three scopes. For a P1-only adapter that reads manually started scans, create a read-only key with --scope scans:read,findings:read.
A key with findings:read can retrieve unpublished draft findings for scans in its organization so an external hub can perform triage. Give the key only to systems that are allowed to handle those drafts.
List metadata or revoke a key without exposing its secret:
apex service-key list
apex service-key revoke <key-id>Service-key management is intentionally available only through the scripted CLI. It is not exposed as an MCP tool or interactive-shell command, which keeps raw secrets out of model tool results and transcripts. The CLI requires an existing device-login bearer session, never initiates login from these commands, and never stores a created service key locally.
Use the service key as a bearer token with the hosted scan lifecycle:
curl -X POST https://ai.cantina.xyz/api/apex/v1/scans \
-H "Authorization: Bearer $APEX_SERVICE_KEY" \
-H "Idempotency-Key: $APEX_IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{
"repoUrl": "https://github.com/acme/example.git",
"commit": "0123456789abcdef0123456789abcdef01234567",
"paths": ["services/payments"]
}'
curl \
-H "Authorization: Bearer $APEX_SERVICE_KEY" \
https://ai.cantina.xyz/api/apex/v1/scans/$SCAN_ID
curl \
-H "Authorization: Bearer $APEX_SERVICE_KEY" \
https://ai.cantina.xyz/api/apex/v1/scans/$SCAN_ID/findingsIn v1, the GitHub repository must already be linked to an active Apex workspace. The hosted API never creates, binds, or mutates workspaces. Apex selects the workspace automatically when exactly one active workspace matches the repository; pass the optional workspaceId to disambiguate multiple matches. Zero matches, or multiple matches without a valid workspaceId, return HTTP 409.
The optional paths array is a set of best-effort focus hints, not a hard repository boundary. Apex may inspect or report code outside those paths. Version one does not yet provide strict path isolation.
Every trigger request requires an Idempotency-Key. Generate one stable key for each intended scan and reuse that same key with the identical request body when retrying after a transport error, timeout, or 5xx. Idempotency is organization-scoped, so a retry remains safe if the service key is rotated between attempts. A definitive 4xx response is retained; after correcting its precondition, submit the request with a new key. Use a new key for every genuinely new scan. The trigger returns a scanId; poll the scan resource until it reaches a terminal status, then fetch findings. The findings response uses the GitLab SAST JSON shape, including unpublished drafts, finding IDs that remain stable when the same scan is re-fetched, titles, descriptions, severities, and source locations when available.
Example prompt for Claude Code:
Build an Apex adapter for our serverless audit hub using the hosted HTTP API, not the Apex CLI or MCP server. Assume the GitHub repository is already linked to an active Apex workspace; this API must not create or mutate workspaces. Read
APEX_SERVICE_KEYfrom the environment, generate one stableIdempotency-Keyper intended scan, and reuse it with the identical request body only after a transport error, timeout, or5xx; after correcting a definitive4xx, use a new key. POST the repository URL and exact commit to/api/apex/v1/scans, includeworkspaceIdwhen needed to disambiguate matches, and handle a409workspace-resolution response. Treat optionalpathsas non-binding focus hints, not a security boundary. Poll/api/apex/v1/scans/{scanId}until terminal, then fetch/api/apex/v1/scans/{scanId}/findings. Preserve the raw GitLab SAST payload, including drafts, and deduplicate findings by stable ID. Never print or log the service key.
Example prompt for Codex:
Add Apex to this headless auditor integration using the hosted REST API. Use bearer auth from
APEX_SERVICE_KEY; do not shell out toapexor start MCP. The GitHub repository must already belong to an active Apex workspace, and the adapter must not create or mutate workspaces. Implement scan trigger,409workspace-resolution handling, status polling with bounded backoff, terminal failure handling, and findings retrieval. SendrepoUrl,commit, optionalpaths, and optionalworkspaceId; treatpathsas non-binding focus hints rather than a hard security boundary. Generate a stableIdempotency-Keyfor each intended scan. Preserve it only for an identical transport/timeout/5xxretry; after correcting a definitive4xx, submit with a new key. Keep the returnedscanId, and store the unmodified findings payload, including drafts, alongside normalized findings. Add tests with mocked HTTP responses and ensure credentials are redacted from errors and logs.
For a P1-only integration where scans are started in the Apex UI:
Given an Apex
scanId, fetch/api/apex/v1/scans/{scanId}/findingswithAPEX_SERVICE_KEY, save the GitLab SAST JSON unchanged, and import each finding once using its stable ID. Do not trigger a new scan and never log the bearer token.
Finding Review Feedback
Finding review collaboration now has explicit write commands:
apex findings comment <finding-id|finding-identifier> --content "Needs auth check"apex findings feedback <finding-id|finding-identifier> valid --comment "Reproduced on latest build"apex findings feedback <finding-id|finding-identifier> invalid --dismissal-reason false-positive --comment "This path is unreachable"apex findings feedback <finding-id|finding-identifier> valid --label fixed --fix-pr-url https://github.com/acme/app/pull/123 --comment "Fixed in PR #123"apex findings fix-review <finding-id|finding-identifier>/findings comment <finding-ref> <comment>/findings feedback <finding-ref> valid [comment]/findings feedback <finding-ref> invalid <false-positive|by-design|not-relevant> [comment]/findings fix-review <finding-ref>
Identifiers such as KERN2-25 are resolved against the selected or latest scan for the current workspace binding. Pass --scan <scan-id> when you need a specific scan, or pass the finding UUID directly to skip workspace-based resolution.
Finding comments, valid/invalid feedback, and fix review scan starts use the same Apex login credentials as read commands. In MCP clients, apex-auth-start followed by apex-auth-wait is enough to authenticate these write tools.
Invalid feedback requires a dismissal reason. Valid feedback can include --suggested-severity extreme|critical|high|medium|low|informational.
Fix review scans can review either a Fix PR or the current repository state:
- For PR-based reviews, either save fixed feedback on the finding with
--label fixedand one or more--fix-pr-urlvalues, or pass--fix-pr-urldirectly toapex findings fix-review. - For current-repository reviews, run
apex findings fix-review <finding-id|finding-identifier>without Fix PR URLs. Bedrock must have original scan repository context for the finding.
The matching MCP flow is apex-finding-fix-review with optional fixPrUrls. Agents that need to record fixed feedback first can still call apex-finding-feedback with status: "valid", labels: ["fixed"], and fixPrUrls, then call apex-finding-fix-review.
The matching CLI and MCP flows intentionally use the same device-login session so agents should not ask users to paste browser cookies or auth tokens.
Local Source Scans
apex scan now works against any local source root you point it at. By default, that source root is the current working directory:
- clean GitHub or GitLab checkouts can stay on the remote-materialization path
- dirty git worktrees fall back to a local snapshot upload by default
- plain directories that are not git repositories are scanned through a local snapshot upload
Useful flags:
--repo <path>to scan one or more explicit local roots instead of the current directory--source-mode auto|remote|localto control remote-first fallback behavior--mode standard|audit|lite|prto choose the scan mode--pr <number>to select one or more GitHub pull requests for--mode pr--pr <number:path,path>or--pr-path <path>to limit a PR scan to changed paths
auto is the default. remote requires Apex to materialize from a remote repository. local forces a local snapshot upload even when a clean remote path is available.
Audit scans use audit as the scan mode and still require provider-backed GitHub or GitLab repositories that Apex can materialize remotely without a local snapshot fallback. ultra remains accepted as a backwards-compatible alias.
Lite scans use lite as the scan mode and require the Lite Scan beta feature plus provider-backed GitHub or GitLab repositories that Apex can materialize remotely. Local snapshot uploads are not supported for Lite scans.
PR scans require exactly one provider-backed GitHub repository. If the current directory resolves to multiple sources, pass --repo <path> to select the one that contains the pull request.
LLM / MCP Usage
The CLI now ships an MCP server so LLM clients can drive Apex directly over stdio.
If Apex is installed globally, prefer:
apex setupThat registers Apex for installed Codex CLI, Claude Code, and GitHub Copilot CLI clients automatically. Codex and Claude registrations also set APEX_MCP_CLIENT and APEX_CLIENT_INTEGRATION so Apex can distinguish agent-driven MCP usage from direct CLI usage.
If you want to wire clients manually instead, Apex ships a stable apex-mcp binary. For Codex:
codex mcp add apex \
--env APEX_MCP_CLIENT=codex \
--env APEX_CLIENT_INTEGRATION=codex \
-- apex-mcpFor Claude Code:
claude mcp add --scope user \
-e APEX_MCP_CLIENT=claude \
-e APEX_CLIENT_INTEGRATION=claude \
-- apex apex-mcpFor GitHub Copilot CLI:
copilot mcp add apex --type stdio --tools "*" -- apex-mcpFor any other MCP client, configure it to launch:
{
"mcpServers": {
"apex": {
"command": "apex-mcp",
"env": {
"APEX_MCP_CLIENT": "custom-mcp-client",
"APEX_CLIENT_INTEGRATION": "custom-mcp-client"
}
}
}
}From a local checkout during development, prefer the repo-local binary so the MCP stream stays clean:
{
"mcpServers": {
"apex": {
"command": "/path/to/apex-cli/bin/apex-mcp",
"env": {
"APEX_MCP_CLIENT": "local-dev",
"APEX_CLIENT_INTEGRATION": "local-dev"
}
}
}
}If you need to launch through pnpm, use --silent:
{
"mcpServers": {
"apex": {
"command": "pnpm",
"args": ["--silent", "mcp"],
"cwd": "/path/to/apex-cli",
"env": {
"APEX_MCP_CLIENT": "local-dev",
"APEX_CLIENT_INTEGRATION": "local-dev"
}
}
}
}Do not point an MCP client at plain pnpm mcp. pnpm writes its script banner to stdout before the protocol stream, which can break the initialize handshake.
The MCP server exposes Apex-specific tools for:
- auth status and device login
- doctor, credits, and provider connection URLs
- workspace inspection and workspace binding
- scan start, status, cancellation, findings, and findings export
- Lite scan start by calling
apex-scanwithmode: "lite" - PR scan start by calling
apex-scanwithmode: "pr"andpullRequests - finding comments and valid/invalid feedback with
apex-finding-commentandapex-finding-feedback - Fix PR and current-repository fix review scans with
apex-finding-fix-review, optionally afterapex-finding-feedback
The MCP server does not expose service-key creation, listing, or revocation. For HTTP-only serverless and cron integrations, use the hosted Apex API directly instead of launching the stdio MCP server.
For repository-scoped operations, pass cwd explicitly so the server can resolve the right .apex/workspace.json binding and repository roots.
For Codex-style clients, the packaged skill can be installed with apex setup codex. For GitHub Copilot CLI, the same skill is installed into ~/.copilot/skills/apex-cli with apex setup copilot. The repo-local source lives at skills/apex-cli/SKILL.md.
For Claude Code, the packaged project skill can be installed into the current repository with apex setup claude. The repo-local source lives at .claude/skills/apex-cli/SKILL.md. Anthropic documents project skills as filesystem directories under .claude/skills/<name>/SKILL.md, and the Claude Agent SDK uses the same location when the Skill tool is enabled.
Usage Telemetry
Apex CLI emits first-party, privacy-preserving usage telemetry so Cantina can understand how people use the direct CLI, the interactive shell, and MCP tools in Codex, Claude, GitHub Copilot CLI, or other clients.
Telemetry is enabled by default and can be disabled locally:
apex telemetry status
apex telemetry disable
apex telemetry enableEnvironment opt-outs override local config:
APEX_TELEMETRY_DISABLED=1 apex scan
# also honored: APEX_DISABLE_TELEMETRY=1 or DO_NOT_TRACK=1Telemetry records lifecycle events such as command/tool start and completion, duration, success/failure category, CLI version, Node/platform basics, anonymous install/session IDs, and sanitized command metadata. It also adds attribution headers to Apex API requests, including surface (cli, interactive_shell, or mcp), client integration, invocation ID, command/tool name, and CLI version.
Telemetry does not send raw cwd paths, repository URLs, finding IDs, scan IDs, PR URLs, comments, file paths, tokens, or raw flag values. Sensitive inputs are reduced to booleans, counts, enum values, or length buckets.
Telemetry event posts do not include bearer tokens. The telemetry endpoint is POST /api/cli/v1/telemetry/events with a batch payload:
{
"events": [
{
"schemaVersion": 1,
"event": "apex.invocation.completed",
"cliVersion": "0.1.17",
"invocation": {
"surface": "mcp",
"command": "apex-scan",
"mcpTool": "apex-scan",
"metadata": {
"mode": "pr",
"pullRequestCount": 1,
"cwdProvided": true
}
},
"client": {
"integration": "codex"
},
"outcome": {
"success": true,
"durationMs": 1234
}
}
]
}Plugin And Marketplace Packaging
The npm package also includes marketplace-ready plugin artifacts:
.codex-plugin/plugin.jsonand.mcp.codex.jsonfor Codex plugin installs.claude-plugin/plugin.jsonand.mcp.claude.jsonfor Claude Code plugin installs.claude-plugin/marketplace.jsonfor a Claude marketplace entry backed by the public npm package
These plugin installs launch the pinned npm package with npx -y -p @cantinasecurity/[email protected] apex-mcp, so users do not need to install apex globally before enabling the plugin.
The repository also includes .agents/plugins/marketplace.json for local Codex marketplace testing from a checkout.
For local Claude validation:
claude plugin validate .
claude plugin marketplace add ./ --scope local
claude plugin install apex-cli@cantina-tools --scope localFor local Codex validation, add this repo as a local marketplace, then install apex-cli from the Cantina Tools marketplace in the Codex plugin directory:
codex plugin marketplace add ./See MARKETPLACE.md for the official Claude, Anthropic Connectors Directory, and OpenAI/Codex submission checklist. The local stdio MCP server is plugin-ready, but remote marketplace submissions still require the external review steps documented there.
Development Notes
The CLI uses the Apex /api/cli/v2/** local-source routes for scan planning and snapshot uploads, with legacy /api/cli/v1/** routes still used for provider-backed flows such as audit scans. Local state is stored under:
~/.config/apex/config.json~/.config/apex/credentials.json.apex/workspace.json
If a scan is already running in the current workspace, apex scan and /scan now require confirmation before starting another one. Scripted usage can opt in explicitly with --force. Active-looking workspace scan rows are checked against the scan progress endpoint before the CLI treats them as blockers, so stale list entries do not hide terminal states such as cancelled.
To move between existing Apex workspaces from the CLI:
- Run
apex workspacesto list the workspaces available to your active company. - Run
apex workspace use <workspace-name|workspace-prefix|workspace-id>to bind the current directory. - If the workspace name contains spaces, quote it, for example
apex workspace use "Core Platform". - Use
apex scans,apex status --scan <scan-id>,apex findings, andapex export findingsagainst that binding.
