Skip to content

winglock/omo_proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

omo_proxy

OpenCode (and other Anthropic-compatible clients) → Claude Code OAuth proxy with proper prompt-cache preservation.

OpenCode → omo_proxy (127.0.0.1:34156) → api.anthropic.com
                ↑
   reuses ~/.claude/.credentials.json (Claude Max subscription)

Why this exists

The Anthropic Messages API has prompt caching that can give a ~9x cost reduction on long contexts (cache_read is 0.1x the input price). A naive proxy that rewrites or shuffles the request body (billing headers, session ids, cache_control markers) silently destroys cache prefixes and forces every request to be billed at full price. On a Claude Max plan that drains the rate limit in hours.

This proxy specifically preserves prompt caching while still authenticating against Claude Code's first-party OAuth token.

Features

  • Stable cache prefix. Billing/session strings persisted in proxy-state.json survive process restarts, so previously written cache entries can still be reused.
  • cache_control cap = 4 (Anthropic max), not 1. When trimming is needed, keeps the tools and system anchors plus the latest messages markers.
  • Stable metadata.user_id. Caller-supplied volatile fields are dropped.
  • OAuth token persistence. Refreshed tokens are written back to ~/.claude/.credentials.json, so a proxy restart never invalidates the refresh chain.
  • Tool name normalization. OpenCode lowercase tools are mapped to Claude Code's CapitalCase set; MCP tools (mcp__*) pass through.
  • Usage logging. Every request prints a [usage] line including cache_read, cache_create, cache_create_5m, cache_create_1h, total_in, etc.
  • Hardened launcher. ensure-claude-proxy.ps1 checks the exact proxy.js is the listener, not just "something on port 34156".

Verified cache behavior

Log excerpt from a real session (224k token system+tools prefix):

#1: in=6  total_in=224281  out=1060  cache_read=0       cache_create=224275
#2: in=6  total_in=225387  out=467   cache_read=224275  cache_create=1106
#3: in=1  total_in=226023  out=517   cache_read=225381  cache_create=641

→ From request #2 onward Anthropic returns the entire prefix as cache_read, billing at 0.1x. Cost on the same conversation drops by ~9x compared to the broken-cache baseline.

Layout

omo_proxy/
├── proxy.js                    main Node.js proxy (804 lines)
├── start-proxy.cmd             foreground launcher (Windows)
├── opencode-cc.cmd             one-shot env wrapper (cmd)
├── opencode-cc.ps1             one-shot env wrapper (PowerShell)
├── shims/
│   ├── opencode.cmd            transparent shim for cmd / PowerShell
│   ├── opencode                transparent shim for Git Bash / WSL
│   └── ensure-claude-proxy.ps1 helper that ensures the right proxy.js is running
└── go/                         legacy Go implementation (not maintained)

Requirements

  • Node.js 18+ (uses built-in http, https, crypto, fs).
  • Claude Code installed and logged in (~/.claude/.credentials.json must exist with a valid claudeAiOauth block).
  • A Claude subscription that includes the model you select in OpenCode (e.g. Max for Opus).

Quick start (Windows)

git clone https://github.com/winglock/omo_proxy "$env:USERPROFILE\omo_proxy"

# (option A) just run the proxy in a foreground window
cd "$env:USERPROFILE\omo_proxy"
node proxy.js

# (option B) install the transparent opencode shim
Copy-Item "$env:USERPROFILE\omo_proxy\shims\opencode.cmd" "$env:USERPROFILE\bin\"
Copy-Item "$env:USERPROFILE\omo_proxy\shims\ensure-claude-proxy.ps1" "$env:USERPROFILE\bin\"
# make sure $env:USERPROFILE\bin is ahead of npm's path so 'opencode' resolves to the shim

If you keep the repo somewhere other than ~/omo_proxy, set:

setx OMO_PROXY_SCRIPT "C:\path\to\omo_proxy\proxy.js"

Quick start (macOS/Linux)

The Node.js proxy works on any platform. macOS users get the bonus that Claude Code stores credentials in Keychain too — currently only the Go variant reads Keychain; on macOS you'll either symlink the credentials JSON into ~/.claude/.credentials.json or extend proxy.js's TokenManager to call security find-generic-password.

git clone https://github.com/winglock/omo_proxy ~/omo_proxy
cd ~/omo_proxy
node proxy.js &

ANTHROPIC_BASE_URL=http://127.0.0.1:34156/v1 \
ANTHROPIC_API_KEY=proxy-placeholder \
opencode

OpenCode config snippet

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "anthropic": {
      "options": {
        "baseURL": "http://127.0.0.1:34156/v1",
        "apiKey": "proxy-placeholder"
      }
    }
  }
}

Verifying the cache is alive

After a few requests:

Select-String -Path ".\proxy.log" -Pattern "\[usage\]" | Select-Object -Last 10

Healthy pattern: cache_read is roughly equal to total_in from the second request onward, and cache_create is small.

If cache_read=0 on every request, something downstream of this proxy is mutating the prompt prefix per call. Check your client first; this proxy stabilizes the parts it controls.

Tool filtering

Anthropic's classifier rejects unknown tool names (treats them as third-party). This proxy keeps only Claude Code's canonical set plus MCP passthrough:

kept: Bash, Read, Glob, Grep, Edit, Write, Task, TodoWrite, WebFetch, WebSearch, Skill, BashOutput, NotebookEdit, mcp__*

dropped: LSP tools (lsp_*), ast_grep_*, session_*, background_cancel, look_at, skill_mcp, websearch_web_search_exa, grep_app_searchGitHub, context7_*, etc. The dropped tools simply aren't available to the Claude model — non-Anthropic providers in the same OpenCode session keep all of them.

Endpoints

Path Behavior
GET /health Local-only check. Returns 200 {"status":"ok"} if the loaded token is more than 5 minutes from expiry, 503 {"status":"token_refresh_required"} otherwise. Does not call upstream.
POST /v1/messages* Transformed and forwarded. Streaming SSE supported.
anything else under /v1/ Auth-rewritten and forwarded as-is.

Files this proxy reads/writes

Path Direction Contents
~/.claude/.credentials.json read + write Claude Code OAuth tokens. Updated on refresh.
./proxy-state.json created on first run Persisted billing lines + session id used to keep cache prefix byte-identical across restarts. Do not commit.
./proxy.log append Request/usage log; rotated at 10 MB.

Disclaimer

This is a personal-use local proxy for an individual subscriber's own first-party OAuth token. It is not affiliated with Anthropic or OpenCode. Don't use it to share credentials, redistribute access, or anything that violates the Claude Code TOS.

License

MIT

About

ziyeah

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors