3a58fba521
OpenAI-compatible FastAPI server wrapping the Claude Code CLI. Translates /v1/chat/completions requests into claude --print subprocess calls using stream-json input/output for multi-turn support and real streaming deltas. Features: - Full multi-turn conversation support via --input-format stream-json - Real-time streaming with --include-partial-messages delta tracking - Rate limit (429) and auth error (401) detection from CLI stderr - Configurable timeout, graceful subprocess cleanup on disconnect - /v1/models endpoint and /health check
3.4 KiB
3.4 KiB
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 from the
resultevent are forwarded in the final response.
Authentication is handled entirely by the claude binary; the bridge just
bind-mounts the existing ~/.claude credentials directory.
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:ro \
claude-max-bridge
Supported models
claude-sonnet-4-6(default fallback)claude-opus-4-5claude-haiku-4-5claude-opus-4-0claude-sonnet-3-7claude-haiku-3-5
Unknown model names in requests fall back to claude-sonnet-4-6.
LiteLLM configuration
Add these entries to your litellm_config.yaml:
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" # required by LiteLLM but ignored by bridge
- model_name: claude-opus-4-5
litellm_params:
model: openai/claude-opus-4-5
api_base: http://claude-max-bridge:8000/v1
api_key: "unused"
Replace claude-max-bridge with the actual hostname or IP where the bridge
container is running.
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
temperature,top_p, and other sampling parameters are silently ignored (logged at DEBUG level) because the CLI does not expose them.- Rate-limit responses from the CLI are translated to HTTP
429. - Auth errors from the CLI are translated to HTTP
401. - The subprocess is killed on client disconnect or timeout.