Claude
Skills
Sign in
Back

claude-code-proxy-patterns

Included with Lifetime
$97 forever

Claude Code OAuth proxy patterns and anti-patterns for multi-provider model routing.

AI Agents

What this skill does


<!-- # SSoT-OK: version references are documentation of binary analysis findings, not package versions -->

# Claude Code Proxy Patterns

Multi-provider proxy that routes Claude Code model tiers to different backends. Haiku to MiniMax (cost/speed), Sonnet/Opus to Anthropic (native OAuth passthrough). Includes Go binary proxy with launchd auto-restart and failover wrapper for resilience.

**Scope**: Local reverse proxy for Claude Code with OAuth subscription (Max plan). Routes based on model name in request body.

**Reference implementations**:

- Go proxy binary: `/usr/local/bin/claude-proxy` (port 8082)

---

> **Self-Evolving Skill**: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.

## When to Use This Skill

- Building or debugging a Claude Code multi-provider proxy
- Setting up `ANTHROPIC_BASE_URL` with OAuth subscription mode
- Integrating Anthropic-compatible providers (MiniMax, etc.)
- Diagnosing "OAuth not supported" or auth failures through a proxy
- Understanding how Claude Code stores and transmits OAuth tokens

**Do NOT use for**: Claude API key-only setups (no proxy needed), MCP server development, Claude Code hooks (operate at tool level, not API level), or corporate HTTPS proxy traversal.

---

## Architecture

```
Claude Code (OAuth/Max subscription)
    |
    |  ANTHROPIC_BASE_URL=http://127.0.0.1:8082 (Go proxy)
    |  (unset ANTHROPIC_API_KEY to avoid auth conflict)
    v
+----------------------------------+
| Go proxy (:8082)                 |
| launchd managed, auto-restart   |
+----------------------------------+
    |
    | model =
    | claude-haiku-
    | 4-5-20251001
    v
+-----------+
| MiniMax   |
| highspeed |
+-----------+
```

**Port Configuration**:

- `:8082` - Go proxy (entry point, launchd-managed, auto-restart)

The Go proxy uses `cenkalti/backoff/v4` for built-in retry logic.

The proxy reads the `model` field from each `/v1/messages` request body. If it matches the configured Haiku model ID, the request goes to MiniMax. Everything else falls through to real Anthropic with OAuth passthrough.

---

## Working Patterns

### WP-01: Keychain OAuth Token Reading

Read OAuth tokens from macOS Keychain where Claude Code stores them.

**Service**: `"Claude Code-credentials"` (note the space before the hyphen)
**Account**: Current username via `getpass.getuser()`

```python
import subprocess, json, getpass

result = subprocess.run(
    ["security", "find-generic-password",
     "-s", "Claude Code-credentials",
     "-a", getpass.getuser(), "-w"],
    capture_output=True, text=True, timeout=5, check=False,
)
if result.returncode == 0:
    data = json.loads(result.stdout.strip())
    oauth = data.get("claudeAiOauth")
```

See [references/oauth-internals.md](./references/oauth-internals.md) for the full deep dive.

### WP-02: Token JSON Structure

The Keychain stores a JSON envelope with the `claudeAiOauth` key.

```json
{
  "claudeAiOauth": {
    "accessToken": "eyJhbG...",
    "refreshToken": "rt_...",
    "expiresAt": 1740268800000,
    "subscriptionType": "claude_pro_2025"
  }
}
```

**Note**: `expiresAt` is in **milliseconds** (Unix epoch _ 1000). Compare with `time.time() _ 1000` or divide by 1000 for seconds.

### WP-03: OAuth Beta Header

The `anthropic-beta: oauth-2025-04-20` header is **required** for OAuth token authentication. Without it, Anthropic rejects the Bearer token.

**Critical**: APPEND to existing beta headers, do not replace them.

```python
# proxy.py:304-308
existing_beta = original_headers.get("anthropic-beta", "")
beta_parts = [b.strip() for b in existing_beta.split(",") if b.strip()] if existing_beta else []
if "oauth-2025-04-20" not in beta_parts:
    beta_parts.append("oauth-2025-04-20")
target_headers["anthropic-beta"] = ",".join(beta_parts)
```

### WP-04: ANTHROPIC_API_KEY=proxy-managed

