Full-featured command-line interface for the Seclai platform. Manage agents, knowledge bases, sources, memory banks, evaluations, solutions, governance, and more — all from the terminal.
All commands return JSON to stdout, so you can pipe into jq or other tools.
npm i -g @seclai/cliOr run directly via npx (no install needed):
npx @seclai/cli agents listIf you are using an AI coding agent like Claude Code, you can also install the skill individually with:
seclai skills installThis installs skills into all detected coding editors by default. Use --tool <tool> to scope it to one editor.
To install the Seclai MCP server into your editors (Cursor, Claude Code, VS Code, etc.):
seclai mcp configure --key "$SECLAI_API_KEY"Or directly via npx:
npx skills add seclai/seclai-cli --full-depth --global --all
npx add-mcp https://api.seclai.com/mcp --header "X-API-Key: $SECLAI_API_KEY" --name SeclaiCommand reference (latest): https://seclai.github.io/seclai-cli/latest/
The CLI supports two authentication methods:
Set the SECLAI_API_KEY environment variable, or pass --api-key per-command:
export SECLAI_API_KEY="sk-..."
# or inline
seclai --api-key "$SECLAI_API_KEY" agents listSSO works out of the box with built-in production defaults — no configuration needed:
# Authenticate via browser (Authorization Code + PKCE)
seclai auth login
# Check authentication status
seclai auth status
# Refresh tokens manually
seclai auth refresh
# Log out (clears cached tokens)
seclai auth logoutFor custom SSO settings (e.g. staging environment), use seclai configure sso
or set environment variables:
| Variable | Description | Default |
|---|---|---|
SECLAI_SSO_DOMAIN |
Cognito domain | auth.seclai.com |
SECLAI_SSO_CLIENT_ID |
Cognito app client ID | 4bgf8v9qmc5puivbaqon9n5lmr |
SECLAI_SSO_REGION |
AWS region | us-west-2 |
Set profiles up interactively, and list the ones you have:
# Prompts for domain, client ID, region, and account ID
seclai configure sso [--profile-name <name>]
# Show every configured profile
seclai configure listUse a named profile with --profile:
seclai --profile staging agents listTokens are cached in ~/.seclai/sso/cache/ and auto-refreshed when expired.
| Variable | Description |
|---|---|
SECLAI_API_KEY |
Default API key (alternative to --api-key) |
SECLAI_API_URL |
Override API base URL (default: https://api.seclai.com) |
SECLAI_PROFILE |
Default SSO profile name (default: default) |
SECLAI_CONFIG_DIR |
Config directory path (default: ~/.seclai) |
SECLAI_API_VERSION |
Dated API version (alternative to --api-version) |
SECLAI_SSO_DOMAIN |
Override SSO domain (default: auth.seclai.com) |
SECLAI_SSO_CLIENT_ID |
Override SSO client ID (default: 4bgf8v9qmc5puivbaqon9n5lmr) |
SECLAI_SSO_REGION |
Override SSO region (default: us-west-2) |
| Flag | Description |
|---|---|
--api-key <key> |
Seclai API key |
--profile <name> |
SSO profile name |
--account-id <id> |
Account ID (X-Account-Id header) |
--config-dir <path> |
Config directory path |
--api-version <date> |
Opt into dated API changes released on or before this YYYY-MM-DD |
--allow-unknown-api-version |
Send an --api-version this CLI was not built against |
--compact |
Output compact (single-line) JSON |
-V, --version |
Print version |
The API is versioned by date. Responses can change shape between versions — a
bare array becoming a {data, pagination} envelope, for instance — so the CLI
sends no version header by default. Upgrading the CLI on its own never
changes what a command prints.
# See which version a request resolves to
seclai api-version get
# Opt one invocation into the changes released up to a date
seclai --api-version 2026-07-27 alerts list
# Or for every client on the account, not just this CLI
seclai api-version set 2026-07-27
seclai api-version clear # revert to the defaultAn --api-version this CLI was not built against is rejected, because a newer
version can reshape a response the CLI would then misread. Pass
--allow-unknown-api-version to send it anyway. api-version set is held to
the same standard — it rejects a version this release was not built against,
with the same override — because the pin applies to every client on the account
and nothing re-checks it afterwards.
Each version includes the changes of the ones before it. seclai api-version --help
prints the same list.
| Version | What it changes |
|---|---|
2026-07-01 |
The baseline, applied when no version is sent and the account is not pinned |
2026-07-27 |
List responses move to {data, pagination}, and a query parameter the endpoint does not declare is rejected with a 422 |
2026-08-03 |
memory create and memory update reject a non-zero max_age_days, which reads as null; an omitted retention_days on create resolves per bank type |
2026-08-21 |
sources create rejects an embedding dimension its embedder does not support — models embedders reports the supported ones |
2026-09-28 |
Agent-definition writes such as agents def update use the current file-list grammar for a step's attachments |
2026-09-30 |
A run's and a step's output, and a step's input, are the text rather than a JSON manifest. Files are in attachments on every version |
2026-10-03 |
A new LLM step written without attachments takes its parent's files |
2026-09-30 changes what agents run and agents runs get print: a script
that parses .output as JSON to find a run's files should read .attachments
instead, which is present whichever version is in effect.
--api-key, --profile, --account-id and --config-dir reject an empty
value. A shell expanding an unset variable passes "", which the SDK discards,
so --api-key "$KEY" with KEY unset would fall back to SECLAI_API_KEY or a
cached SSO session and run as a different identity — and --account-id "" would
act on the default org rather than the one you named. Guard the flag instead of
the value:
seclai ${KEY:+--api-key "$KEY"} agents listAn empty --api-version is still accepted, with a warning: it costs only the
version header. A future release will reject that too.
seclai agents list [--page N] [--limit N]
seclai agents create --json '{"name":"My Agent"}'
seclai agents get <agentId>
seclai agents update <agentId> --json '{"name":"Renamed"}'
seclai agents delete <agentId>
# Pause an agent across every trigger path, then resume it
seclai agents disable <agentId>
seclai agents enable <agentId>
# Which live agents call this one via a call_agent step?
seclai agents callers <agentId># Alias, sender allowlist, and inbound-handling flags for an EMAIL_RECEIVED trigger
seclai agents triggers email-config <agentId> <triggerId> --json '{"alias":"support"}'Four modes: basic, streaming (SSE wait), events (NDJSON), and polling.
# Basic run (returns final result)
seclai agents run <agentId> --json '{"input":"Hello"}'
seclai agents run <agentId> --json-file ./run.json
cat run.json | seclai agents run <agentId> --json-file -
# SSE streaming (waits for done event or timeout)
seclai agents run <agentId> --json '{"input":"Hi"}' --stream --timeout-ms 60000
# NDJSON event stream (outputs every SSE event as a JSON line)
seclai agents run <agentId> --json '{"input":"Hi"}' --events
seclai agents run <agentId> --json '{"input":"Hi"}' --events --event-filter status
seclai agents run <agentId> --json '{"input":"Hi"}' --events --output data
# Polling (submit then poll until complete)
seclai agents run <agentId> --json '{"input":"Hi"}' --poll --poll-interval-ms 2000seclai agents runs list <agentId> [--page N] [--limit N]
seclai agents runs get <runId> [--include-step-outputs]
seclai agents runs cancel <runId>
seclai agents runs delete <runId> # deprecated alias for `runs cancel`
seclai agents runs search [--page N] [--limit N] [--json '...']
seclai agents runs eval-results <agentId> <runId> [--page N] [--limit N] [--paged]
# Download a file attachment emitted by a run step. attachmentId is the
# URL-safe-base64 storage_key from run output manifests / webhooks.
seclai agents runs download-attachment <runId> <attachmentId> [--download-name <name>] [--output <path>]seclai agents def get <agentId>
seclai agents def update <agentId> --json '{"steps":[...]}'seclai agents export <agentId>
seclai agents export <agentId> --no-download
# Validate an agent_definition payload without creating an agent.
# Reports counts and any unresolved_refs you'll need to map with entity_remap
# when calling `agents create` or `agents update`.
# The body shape is `{ "agent_definition": <export payload> }`.
seclai agents preview-import --json-file ./preview-body.json
seclai agents export <agentId> \
| jq '{agent_definition: .}' \
| seclai agents preview-import --json-file -# Discover which files (if any) the agent expects before staging uploads.
seclai agents attachment-references <agentId>
seclai agents upload-input <agentId> --file ./data.csv [--file-name data.csv] [--mime-type text/csv]
seclai agents input-status <agentId> <uploadId>seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot"
seclai agents ai step-config <agentId> --user-input "Configure the search step"
# --step-type is required: the API rejects the request without it.
seclai agents ai history <agentId> --step-type llm [--step-id <id>] [--limit N] [--offset N]
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'seclai sources list [--page N] [--limit N] [--sort <field>] [--order asc|desc]
seclai sources create --json '{"name":"Docs","type":"manual"}'
seclai sources get <sourceId>
seclai sources update <sourceId> --json '{"name":"Renamed"}'
seclai sources delete <sourceId>source is an alias for sources (e.g. seclai source list).
seclai sources upload <sourceId> --file ./doc.pdf [--title "Doc"] [--mime-type application/pdf] [--metadata '{}']
seclai sources upload-text <sourceId> --json '{"title":"Note","text":"Hello world"}'Whether what you uploaded has finished indexing. Both uploads return a
content_version_id; pass it back to poll.
seclai sources contents list <sourceId> [--page N] [--limit N] [--sort created_at|title|status] [--order asc|desc]
seclai sources contents list <sourceId> --status failed
seclai sources contents list <sourceId> --content-version-id <id> --content-version-id <id>
seclai sources contents status <sourceId> <contentVersionId>list prints {data, pagination} on every API version. Repeat
--content-version-id to poll a batch in one request, and keep a request to
about 100 ids: they travel in the URL, and one over 8,192 bytes is rejected with
a 414. An empty id is refused rather than dropped, so an unset shell variable
cannot widen the listing.
seclai sources exports list <sourceId> [--page N] [--limit N]
seclai sources exports create <sourceId>
seclai sources exports get <sourceId> <exportId>
seclai sources exports cancel <sourceId> <exportId>
seclai sources exports delete <sourceId> <exportId>
seclai sources exports download <sourceId> <exportId>
seclai sources exports estimate <sourceId>seclai sources migration get <sourceId>
seclai sources migration start <sourceId>
seclai sources migration cancel <sourceId>seclai contents get <contentVersionId> [--start N] [--end N]
seclai contents delete <contentVersionId>
seclai contents embeddings <contentVersionId> [--page N] [--limit N]seclai contents upload <contentVersionId> --file ./updated.pdf [--metadata '{}']
seclai contents replace <contentVersionId> --file ./updated.pdf # alias
seclai contents replace-text <contentVersionId> --json '{"title":"Note","text":"Updated content"}'seclai kb list [--page N] [--limit N] [--sort <field>] [--order asc|desc]
seclai kb create --json '{"name":"Support KB"}'
seclai kb get <kbId>
seclai kb update <kbId> --json '{"name":"Renamed"}'
seclai kb delete <kbId>seclai memory list [--page N] [--limit N]
seclai memory create --json '{"name":"Chat Memory","type":"conversation"}'
seclai memory get <memoryBankId>
seclai memory update <memoryBankId> --json '{"name":"Renamed"}'
seclai memory delete <memoryBankId>
seclai memory stats <memoryBankId>
seclai memory agents <memoryBankId>
seclai memory compact <memoryBankId>
seclai memory delete-source <memoryBankId>
seclai memory templates
seclai memory test-compaction <memoryBankId> [--json '...']
seclai memory test-compaction-standalone [--json '...']seclai memory ai generate --user-input "Configure compaction"
seclai memory ai last
seclai memory ai accept <conversationId> --json '{"accepted":true}'seclai evals criteria list <agentId> [--page N] [--limit N] [--paged]
seclai evals criteria create <agentId> --json '{"name":"Quality"}'
seclai evals criteria get <criteriaId>
seclai evals criteria update <criteriaId> --json '{"name":"Renamed"}'
seclai evals criteria delete <criteriaId>
seclai evals criteria summary <criteriaId>--paged wraps the results in {"data": [...]} instead of returning a bare
array, so .data is a stable path to read whatever --api-version is in
effect. The same flag is on alerts configs list, models alerts list and
agents runs eval-results, whose top-level keys (configs, alerts) also
become data from 2026-07-27. Nothing is invented: the pagination block appears only once the API
sends one, from --api-version 2026-07-27. Migrate scripts to .data first,
then opt in to get .pagination.
seclai evals results list <criteriaId> [--page N] [--limit N]
seclai evals results create <criteriaId> --json '{"run_id":"...","score":0.9}'seclai evals compatible-runs <criteriaId> [--page N] [--limit N]
seclai evals test-draft <agentId> --json '{"criteria":{...}}'
seclai evals agent-results <agentId> [--page N] [--limit N]
seclai evals agent-runs <agentId> [--page N] [--limit N]
seclai evals non-manual-summary <agentId>seclai solutions list [--page N] [--limit N]
seclai solutions create --json '{"name":"My Solution"}'
seclai solutions get <solutionId>
seclai solutions update <solutionId> --json '{"name":"Renamed"}'
seclai solutions delete <solutionId>seclai solutions link <solutionId> --agents '["id1","id2"]' --kb '["id3"]' --sources '["id4"]'
seclai solutions unlink <solutionId> --agents '["id1"]'seclai solutions convos list <solutionId>
seclai solutions convos add <solutionId> --json '{"user_input":"Add a source"}'
seclai solutions convos mark <solutionId> <conversationId> --json '{"accepted":true}'seclai solutions ai generate <solutionId> --user-input "Add a FAQ source"
seclai solutions ai kb <solutionId> --user-input "Create a knowledge base"
seclai solutions ai source <solutionId> --user-input "Create a file source"
seclai solutions ai accept <solutionId> <conversationId>
seclai solutions ai decline <solutionId> <conversationId>seclai governance ai generate --user-input "Create a content safety policy"
seclai governance ai list
seclai governance ai accept <conversationId>
seclai governance ai decline <conversationId>seclai alerts list [--page N] [--limit N] [--status open]
seclai alerts get <alertId>
seclai alerts status <alertId> --json '{"status":"resolved"}'
seclai alerts comment <alertId> --json '{"comment":"Investigating"}'
seclai alerts subscribe <alertId>
seclai alerts unsubscribe <alertId>There is no --severity: GET /alerts declares no such filter, so it never
filtered — it returned unfiltered rows that looked filtered. Filter client-side:
seclai alerts list | jq '[.data[] | select(.severity == "high")]'.
seclai alerts configs list [--page N] [--limit N]
seclai alerts configs create --json '{"name":"Critical Alerts","type":"email"}'
seclai alerts configs get <configId>
seclai alerts configs update <configId> --json '{"name":"Renamed"}'
seclai alerts configs delete <configId>seclai alerts prefs list
seclai alerts prefs update <organizationId> <alertType> --json '{"enabled":true}'seclai models list [--provider <name>] [--supports-tool-use] [--supports-thinking]
seclai models list --supports-input-media image --supports-output-media video
seclai models get <modelId>
# Each media-generation modality and tier, with its model and cost
seclai models tiers
# Embedding models a source can index with, and rerankers a knowledge base can use
seclai models embedders [--supports-input-media text|image|video|audio] [--paged]
seclai models rerankers [--paged]embedders and rerankers print the list under models, with the defaults and
pricing beside it, on every API version. --paged prints it under data
instead, plus the pagination block once the API sends one.
seclai models alerts list [--page N] [--limit N]
seclai models alerts mark-read <alertId>
seclai models alerts mark-all-read
seclai models alerts unread-countseclai models recommendations <modelId>seclai models experiments list [--days N] [--start-date <date>] [--end-date <date>] [--limit N] [--offset N]
seclai models experiments create --json '{"model_ids":["gpt-4o"],"prompt":"Compare responses"}'
seclai models experiments get <experimentId>
seclai models experiments cancel <experimentId>
seclai models experiments delete <experimentId> # soft-delete, preserves audit historyseclai search --query "deployment guide" [--limit N] [--entity-type agent|source|kb]
# Search the Seclai documentation
seclai docs search --query "memory banks" [--mode keyword|semantic] [--limit N]# The authenticated user's account ID and organization memberships
seclai meAgent email: the domains agents send from, the inbound blocklist, inbound health, and recipient opt-outs.
seclai email domains list
seclai email domains add --kind custom --value mail.example.com [--delegated]
seclai email domains verify <domainId> # check DNS now
seclai email domains set-primary <domainId>
seclai email domains test-email <domainId> # send a test to the account owner
seclai email domains dmarc <domainId> [--days N] [--top-sources N]
seclai email domains remove <domainId>
seclai email domains use-shared # revert to agent.seclai.comadd returns the DNS records to publish. Use --delegated when the domain's
DNS is delegated to Seclai, so those records are published for you.
seclai email blocked list [--limit N] [--offset N]
seclai email blocked add --sender-email spam@example.com [--note "phishing"]
seclai email blocked add --sender-email example.com --match-type domain
seclai email blocked remove <blockedId>
seclai email blocked auto-block-mode disabled|input|input_and_outputseclai email inbound status # quota, pause state, queued runs
seclai email inbound rejections [--agent-id <id>] [--limit N]
seclai email inbound cancel-queued # fail all over-quota parked runs
seclai email inbound resume # lift the account-wide pauseseclai email optouts list [--agent-id <id>] [--limit N] [--offset N]
seclai email optouts remove <optoutId>The cloud-drive connections that file triggers, drive steps and cloud_drive
sources read from. Connecting a drive happens in the app; these commands inspect
and maintain the connections that exist.
seclai cloud-drives providers # providers that can be connected
seclai cloud-drives list
seclai cloud-drives get <connectionId>
seclai cloud-drives update <connectionId> [--name <name>] [--folder-path <path>]
seclai cloud-drives update <connectionId> --whole-drive
seclai cloud-drives agents <connectionId> # agents using the connection
seclai cloud-drives rejections <connectionId> [--limit N] # skipped files, newest first
seclai cloud-drives disconnect <connectionId> # revoke tokens, keep the connection
seclai cloud-drives delete <connectionId>The listings print a plain array on every API version.
update changes only what you pass. Changing the folder resets the sync cursor,
so files already in the new folder do not fire triggers. An empty
--folder-path is refused, because the API reads it as the whole drive — say
--whole-drive when that is what you mean.
delete is refused with a 409 while an agent trigger or a content source still
depends on the connection. agents does not list content sources, so an empty
result does not mean the delete will go through.
rejections is where to look when an agent did not run for a file: a skipped
file fires no trigger. --limit takes 1 to 200 and defaults to 50.
Top-level AI assistant for multi-domain operations.
seclai ai feedback --json '{"conversation_id":"...","feedback":"helpful"}'
seclai ai kb --user-input "Create a support knowledge base"
seclai ai source --user-input "Create a documentation source"
seclai ai solution --user-input "Build a customer support solution"
seclai ai memory --user-input "Create a conversation memory bank"
seclai ai memory-history
seclai ai accept <conversationId> [--json '...']
seclai ai decline <conversationId>
seclai ai memory-accept <conversationId> [--json '...']Install Seclai skill files for AI coding tools (Copilot, Claude Code, Cursor, Windsurf, Codex, Kiro, Cline, Roo Code, Gemini, Antigravity).
# Auto-detect tools from workspace directory structure
seclai skills install
# Target a specific tool
seclai skills install --tool copilot
seclai skills install --tool claude
seclai skills install --tool cursor
seclai skills install --tool kiro
seclai skills install --tool cline
# Install for all supported tools
seclai skills install --tool all
# Specify a custom directory
seclai skills install --tool copilot --dir /path/to/project
# Via npx (no install required)
npx @seclai/cli skills installSkills follow the Agent Skills specification. Each tool gets a seclai-cli/ directory containing a SKILL.md with YAML frontmatter and a references/ subdirectory for progressive disclosure:
| Tool | Directory |
|---|---|
| Copilot | .github/copilot/seclai-cli/ |
| Claude Code | .claude/skills/seclai-cli/ |
| Cursor | .cursor/skills/seclai-cli/ |
| Windsurf | .windsurf/skills/seclai-cli/ |
| Codex | .codex/skills/seclai-cli/ |
| Kiro | .kiro/steering/seclai-cli/ |
| Cline | .clinerules/seclai-cli/ |
| Roo Code | .roo/rules/seclai-cli/ |
| Gemini | .gemini/seclai-cli/ |
| Antigravity | .antigravity/seclai-cli/ |
You can also install skills using the skills CLI:
npx skills add seclai/seclai-cliConfigure the Seclai MCP server for AI coding tools:
# Auto-detect tools and write MCP config
seclai mcp configure --key YOUR_API_KEY
# Target a specific tool
seclai mcp configure --key YOUR_API_KEY --target claude-code
seclai mcp configure --key YOUR_API_KEY --target cursor
seclai mcp configure --key YOUR_API_KEY --target claude-desktop
seclai mcp configure --key YOUR_API_KEY --target windsurf
# Configure all known targets
seclai mcp configure --key YOUR_API_KEY --target all
# Show the MCP config snippet (for manual setup)
seclai mcp show
seclai mcp show --key YOUR_API_KEY| Target | Config File | Scope |
|---|---|---|
| claude-code | .mcp.json |
Project |
| cursor | .cursor/mcp.json |
Project |
| claude-desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
Global |
| windsurf | ~/.codeium/windsurf/mcp_config.json |
Global |
The command merges into existing config files — it won't overwrite other MCP servers.
Generate shell completion scripts for tab-completion of commands:
# Bash — add to ~/.bashrc
eval "$(seclai completion bash)"
# Zsh — add to ~/.zshrc
eval "$(seclai completion zsh)"
# Fish — save to completions directory
seclai completion fish > ~/.config/fish/completions/seclai.fishnpm installnpm run typechecknpm run buildnpm run dev -- --helpnpm testnpm run build
npm link
seclai --help