Skip to content

Commit 5aeb375

Browse files
authored
Merge pull request #52 from skipships/readme-tweaks
docs: improves README
2 parents bcbb065 + 5880357 commit 5aeb375

1 file changed

Lines changed: 79 additions & 101 deletions

File tree

README.md

Lines changed: 79 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,59 @@
44
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55
[![GitHub stars](https://img.shields.io/github/stars/rynfar/opencode-claude-max-proxy.svg)](https://github.com/rynfar/opencode-claude-max-proxy/stargazers)
66

7-
A transparent proxy that lets you use your **Claude Max subscription** with [OpenCode](https://opencode.ai) — with full multi-model agent delegation.
7+
A transparent proxy that allows a Claude Max subscription to be used with [OpenCode](https://opencode.ai), preserving multi-model agent routing.
88

9-
## The Idea
9+
OpenCode targets the Anthropic API, while Claude Max provides access to Claude via the [Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk). The proxy forwards tool calls from Claude Max to OpenCode so agent routing works correctly.
1010

11-
OpenCode speaks the Anthropic API. Claude Max gives you unlimited Claude through the [Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk). This proxy bridges the two — but the interesting part isn't the bridging, it's **how tool execution and agent delegation work**.
11+
Tool execution is handled within the Agent SDK, which is responsible for running tools, coordinating sub-agents, and streaming responses. Because the SDK only has access to Claude models, any tool calls or delegated tasks are executed within that scope. In configurations such as [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode), where agents may be assigned to OpenaI, Gemini, or other providers, those assignments are not preserved at execution time, and all work is effectively routed through Claude.
1212

13-
Most proxy approaches try to handle everything internally: the SDK runs tools, manages subagents, and streams back a finished result. The problem? The SDK only knows about Claude models. If you're using [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) with agents routed to GPT-5.4, Gemini, or other providers, the SDK can't reach them. Your carefully configured multi-model agent setup gets flattened to "everything runs on Claude."
13+
We avoid that limitation by forwarding tool calls instead of executing them. When a `tool_use` event is emitted, the proxy intercepts it, halts the current turn, and sends the raw payload back to OpenCode. OpenCode executes the tool, including any agent routing, and returns the result as a `tool_result`. The proxy then resumes the SDK session.
1414

15-
This proxy takes a different approach: **passthrough delegation**. Instead of handling tools internally, the proxy intercepts every tool call *before the SDK executes it*, stops the turn, and forwards the raw `tool_use` back to OpenCode. OpenCode handles execution — including routing `Task` calls through its own agent system with full model routing. The result comes back as a standard `tool_result`, the proxy resumes the SDK session, and Claude continues.
16-
17-
The effect: Claude thinks it's calling tools normally. OpenCode thinks it's talking to the Anthropic API. But behind the scenes, your oracle agent runs on GPT-5.4, your explore agent runs on Gemini, and the main session runs on Claude Max — exactly as configured.
15+
From Claude’s perspective, tool usage proceeds normally. From OpenCode’s perspective, it is interacting with the Anthropic API. Execution remains distributed according to the configured agents, allowing different models to handle different roles without being constrained by the SDK.
1816

1917
```
2018
┌──────────┐ ┌───────────────┐ ┌──────────────┐
21-
│ │ 1. Request │ │ SDK Auth │ │
19+
│ │ │ │ │ │
2220
│ OpenCode │ ─────────────► │ Proxy │ ───────────► │ Claude Max │
23-
│ │ │ (localhost) │ │ │
24-
│ │ 4. tool_use │ │ 2. Generate │ │
25-
│ │ ◄───────────── │ PreToolUse │ ◄─────────── │ Response │
26-
│ │ │ hook blocks │ │ │
27-
│ │ 5. Execute │ │ └──────────────┘
28-
│ │ ─── Task ───► │ │
29-
│ │ (routes to │ │ ┌──────────────┐
30-
│ │ GPT-5.4, │ 6. Resume │ │ oh-my- │
31-
│ │ Gemini,etc) │ ◄──────────── │ │ opencode │
32-
│ │ │ tool_result │ │ agents │
33-
│ │ 7. Final │ │ │ │
34-
│ │ ◄───────────── │ │ │ oracle: │
35-
│ │ response │ │ │ GPT-5.4 │
36-
└──────────┘ └───────────────┘ │ explore: │
37-
│ Gemini │
38-
│ librarian: │
39-
│ Sonnet │
40-
└──────────────┘
21+
│ │ │ (localhost) │ │ (Agent SDK) │
22+
│ │ │ │ │ │
23+
│ │ │ │ ◄─────────── │ tool_use │
24+
│ │ │ ◄──────────── │ │ │
25+
│ │ │ Intercept │ └──────────────┘
26+
│ │ │ (stop turn) │
27+
│ │ │ │
28+
│ │ │ ▼
29+
│ │ │ ┌──────────────┐
30+
│ │ │ │ OpenCode │
31+
│ │ │ │ agent │
32+
│ │ │ │ system │
33+
│ │ │ └──────────────┘
34+
│ │ │ │
35+
│ │ │ ▼
36+
│ │ │ ◄────────────
37+
│ │ │ Resume turn
38+
│ │ │
39+
│ │ ◄───────────── │
40+
│ │ response │
41+
└──────────┘ └───────────────┘
4142
```
4243

4344
## How Passthrough Works
4445

45-
The key insight is the Claude Agent SDK's `PreToolUse` hook — an officially supported callback that fires before any tool executes. Combined with `maxTurns: 1`, it gives us precise control over the execution boundary:
46+
The Claude Agent SDK exposes a `PreToolUse` hook that fires before any tool executes. Combined with `maxTurns: 1`, it gives us precise control over the execution boundary without monkey-patching or stream rewriting.
4647

4748
1. **Claude generates a response** with `tool_use` blocks (Read a file, delegate to an agent, run a command)
48-
2. **The PreToolUse hook fires** for each tool call we capture the tool name, input, and ID, then return `decision: "block"` to prevent SDK-internal execution
49+
2. **The PreToolUse hook fires** for each tool call - we capture the tool name, input, and ID, then return `decision: "block"` to prevent SDK-internal execution
4950
3. **The SDK stops** (blocked tool + maxTurns:1 = turn complete) and we have the full tool_use payload
5051
4. **The proxy returns it to OpenCode** as a standard Anthropic API response with `stop_reason: "tool_use"`
5152
5. **OpenCode handles everything** — file reads, shell commands, and crucially, `Task` delegation through its own agent system with full model routing
5253
6. **OpenCode sends `tool_result` back**, the proxy resumes the SDK session, and Claude continues
5354

54-
No monkey-patching. No forked SDKs. No fragile stream rewriting. Just a hook and a turn limit.
55-
5655
## Quick Start
5756

5857
### Prerequisites
5958

60-
1. **Claude Max subscription**[Subscribe here](https://claude.ai/settings/subscription)
59+
1. **Claude Max subscription**[Subscribe here](https://claude.ai/settings/billing)
6160
2. **Claude CLI** authenticated: `npm install -g @anthropic-ai/claude-code && claude login`
6261

6362
### Option A: npm Install
@@ -172,13 +171,13 @@ bun run proxy
172171

173172
Tools execute inside the proxy via MCP. Subagents run on Claude via the SDK's native agent system. Simpler setup, but all agents use Claude regardless of oh-my-opencode config.
174173

175-
| | Passthrough | Internal |
176-
|---|---|---|
177-
| Tool execution | OpenCode | Proxy (MCP) |
178-
| Agent delegation | OpenCode → multi-model | SDK → Claude only |
179-
| oh-my-opencode models | ✅ Respected | ❌ All Claude |
180-
| Agent system prompts | ✅ Full | ⚠️ Description only |
181-
| Setup complexity | Same | Same |
174+
| | Passthrough | Internal |
175+
| --------------------- | ---------------------- | ------------------- |
176+
| Tool execution | OpenCode | Proxy (MCP) |
177+
| Agent delegation | OpenCode → multi-model | SDK → Claude only |
178+
| oh-my-opencode models | ✅ Respected | ❌ All Claude |
179+
| Agent system prompts | ✅ Full | ⚠️ Description only |
180+
| Setup complexity | Same | Same |
182181

183182
## Works With Any Agent Framework
184183

@@ -201,8 +200,13 @@ The proxy tracks SDK session IDs and resumes conversations on follow-up requests
201200
Session tracking works two ways:
202201

203202
1. **Header-based** (recommended) — Add the included OpenCode plugin:
203+
204204
```json
205-
{ "plugin": ["./path/to/opencode-claude-max-proxy/src/plugin/claude-max-headers.ts"] }
205+
{
206+
"plugin": [
207+
"./path/to/opencode-claude-max-proxy/src/plugin/claude-max-headers.ts"
208+
]
209+
}
206210
```
207211

208212
2. **Fingerprint-based** (automatic fallback) — hashes the first user message to identify returning conversations
@@ -211,14 +215,14 @@ Sessions are cached for 24 hours. When using per-terminal proxies (`oc.sh`), the
211215

212216
## Configuration
213217

214-
| Variable | Default | Description |
215-
|----------|---------|-------------|
216-
| `CLAUDE_PROXY_PASSTHROUGH` | (unset) | Enable passthrough mode forward all tools to OpenCode |
217-
| `CLAUDE_PROXY_PORT` | 3456 | Proxy server port |
218-
| `CLAUDE_PROXY_HOST` | 127.0.0.1 | Proxy server host |
219-
| `CLAUDE_PROXY_WORKDIR` | (cwd) | Working directory for Claude and tools |
220-
| `CLAUDE_PROXY_MAX_CONCURRENT` | 1 | Max concurrent SDK sessions (increase with caution) |
221-
| `CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS` | 120 | Connection idle timeout |
218+
| Variable | Default | Description |
219+
| ----------------------------------- | --------- | -------------------------------------------------------- |
220+
| `CLAUDE_PROXY_PASSTHROUGH` | (unset) | Enable passthrough mode to forward all tools to OpenCode |
221+
| `CLAUDE_PROXY_PORT` | 3456 | Proxy server port |
222+
| `CLAUDE_PROXY_HOST` | 127.0.0.1 | Proxy server host |
223+
| `CLAUDE_PROXY_WORKDIR` | (cwd) | Working directory for Claude and tools |
224+
| `CLAUDE_PROXY_MAX_CONCURRENT` | 1 | Max concurrent SDK sessions (increase with caution) |
225+
| `CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS` | 120 | Connection idle timeout |
222226

223227
## Concurrency
224228

@@ -236,68 +240,58 @@ Sessions are cached for 24 hours. When using per-terminal proxies (`oc.sh`), the
236240
>
237241
> We are monitoring the upstream Bun issue for a fix.
238242
239-
## Model Mapping
240-
241-
| OpenCode Model | Claude SDK |
242-
|----------------|------------|
243-
| `anthropic/claude-opus-*` | opus |
244-
| `anthropic/claude-sonnet-*` | sonnet (default) |
245-
| `anthropic/claude-haiku-*` | haiku |
246-
247-
## Disclaimer
248-
249-
This project is an **unofficial wrapper** around Anthropic's publicly available [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk). It is not affiliated with, endorsed by, or supported by Anthropic.
250-
251-
**Use at your own risk.** The authors make no claims regarding compliance with Anthropic's Terms of Service. It is your responsibility to review and comply with [Anthropic's Terms of Service](https://www.anthropic.com/terms) and any applicable usage policies. Terms may change at any time.
252-
253-
This project calls `query()` from Anthropic's public npm package using your own authenticated account. No API keys are intercepted, no authentication is bypassed, and no proprietary systems are reverse-engineered.
254-
255243
## FAQ
256244

257245
<details>
258246
<summary><strong>Why passthrough mode instead of handling tools internally?</strong></summary>
259247

260-
Internal tool execution means the SDK handles everything — but the SDK only speaks Claude. If your agents are configured for GPT-5.4 or Gemini via oh-my-opencode, that routing gets lost. Passthrough mode preserves the full OpenCode agent pipeline, including multi-model routing, full system prompts, and agent lifecycle management.
248+
If the Agent SDK executes tools directly, everything runs through Claude. Any agent routing defined in OpenCode is bypassed. Passthrough mode just sends the tool calls to OpenCode to run.
249+
261250
</details>
262251

263252
<details>
264253
<summary><strong>Does this work without oh-my-opencode?</strong></summary>
265254

266-
Yes. Both modes work with native OpenCode (build + plan agents) and with any custom agents defined in your `opencode.json`. oh-my-opencode just adds more agents and model routing — the proxy handles whatever OpenCode sends.
255+
Yes. Both modes work with native OpenCode (build + plan agents) and with any custom agents defined in your `opencode.json`. oh-my-opencode just adds more agents and model routing. The proxy handles whatever OpenCode sends.
256+
267257
</details>
268258

269259
<details>
270-
<summary><strong>Why do I need ANTHROPIC_API_KEY=dummy?</strong></summary>
260+
<summary><strong>Why do I need `ANTHROPIC_API_KEY=dummy`?</strong></summary>
271261

272262
OpenCode requires an API key to be set, but the proxy never uses it. Authentication is handled by your `claude login` session through the Agent SDK.
263+
273264
</details>
274265

275266
<details>
276267
<summary><strong>What about rate limits?</strong></summary>
277268

278269
Your Claude Max subscription has its own usage limits. The proxy doesn't add any additional limits. Concurrent requests are supported.
270+
279271
</details>
280272

281273
<details>
282274
<summary><strong>Is my data sent anywhere else?</strong></summary>
283275

284276
No. The proxy runs locally. Requests go directly to Claude through the official SDK. In passthrough mode, tool execution happens in OpenCode on your machine.
277+
285278
</details>
286279

287280
<details>
288281
<summary><strong>Why does internal mode use MCP tools?</strong></summary>
289282

290283
The Claude Agent SDK uses different parameter names for tools than OpenCode (e.g., `file_path` vs `filePath`). Internal mode provides its own MCP tools with SDK-compatible parameter names. Passthrough mode doesn't need this since OpenCode handles tool execution directly.
284+
291285
</details>
292286

293287
## Troubleshooting
294288

295-
| Problem | Solution |
296-
|---------|----------|
297-
| "Authentication failed" | Run `claude login` to authenticate |
298-
| "Connection refused" | Make sure the proxy is running: `bun run proxy` |
299-
| "Port 3456 is already in use" | `kill $(lsof -ti :3456)` or use `CLAUDE_PROXY_PORT=4567` |
300-
| Title generation fails | Set `"small_model": "anthropic/claude-haiku-4-5"` in your OpenCode config |
289+
| Problem | Solution |
290+
| ----------------------------- | ------------------------------------------------------------------------- |
291+
| "Authentication failed" | Run `claude login` to authenticate |
292+
| "Connection refused" | Make sure the proxy is running: `bun run proxy` |
293+
| "Port 3456 is already in use" | `kill $(lsof -ti :3456)` or use `CLAUDE_PROXY_PORT=4567` |
294+
| Title generation fails | Set `"small_model": "anthropic/claude-haiku-4-5"` in your OpenCode config |
301295

302296
## Auto-start (macOS)
303297

@@ -331,23 +325,14 @@ EOF
331325
launchctl load ~/Library/LaunchAgents/com.claude-max-proxy.plist
332326
```
333327

334-
## Testing
328+
## Development
329+
330+
### Run tests
335331

336332
```bash
337333
bun test
338334
```
339335

340-
106 tests across 13 files covering:
341-
- Passthrough tool forwarding and tool_result acceptance
342-
- PreToolUse hook interception and agent name fuzzy matching
343-
- SDK agent definition extraction (native OpenCode + oh-my-opencode)
344-
- MCP tool filtering (internal mode)
345-
- Session resume (header-based and fingerprint-based)
346-
- Streaming message deduplication
347-
- Working directory propagation
348-
- Concurrent request handling
349-
- Error classification (auth, rate limit, billing, timeout)
350-
351336
### Health Endpoint
352337

353338
```bash
@@ -356,7 +341,7 @@ curl http://127.0.0.1:3456/health
356341

357342
Returns auth status, subscription type, and proxy mode. Use this to verify the proxy is running and authenticated before connecting OpenCode.
358343

359-
## Architecture
344+
### Architecture
360345

361346
```
362347
src/
@@ -368,24 +353,17 @@ src/
368353
├── mcpTools.ts # MCP tool definitions for internal mode (read, write, edit, bash, glob, grep)
369354
├── logger.ts # Structured logging with AsyncLocalStorage context
370355
├── plugin/
371-
│ └── claude-max-headers.ts # OpenCode plugin for session header injection
372-
└── __tests__/ # 106 tests across 13 files
373-
├── helpers.ts
374-
├── integration.test.ts
375-
├── proxy-agent-definitions.test.ts
376-
├── proxy-agent-fuzzy-match.test.ts
377-
├── proxy-error-handling.test.ts
378-
├── proxy-mcp-filtering.test.ts
379-
├── proxy-passthrough-concept.test.ts
380-
├── proxy-pretooluse-hook.test.ts
381-
├── proxy-session-resume.test.ts
382-
├── proxy-streaming-message.test.ts
383-
├── proxy-subagent-support.test.ts
384-
├── proxy-tool-forwarding.test.ts
385-
├── proxy-transparent-tools.test.ts
386-
└── proxy-working-directory.test.ts
356+
└── claude-max-headers.ts # OpenCode plugin for session header injection
387357
```
388358

359+
## Disclaimer
360+
361+
This project is an **unofficial wrapper** around Anthropic's publicly available [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk). It is not affiliated with, endorsed by, or supported by Anthropic.
362+
363+
**Use at your own risk.** The authors make no claims regarding compliance with Anthropic's Terms of Service. It is your responsibility to review and comply with [Anthropic's Terms of Service](https://www.anthropic.com/legal/consumer-terms) and [Authorized Usage Policy](https://www.anthropic.com/legal/aup). Terms may change at any time.
364+
365+
This project calls `query()` from Anthropic's public npm package using your own authenticated account. No API keys are intercepted, no authentication is bypassed, and no proprietary systems are reverse-engineered.
366+
389367
## License
390368

391369
MIT

0 commit comments

Comments
 (0)