claude-max-bridge
A small FastAPI server that exposes an OpenAI-compatible /v1/chat/completions
endpoint and translates each request into a claude CLI subprocess call.
This lets LiteLLM (or any OpenAI-compatible client) consume a Claude Max
subscription through the locally-installed claude binary without needing an
Anthropic API key.
How it works
- Client sends an OpenAI chat-completions request (streaming or non-streaming).
- The bridge extracts the system prompt and conversation turns.
- It spawns
claude --print --output-format stream-json --input-format stream-json ...and pipes the conversation as NDJSON to stdin. - It parses the NDJSON stdout, computes text deltas from partial assistant messages, and re-emits them as OpenAI SSE chunks (or buffers for non-streaming).
- Usage totals and cost from the
resultevent are forwarded in the response.
Authentication is handled entirely by the claude binary; the bridge
bind-mounts the existing ~/.claude credentials directory (rw, so the CLI
can refresh expired OAuth tokens automatically).
Environment variables
| Variable | Default | Description |
|---|---|---|
CLAUDE_BIN |
/usr/local/bin/claude |
Path to the claude CLI binary |
CLAUDE_HOME |
/root/.claude |
Path to the Claude config / credentials directory |
LOG_LEVEL |
INFO |
Python logging level |
REQUEST_TIMEOUT_S |
120 |
Per-request subprocess timeout in seconds |
Running with Docker
docker build -t claude-max-bridge .
docker run -d \
--name claude-max-bridge \
-p 8000:8000 \
-v /usr/local/bin/claude:/usr/local/bin/claude:ro \
-v /root/.claude:/root/.claude:rw \
-v /root/.claude.json:/root/.claude.json:rw \
claude-max-bridge
Supported models
claude-sonnet-4-6(default fallback)claude-opus-4-7claude-haiku-4-5claude-opus-4-5claude-opus-4-0claude-sonnet-3-7claude-haiku-3-5- Short aliases:
sonnet,opus,haiku
Unknown model names fall back to claude-sonnet-4-6.
Extended parameters (extra_body)
The bridge maps several CLI flags via the extra_body field:
extra_body key |
CLI flag | Notes |
|---|---|---|
effort |
--effort <level> |
low/medium/high/xhigh/max. Overrides temperature mapping. |
fallback_model |
--fallback-model <model> |
Model used when primary hits rate limit |
system_prompt_mode |
--system-prompt / --append-system-prompt |
"replace" (default) or "append" |
max_turns |
--max-turns <n> |
Cap agentic turns (non-interactive) |
max_budget_usd |
--max-budget-usd <amount> |
Per-request cost cap |
json_schema |
--json-schema <schema> |
Structured JSON output |
exclude_dynamic_system_prompt_sections |
--exclude-dynamic-system-prompt-sections |
Better cache reuse for multi-user |
temperature is also mapped to --effort when no explicit effort is set:
<0.2→low, <0.4→medium, <0.7→high, <0.9→xhigh, ≥0.9→max.
response_format.json_schema (OpenAI standard) is also recognised and
forwarded to --json-schema automatically.
Response extensions
Non-streaming responses and streaming done-chunks include:
| Field | Description |
|---|---|
x_claude_cost_usd |
Actual cost as reported by CLI (total_cost_usd). $0 for Max subscription. |
x_claude_rate_limit |
Rate-limit info: utilization, resetsAt, rateLimitType |
LiteLLM configuration
model_list:
- model_name: claude-sonnet-4-6
litellm_params:
model: openai/claude-sonnet-4-6
api_base: http://claude-max-bridge:8000/v1
api_key: "unused"
stream_timeout: 120
timeout: 120
input_cost_per_token: 0.000003 # for dashboard visibility; actual cost $0
output_cost_per_token: 0.000015
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /health |
Liveness check, always returns 200 OK |
| GET | /v1/models |
Lists supported models |
| POST | /v1/chat/completions |
OpenAI-compatible chat completions |
Notes
- Rate-limit responses from the CLI are translated to HTTP
429. - Auth errors are translated to HTTP
401. - The subprocess is killed on client disconnect or timeout.
top_p,frequency_penalty,presence_penalty,seed,stop,nare ignored.- MCP servers are explicitly disabled (
--mcp-config '{"mcpServers":{}}'--strict-mcp-config) to prevent credential leakage and side-effects.