cli-agent-api
v0.0.1
Published
API for your CLI agents.
Readme
cli-agent-api
Wrap CLI coding agents behind OpenAI-style APIs.
MVP scope
- Claude Code only
POST /claude/v1/chat/completions- Streaming and non-streaming responses
- Generic route shape is
/:provider/<rest>, but onlyclaude+/v1/chat/completionsis implemented today
Requirements
- Node.js 22+
pnpmclaudeavailable onPATH
Path defaults:
XDG_DATA_HOMEdefaults to$HOME/.local/shareDATA_DIRdefaults to$XDG_DATA_HOME/cli-agent-api- Claude is started with
cwdset to$DATA_DIR/agent-workspace - The server creates
$DATA_DIRand$DATA_DIR/agent-workspaceautomatically when needed
Run
pnpm exec cli-agent-api serveOptions:
--hostdefaults to127.0.0.1orHOST--portdefaults to8041orPORT--api-keyreads comma-separated bearer tokens from the flag orAPI_KEY
Authentication is disabled when no API key is configured. When enabled, requests must send Authorization: Bearer <token>.
Example:
cli-agent-api serve --port 8041 --api-key alpha,betaThe server writes newline-delimited JSON logs to stdout. Every line includes timestamp, level, event, and message.
HTTP behavior
- CORS is currently open to all origins with
Access-Control-Allow-Origin: * OPTIONSpreflight requests are handled automatically- Unsupported providers return
404 provider_not_found - Unsupported provider routes return
404 not_found
The permissive CORS policy is convenient for browser-based clients like Open WebUI, but it is less safe. If you expose the server beyond localhost, use API keys at minimum.
Request behavior
- The last message is used as the new Claude prompt
- The last message must contain text content
systemanddevelopermessages are not written into Claude history; their text is concatenated and forwarded to Claude as--system-prompt- Earlier text
userandassistantmessages are serialized into a synthetic Claude session under~/.claude/projects/<encoded-cwd>/<session-id>.jsonl - When prior history exists, Claude is invoked with
--resume <session-id>plus the new prompt - Non-text or unsupported history messages are ignored during session seeding
Claude behavior
Claude is invoked in print mode with stream-json output and these default tools enabled:
WebSearchWebFetch
Equivalent CLI flags:
claude -p \
--output-format stream-json \
--include-partial-messages \
--verbose \
--tools "WebSearch WebFetch" \
--allowedTools "WebSearch WebFetch"Request example
curl http://127.0.0.1:8041/claude/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer alpha' \
-d '{
"model": "sonnet",
"stream": true,
"messages": [
{ "role": "developer", "content": [{ "type": "text", "text": "You are an AI agent." }] },
{ "role": "user", "content": [{ "type": "text", "text": "Hello there!" }] }
]
}'This becomes roughly:
claude ... --system-prompt "You are an AI agent." "Hello there!"Response behavior
Non-streaming responses return standard chat completion objects plus:
usageis always present- OpenAI-style token fields are included
- extra Claude usage fields are preserved when available, such as cost, duration, quota, or provider-specific metadata
Streaming responses return SSE chat completion chunks and always end with:
- a final chunk containing
finish_reason - a separate final chunk with
choices: []andusage data: [DONE]
Streaming extensions
The server currently emits a few nonstandard Chat Completions fields because Claude Code exposes richer agent state than the base API:
delta.reasoningdelta.reasoning_contentdelta.reasoning_detailsdelta.tool_callsdelta.tool_calls[].result
The matching non-streaming assistant message may also include:
message.reasoningmessage.reasoning_contentmessage.reasoning_details
These fields are useful for compatible clients, but generic OpenAI-compatible UIs may ignore them. In particular, tool_calls[].result is not part of the standard OpenAI tool-calling flow.
Architecture notes
- Provider-specific process management lives under
src/providers/ - API surface handlers live under
src/apis/ - The server is wired through handler/provider registries so adding
/v1/responsesor new providers should not require rewriting the core server - Current exported provider support is Claude only
Development
pnpm installpnpm testpnpm typecheckpnpm buildRelease and publish
Create a local package tarball:
pnpm run pack-packagePublish an existing tarball:
pnpm run publish-packed-package --tag latestGitHub Actions now provides:
Checksfor lint, typecheck, and testPack Packageto build and uploadpackage.tgzPack and Publish Packageto run checks, pack, and publish on GitHub release publish or manual dispatch
For GitHub Actions publishing, configure the NPM environment on the repository:
NPM_REGISTRY_SERVERvariable when publishing to a non-default registryNPM_AUTH_TOKENsecret when not using npm trusted publishing
