Meridian turns your Claude Max subscription into a local Anthropic API. Any tool that speaks the Anthropic protocol — OpenCode, Crush, Cline, Continue, Aider — connects to Meridian and gets Claude, powered by your existing subscription through the official Claude Code SDK.
Harness Claude, your way.
Important
🚀 This project is becoming Meridian. The npm package will be renamed from opencode-claude-max-proxy to meridian in an upcoming release. Your current install and all existing configurations will continue to work — no action needed yet.
# Install
npm install -g opencode-claude-max-proxy
# Authenticate (one time)
claude login
# Start
meridianMeridian starts on http://127.0.0.1:3456. Point any Anthropic-compatible tool at it:
ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencodeThe API key value doesn't matter — Meridian authenticates through your Claude Max session, not API keys.
You're paying for Claude Max. It includes programmatic access through the Claude Code SDK. But your favorite coding tools expect an Anthropic API endpoint and an API key.
Meridian bridges that gap. It runs locally, accepts standard Anthropic API requests, and routes them through the SDK using your Max subscription. Claude does the work — Meridian just lets you pick the tool.
- Standard Anthropic API — drop-in compatible with any tool that supports custom
base_url - Session management — conversations persist across requests, survive compaction and undo, resume after proxy restarts
- Streaming — full SSE streaming with MCP tool filtering
- Concurrent sessions — run parent + subagent requests in parallel
- Passthrough mode — forward tool calls to the client instead of executing internally
- Multimodal — images, documents, and file attachments pass through to Claude
- Telemetry dashboard — real-time performance metrics at
/telemetry - Cross-proxy resume — sessions persist to disk and survive restarts
- Agent adapter pattern — extensible architecture for supporting new agent protocols
ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencodeFor automatic session tracking, use a plugin like opencode-meridian, or see the reference plugin to build your own.
export ANTHROPIC_API_KEY=x
export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
# Then start your tool normally| Agent | Status | Plugin | Notes |
|---|---|---|---|
| OpenCode | ✅ Verified | opencode-meridian | Full tool support, session resume, streaming, subagents |
| Crush | ✅ Verified | — | Tool execution, multi-turn, headless mode |
| Cline | 🔲 Untested | — | Should work — standard Anthropic API |
| Continue | 🔲 Untested | — | Should work — standard Anthropic API |
| Aider | 🔲 Untested | — | Should work — standard Anthropic API |
Tested an agent or built a plugin? Open an issue and we'll add it.
Meridian is built as a modular proxy with clean separation of concerns:
src/proxy/
├── server.ts ← HTTP orchestration (routes, SSE streaming, concurrency)
├── adapter.ts ← AgentAdapter interface (extensibility point)
├── adapters/opencode.ts ← OpenCode-specific behavior
├── query.ts ← SDK query options builder
├── errors.ts ← Error classification
├── models.ts ← Model mapping (sonnet/opus/haiku)
├── tools.ts ← Tool blocking lists
├── messages.ts ← Content normalization
├── session/
│ ├── lineage.ts ← Per-message hashing, mutation classification (pure)
│ ├── fingerprint.ts ← Conversation fingerprinting
│ └── cache.ts ← LRU session caches
├── sessionStore.ts ← Cross-proxy file-based session persistence
├── agentDefs.ts ← Subagent definition extraction
└── passthroughTools.ts ← Tool forwarding mode
Sessions map agent conversations to Claude SDK sessions. Meridian classifies every incoming request:
| Classification | What Happened | Action |
|---|---|---|
| Continuation | New messages appended | Resume SDK session |
| Compaction | Agent summarized old messages | Resume (suffix preserved) |
| Undo | User rolled back messages | Fork at rollback point |
| Diverged | Completely different conversation | Start fresh |
Sessions are stored in-memory (LRU) and persisted to ~/.cache/opencode-claude-max-proxy/sessions.json for cross-proxy resume.
Implement the AgentAdapter interface in src/proxy/adapters/:
interface AgentAdapter {
getSessionId(c: Context): string | undefined
extractWorkingDirectory(body: any): string | undefined
normalizeContent(content: any): string
getBlockedBuiltinTools(): readonly string[]
getAgentIncompatibleTools(): readonly string[]
getMcpServerName(): string
getAllowedMcpTools(): readonly string[]
}See adapters/opencode.ts for reference.
| Variable | Default | Description |
|---|---|---|
CLAUDE_PROXY_PORT |
3456 |
Port to listen on |
CLAUDE_PROXY_HOST |
127.0.0.1 |
Host to bind to |
CLAUDE_PROXY_PASSTHROUGH |
unset | Forward tool calls to client instead of executing |
CLAUDE_PROXY_MAX_CONCURRENT |
10 |
Maximum concurrent SDK sessions |
CLAUDE_PROXY_MAX_SESSIONS |
1000 |
In-memory LRU session cache size |
CLAUDE_PROXY_MAX_STORED_SESSIONS |
10000 |
File-based session store capacity |
CLAUDE_PROXY_WORKDIR |
cwd() |
Default working directory for SDK |
CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS |
120 |
HTTP keep-alive timeout |
CLAUDE_PROXY_TELEMETRY_SIZE |
1000 |
Telemetry ring buffer size |
Meridian can be used as a library for building agent plugins and integrations.
import { startProxyServer } from "opencode-claude-max-proxy"
// Start a proxy instance
const instance = await startProxyServer({
port: 3456,
host: "127.0.0.1",
silent: true, // suppress console output
})
// instance.config — resolved ProxyConfig
// instance.server — underlying http.Server
// Shut down cleanly
await instance.close()For reliable session tracking, agents should send a session identifier via HTTP header. Without it, the proxy falls back to fingerprint-based matching (hashing the first user message + working directory), which is less reliable.
| Header | Purpose |
|---|---|
x-opencode-session |
Maps agent conversations to Claude SDK sessions for resume, undo, and compaction |
The proxy uses this header to maintain conversation continuity across requests. Plugin authors should inject it on every request to /v1/messages.
Meridian is the proxy. Plugins live in the agent's ecosystem.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Agent │ HTTP │ Meridian │ SDK │ Claude Max │
│ (OpenCode, │────────▶│ Proxy │────────▶│ │
│ Crush, etc) │◀────────│ │◀────────│ │
└──────────────┘ └──────────────┘ └──────────────┘
│
│ plugin injects headers,
│ manages proxy lifecycle
│
┌──────────────┐
│ Agent Plugin │
│ (optional) │
└──────────────┘
A plugin's job is to:
- Start/stop a Meridian instance (
startProxyServer/instance.close()) - Inject session headers into outgoing requests
- Check proxy health (
GET /health)
See examples/opencode-plugin/ for a reference implementation.
| Endpoint | Description |
|---|---|
GET / |
Landing page (HTML) or status JSON (Accept: application/json) |
POST /v1/messages |
Anthropic Messages API |
POST /messages |
Alias for /v1/messages |
GET /health |
Auth status, subscription type, mode |
GET /telemetry |
Performance dashboard |
GET /telemetry/requests |
Recent request metrics (JSON) |
GET /telemetry/summary |
Aggregate statistics (JSON) |
GET /telemetry/logs |
Diagnostic logs (JSON) |
docker run -v ~/.claude:/home/claude/.claude -p 3456:3456 meridianOr with docker-compose:
docker compose up -dnpm test # 339 unit/integration tests (bun test)
npm run build # Build with bun + tscThree test tiers:
| Tier | What | Speed |
|---|---|---|
| Unit | Pure functions, no mocks | Fast |
| Integration | HTTP layer with mocked SDK | Fast |
| E2E | Real proxy + real Claude Max (E2E.md) |
Manual |
Is this allowed by Anthropic's terms? Meridian uses the official Claude Code SDK — the same SDK Anthropic publishes and maintains for programmatic access. It authenticates through your existing Claude Max session using OAuth, not API keys. Nothing is modified, reverse-engineered, or bypassed.
How is this different from using an API key? API keys are billed per token. Your Max subscription is a flat monthly fee with higher rate limits. Meridian lets you use that subscription from any compatible tool.
Does it work with Claude Pro? It works with any Claude subscription that supports the Claude Code SDK. Max is recommended for the best rate limits.
What happens if my session expires?
The SDK handles token refresh automatically. If it can't refresh, Meridian returns a clear error telling you to run claude login.
Issues and PRs welcome. See ARCHITECTURE.md for module structure and dependency rules, CLAUDE.md for coding guidelines, and E2E.md for end-to-end test procedures.
MIT