fkrebs 5f8a98772d fix: --verbose flag, rw credential mounts, correct MCP config schema
- Add --verbose (required for --output-format stream-json)
- Fix --mcp-config value: {"mcpServers":{}} (schema rejects plain {})
- Add --strict-mcp-config to prevent any MCP server from loading
- .claude dir and .claude.json mounted rw so CLI can refresh OAuth token
2026-05-22 16:34:06 +02:00

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

  1. Client sends an OpenAI chat-completions request (streaming or non-streaming).
  2. The bridge extracts the system prompt and conversation turns.
  3. It spawns claude --print --output-format stream-json --input-format stream-json ... and pipes the conversation as NDJSON to stdin.
  4. 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).
  5. Usage totals from the result event 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-5
  • claude-haiku-4-5
  • claude-opus-4-0
  • claude-sonnet-3-7
  • claude-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.
S
Description
OpenAI-compatible HTTP bridge over the Claude Code CLI — connects LiteLLM to the Claude Max subscription
Readme 40 KiB
Languages
Python 98.5%
Dockerfile 1.5%