Setting `ANTHROPIC_BASE_URL` alone is insufficient in OAuth mode. Claude Code must also see `ANTHROPIC_API_KEY` set to switch from OAuth-only mode to API-key mode, which then honors `ANTHROPIC_BASE_URL`.

```bash
# In .zshenv (managed by proxy-toggle)
export ANTHROPIC_BASE_URL="http://127.0.0.1:8082"
export ANTHROPIC_API_KEY="proxy-managed"
```

The value `"proxy-managed"` is a dummy sentinel. The proxy intercepts it (line 324) and never forwards it to providers.

### WP-05: OAuth Token Cache with TTL

Avoid repeated Keychain subprocess calls by caching the token for 5 minutes.

```python
# proxy.py:117-118
_oauth_cache: dict = {"token": None, "expires_at": 0.0, "fetched_at": 0.0}
_OAUTH_CACHE_TTL = 300  # Re-read from Keychain every 5 minutes
```

Cache invalidation triggers:

- TTL expired (5 minutes since last fetch)
- Token's `expiresAt` has passed
- Proxy restart

### WP-06: Auth Priority Chain

The proxy tries multiple auth sources in order for Anthropic-bound requests.

```
1. REAL_ANTHROPIC_API_KEY env var   -> x-api-key header (explicit config)
2. Keychain OAuth token             -> Authorization: Bearer + anthropic-beta
3. ~/.claude/.credentials.json      -> Authorization: Bearer (plaintext fallback)
4. Forward client Authorization     -> Pass through whatever Claude Code sent
5. No auth                          -> Will 401 (expected)
```

See `proxy.py:293-314` for the implementation.

### WP-07: count_tokens Endpoint Auth

The `/v1/messages/count_tokens` endpoint needs the same auth as `/v1/messages`. Claude Code calls this for preflight token counting. Missing auth here causes silent failures. Returns 501 for non-Anthropic providers (MiniMax doesn't support it).

### WP-08: Anthropic-Compatible Provider URLs

Third-party providers that support the Anthropic `/v1/messages` API format.

| Provider          | Base URL                           | Notes                                             |
| ----------------- | ---------------------------------- | ------------------------------------------------- |
| MiniMax highspeed | `https://api.minimax.io/anthropic` | Returns `base_resp` field, extra `thinking` block |

See [references/provider-compatibility.md](./references/provider-compatibility.md) for the full matrix.

### WP-09: Concurrency Semaphore

Per-provider rate limiting prevents overwhelming third-party APIs. No semaphore for Anthropic (they handle their own rate limiting).

```python
# proxy.py:207-209
MAX_CONCURRENT_REQUESTS = int(os.getenv("MAX_CONCURRENT_REQUESTS", "5"))
haiku_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
opus_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
sonnet_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
```

### WP-10: proxy-toggle Enable/Disable

The `proxy-toggle` script manages `.zshenv` entries and a flag file atomically.

```bash
~/.claude/bin/proxy-toggle enable    # Adds env vars, creates flag file, checks health
~/.claude/bin/proxy-toggle disable   # Removes env vars, removes flag file
~/.claude/bin/proxy-toggle status    # Shows routing flag, proxy process, .zshenv state
```

**Important**: Claude Code must be restarted after toggling because `ANTHROPIC_BASE_URL` is read at startup.

### WP-11: Health Endpoint

The `/health` endpoint returns provider configuration state for monitoring.

```bash
curl -s http://127.0.0.1:8082/health | jq .
```

### WP-12: Go Proxy with Retry

Go proxy with built-in retry using `cenkalti/backoff/v4` (exponential backoff: 500ms -> 1s -> 2s, max 5s elapsed).

```go
import "github.com/cenkalti/backoff/v4"

backoffConfig := backoff.NewExponentialBackOff(
    backoff.WithInitialInterval(500 * time.Millisecond),
    backoff.WithMultiplier(2),
    backoff.WithMaxInterval(2 * time.Second),
    backoff.WithMaxElapsedTime(5 * time.Second),
)
err := backoff.Retry(operation, backoffConfig)
```

**Location**: `/usr/local/bin/claude-proxy` | **Environment**: `ANTHROPIC_BASE_URL=http://127.0.0.1:8082` in `.zshenv`

### WP-13: Launchd Service Configuration

The Go proxy ru

Related in AI Agents