Local proxy that lets any app use the Anthropic Messages API through your Claude Code subscription. No API keys needed.
You have a Claude Pro/Max subscription with Claude Code. You want to use it from other apps (OpenClaw, custom bots, scripts) — but Anthropic's API requires API keys with separate billing, and OAuth tokens get flagged as "third-party apps" requiring extra usage credits.
This proxy sits between your app and Claude. It:
- Accepts standard Anthropic Messages API requests (including SSE streaming)
- Routes them through
claude -p(Claude Code's print mode) - Returns proper Messages API responses
Your requests go through Claude Code's first-party auth — using your existing subscription, no API keys, no extra-usage credits.
Your App → claude-code-proxy (localhost:8082) → claude -p → Anthropic API
Anthropic Messages API First-party Subscription
compatible auth limits
- Node.js 18+
- Claude Code installed and logged in (
claude auth statusshould showloggedIn: true) - Claude Pro or Max subscription
npx claude-code-proxyThat's it. The proxy starts on http://127.0.0.1:8082.
Point your app's Anthropic base URL to the proxy:
export ANTHROPIC_BASE_URL=http://127.0.0.1:8082--port <number> Port to listen on (default: 8082)
--claude <path> Path to claude binary (auto-detected)
--timeout <ms> Max time per request in ms (default: 300000)
npx claude-code-proxyOpenClaw's Anthropic provider ignores ANTHROPIC_BASE_URL. You need to patch it so requests route through the proxy instead of directly to Anthropic.
Find the createClient function in:
<openclaw-install>/node_modules/@mariozechner/pi-ai/dist/providers/anthropic.js
On macOS with Homebrew, the full path is typically:
/opt/homebrew/lib/node_modules/openclaw/node_modules/@mariozechner/pi-ai/dist/providers/anthropic.js
Add these 3 lines to the top of the createClient function body:
function createClient(model, apiKey, interleavedThinking, optionsHeaders, dynamicHeaders) {
// [claude-proxy] Allow ANTHROPIC_BASE_URL env var to override hardcoded baseUrl
if (typeof process !== "undefined" && process.env.ANTHROPIC_BASE_URL && model.provider === "anthropic") {
model = { ...model, baseUrl: process.env.ANTHROPIC_BASE_URL };
}
// ... rest of function unchangedNote: This patch is overwritten every time you run
openclaw update. You'll need to re-apply it after updates.
Edit your OpenClaw gateway plist (~/Library/LaunchAgents/ai.openclaw.gateway.plist) and add this inside the <dict> under EnvironmentVariables:
<key>ANTHROPIC_BASE_URL</key>
<string>http://127.0.0.1:8082</string>The proxy routes requests through claude -p, which can take several minutes for complex tool-use responses. OpenClaw's default LLM idle timeout is 60 seconds — too short.
Add these to your ~/.openclaw/openclaw.json inside agents.defaults:
{
"agents": {
"defaults": {
"timeoutSeconds": 600,
"llm": {
"idleTimeoutSeconds": 600
}
}
}
}# Restart the gateway to pick up env + config changes
launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl load ~/Library/LaunchAgents/ai.openclaw.gateway.plistThe only thing you need to redo is step 2 — re-apply the pi-ai patch. The proxy, plist, and config changes survive updates.
To run the proxy as a persistent background service:
cat > ~/Library/LaunchAgents/claude-code-proxy.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>claude-code-proxy</string>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/path/to/claude-code-proxy/index.js</string>
<string>--port</string>
<string>8082</string>
</array>
<key>StandardOutPath</key><string>/tmp/claude-code-proxy.log</string>
<key>StandardErrorPath</key><string>/tmp/claude-code-proxy.err</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key><string>/Users/YOU</string>
<key>PATH</key><string>/usr/local/bin:/usr/bin:/bin:~/.local/bin</string>
</dict>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/claude-code-proxy.plist# Python
import anthropic
client = anthropic.Anthropic(base_url="http://127.0.0.1:8082", api_key="unused")
msg = client.messages.create(model="claude-opus-4-6", max_tokens=1024, messages=[...])// Node.js
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "http://127.0.0.1:8082", apiKey: "unused" });
const msg = await client.messages.create({ model: "claude-opus-4-6", max_tokens: 1024, messages: [...] });# curl
curl http://127.0.0.1:8082/v1/messages \
-H "Content-Type: application/json" \
-d '{"model":"claude-opus-4-6","max_tokens":100,"messages":[{"role":"user","content":"Hello"}]}'- Your app sends a standard Anthropic Messages API request to the proxy
- The proxy converts the messages to a text prompt
- It calls
claude -p --output-format json --model <model>via stdin - Claude Code handles auth using your subscription (first-party, plan limits)
- The proxy converts Claude Code's JSON output back to Messages API format
- If
stream: true, it emits proper SSE events
Anthropic pattern-matches system prompts for known third-party app identifiers and reclassifies those requests to draw from extra-usage credits instead of plan limits. The proxy automatically replaces detected identifiers with neutral terms. You can add custom patterns in the SANITIZE_PATTERNS array.
- Not real streaming — Claude Code runs to completion, then the proxy emits the result as SSE events. The response appears all at once rather than token-by-token.
- Tool use — Tool definitions are injected into the system prompt and tool calls are parsed from text output. Works for simple cases but may not match the native API's structured tool use exactly.
- Concurrency — Each request spawns a
claude -pprocess. Heavy concurrent usage may hit Claude Code's rate limits. - macOS/Linux only — Requires Claude Code CLI. Windows support depends on Claude Code's Windows availability.
MIT