{"id":47602641,"url":"https://github.com/rynfar/meridian","last_synced_at":"2026-04-08T01:01:37.262Z","repository":{"id":333762905,"uuid":"1138589051","full_name":"rynfar/meridian","owner":"rynfar","description":"Use your Claude Max subscription with OpenCode. Proxy that bridges Anthropic's official SDK to enable Claude Max in third-party tools.","archived":false,"fork":false,"pushed_at":"2026-04-03T00:15:56.000Z","size":662,"stargazers_count":522,"open_issues_count":14,"forks_count":80,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-03T01:39:59.749Z","etag":null,"topics":["ai-coding","aider","anthropic","bun","charmbracelet","claude","claude-agent-sdk","claude-max","cline","crush-cli","llm","opencode","opencode-plugin","proxy"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@rynfar/meridian","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rynfar.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-20T21:39:40.000Z","updated_at":"2026-04-03T01:36:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rynfar/meridian","commit_stats":null,"previous_names":["rynfar/opencode-claude-max-proxy","rynfar/meridian"],"tags_count":65,"template":false,"template_full_name":null,"purl":"pkg:github/rynfar/meridian","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rynfar%2Fmeridian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rynfar%2Fmeridian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rynfar%2Fmeridian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rynfar%2Fmeridian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rynfar","download_url":"https://codeload.github.com/rynfar/meridian/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rynfar%2Fmeridian/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31381009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T21:40:47.592Z","status":"ssl_error","status_checked_at":"2026-04-03T21:40:05.436Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-coding","aider","anthropic","bun","charmbracelet","claude","claude-agent-sdk","claude-max","cline","crush-cli","llm","opencode","opencode-plugin","proxy"],"created_at":"2026-04-01T18:56:58.246Z","updated_at":"2026-04-08T01:01:37.248Z","avatar_url":"https://github.com/rynfar.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/banner.svg\" alt=\"Meridian\" width=\"800\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/rynfar/meridian/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/rynfar/meridian?style=flat-square\u0026color=6366f1\u0026label=release\" alt=\"Release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@rynfar/meridian\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@rynfar/meridian?style=flat-square\u0026color=8b5cf6\u0026label=npm\" alt=\"npm\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-a78bfa?style=flat-square\" alt=\"Platform\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-c4b5fd?style=flat-square\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://discord.gg/7vNVFYBz\"\u003e\u003cimg src=\"https://img.shields.io/badge/discord-join-5865F2?style=flat-square\u0026logo=discord\u0026logoColor=white\" alt=\"Discord\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nMeridian bridges the Claude Code SDK to the standard Anthropic API. No OAuth interception. No binary patches. No hacks. Just pure, documented SDK calls. Any tool that speaks the Anthropic or OpenAI protocol — OpenCode, Crush, Cline, Aider, Pi, Droid, Open WebUI — connects to Meridian and gets Claude, with session management, streaming, and prompt caching handled natively by the SDK.\n\n\u003e [!NOTE]\n\u003e ### How Meridian works with Anthropic\n\u003e\n\u003e Meridian is built entirely on the [Claude Code SDK](https://docs.anthropic.com/en/docs/claude-code/sdk). Every request flows through `query()` — the same documented function Anthropic provides for programmatic access. No OAuth tokens are extracted, no binaries are patched, nothing is reverse-engineered.\n\u003e\n\u003e Because we use the SDK, Anthropic remains in full control of prompt caching, context window management, compaction, rate limiting, and authentication. Meridian doesn't bypass these mechanisms — it depends on them. Max subscription tokens flow through the correct channel, governed by the same guardrails Anthropic built into Claude Code.\n\u003e\n\u003e What Meridian adds is a **presentation and interoperability layer**. We translate Claude Code's output into the standard Anthropic API format so developers can connect the editors, terminals, and workflows they prefer. The SDK does the work; Meridian formats the result.\n\u003e\n\u003e If you're looking for a tool that circumvents usage limits or bypasses Anthropic's controls, this project is not for you. We play nice with the SDK because we believe that's how developers can continue to choose their own frontends while respecting Anthropic's platform.\n\n\u003e [!WARNING]\n\u003e ### Why Meridian does not support OpenClaw\n\u003e\n\u003e There is technically a way to make Meridian work with OpenClaw, but we're not interested in pursuing it.\n\u003e\n\u003e The reason Claude Max offers generous usage limits is because Anthropic can justify it through Claude Code — their harness, their optimizations, their control. OpenClaw blows through that with autonomous workflows that Anthropic has little ability to manage or optimize. Using Opus to check an email when a local model would handle it fine isn't efficient use — it's waste that degrades the plan for everyone.\n\u003e\n\u003e I built Meridian because I believe developers should have the right to use the frontend of their choice. But that right comes with a responsibility: don't wreck the subscription for the rest of us. Sloppy autonomous agents that burn through Claude Max tokens are directly counter-productive to developers like me who depend on the plan being sustainable.\n\u003e\n\u003e Meridian's philosophy is simple — play nice with the SDK, let Anthropic optimize how they see fit, and use the frontend you want within the constraints of Claude Code. OpenClaw is not just a frontend; it's an autonomous system that abuses the Max plan. We won't be supporting it.\n\n## Quick Start\n\n```bash\n# 1. Install\nnpm install -g @rynfar/meridian\n\n# 2. Authenticate (one time)\nclaude login\n\n# 3. Configure OpenCode plugin (one time — OpenCode users only)\nmeridian setup\n\n# 4. Start\nmeridian\n```\n\nMeridian runs on `http://127.0.0.1:3456`. Point any Anthropic-compatible tool at it:\n\n```bash\nANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode\n```\n\nThe API key value is a placeholder — Meridian authenticates through the Claude Code SDK, not API keys. Most Anthropic-compatible tools require this field to be set, but any value works.\n\n## Why Meridian?\n\nThe Claude Code SDK provides programmatic access to Claude. But your favorite coding tools expect an Anthropic API endpoint. Meridian bridges that gap — it runs locally, accepts standard API requests, and routes them through the SDK. Claude Code does the heavy lifting; Meridian translates the output.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/how-it-works.svg\" alt=\"How Meridian works\" width=\"920\"/\u003e\n\u003c/p\u003e\n\n## Features\n\n- **Standard Anthropic API** — drop-in compatible with any tool that supports a custom `base_url`\n- **OpenAI-compatible API** — `/v1/chat/completions` and `/v1/models` for tools that only speak the OpenAI protocol (Open WebUI, Continue, etc.) — no LiteLLM needed\n- **Session management** — conversations persist across requests, survive compaction and undo, resume after proxy restarts\n- **Streaming** — full SSE streaming with MCP tool filtering\n- **Concurrent sessions** — run parent and subagent requests in parallel\n- **Subagent model selection** — primary agents get 1M context; subagents get 200k, preserving rate-limit budget\n- **Auto token refresh** — expired OAuth tokens are refreshed automatically; requests continue without interruption\n- **Passthrough mode** — forward tool calls to the client instead of executing internally\n- **Multimodal** — images, documents, and file attachments pass through to Claude\n- **Multi-profile** — switch between Claude accounts instantly, no restart needed\n- **Telemetry dashboard** — real-time performance metrics at `/telemetry`, including token usage and prompt cache efficiency ([`MONITORING.md`](MONITORING.md))\n\n## Multi-Profile Support\n\nMeridian can route requests to different Claude accounts. Each **profile** is a named auth context — a separate Claude login with its own OAuth tokens. Switch between personal and work accounts, or share a single Meridian instance across teams.\n\n### Adding profiles\n\n```bash\n# Add your personal account\nmeridian profile add personal\n# → Opens browser for Claude login\n\n# Add your work account (sign out of claude.ai first, then sign into the work account)\nmeridian profile add work\n```\n\n\u003e **⚠ Important:** Claude's OAuth reuses your browser session. Before adding a second account, sign out of claude.ai and sign into the other account first.\n\n### Switching profiles\n\n```bash\n# CLI (while proxy is running)\nmeridian profile switch work\n\n# Per-request header (any agent)\ncurl -H \"x-meridian-profile: work\" ...\n```\n\nYou can also switch profiles from the web UI at `http://127.0.0.1:3456/profiles` — a dropdown appears in the nav bar on all pages when profiles are configured.\n\n### Profile commands\n\n| Command | Description |\n|---------|-------------|\n| `meridian profile add \u003cname\u003e` | Add a profile and authenticate via browser |\n| `meridian profile list` | List profiles and auth status |\n| `meridian profile switch \u003cname\u003e` | Switch the active profile (requires running proxy) |\n| `meridian profile login \u003cname\u003e` | Re-authenticate an expired profile |\n| `meridian profile remove \u003cname\u003e` | Remove a profile and its credentials |\n\n### How it works\n\nEach profile stores its credentials in an isolated `CLAUDE_CONFIG_DIR` under `~/.config/meridian/profiles/\u003cname\u003e/`. When a request arrives, Meridian resolves the profile in priority order:\n\n1. `x-meridian-profile` request header (per-request override)\n2. Active profile (set via `meridian profile switch` or the web UI)\n3. First configured profile\n\nSession state is scoped per profile — switching accounts won't cross-contaminate conversation history.\n\n### Environment variable configuration\n\nFor advanced setups (CI, Docker), profiles can also be provided via environment variable:\n\n```bash\nexport MERIDIAN_PROFILES='[{\"id\":\"personal\",\"claudeConfigDir\":\"/path/to/config1\"},{\"id\":\"work\",\"claudeConfigDir\":\"/path/to/config2\"}]'\nexport MERIDIAN_DEFAULT_PROFILE=personal\nmeridian\n```\n\nWhen `MERIDIAN_PROFILES` is set, it takes precedence over disk-configured profiles. When unset, Meridian auto-discovers profiles from `~/.config/meridian/profiles.json` on each request.\n\n## Agent Setup\n\n### OpenCode\n\n**Step 1: Run `meridian setup` (required, one time)**\n\n```bash\nmeridian setup\n```\n\nThis adds the Meridian plugin to your OpenCode global config (`~/.config/opencode/opencode.json`). The plugin enables:\n\n- **Session tracking** — reliable conversation continuity across requests\n- **Safe model defaults** — Opus uses 1M context (included with Max subscription); Sonnet uses 200k to avoid Extra Usage charges ([details](#extended-context-billing))\n- **Subagent model selection** — subagents automatically use `sonnet`/`opus` (200k), preserving rate-limit budget\n\nIf the plugin is missing, Meridian warns at startup and reports `\"plugin\": \"not-configured\"` in the health endpoint.\n\n**Step 2: Start**\n\n```bash\nANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode\n```\n\nOr set these in your shell profile so they're always active:\n\n```bash\nexport ANTHROPIC_API_KEY=x\nexport ANTHROPIC_BASE_URL=http://127.0.0.1:3456\n```\n\n### Crush\n\nAdd a provider to `~/.config/crush/crush.json`:\n\n```json\n{\n  \"providers\": {\n    \"meridian\": {\n      \"id\": \"meridian\",\n      \"name\": \"Meridian\",\n      \"type\": \"anthropic\",\n      \"base_url\": \"http://127.0.0.1:3456\",\n      \"api_key\": \"dummy\",\n      \"models\": [\n        { \"id\": \"claude-sonnet-4-6\", \"name\": \"Claude Sonnet 4.6 (1M)\", \"context_window\": 1000000, \"default_max_tokens\": 64000, \"can_reason\": true, \"supports_attachments\": true },\n        { \"id\": \"claude-opus-4-6\",   \"name\": \"Claude Opus 4.6 (1M)\",   \"context_window\": 1000000, \"default_max_tokens\": 32768, \"can_reason\": true, \"supports_attachments\": true },\n        { \"id\": \"claude-haiku-4-5-20251001\", \"name\": \"Claude Haiku 4.5\", \"context_window\": 200000, \"default_max_tokens\": 16384, \"can_reason\": true, \"supports_attachments\": true }\n      ]\n    }\n  }\n}\n```\n\n```bash\ncrush run --model meridian/claude-sonnet-4-6 \"refactor this function\"\ncrush --model meridian/claude-opus-4-6       # interactive TUI\n```\n\nCrush is automatically detected from its `Charm-Crush/` User-Agent — no plugin needed.\n\n### Droid (Factory AI)\n\nAdd Meridian as a custom model provider in `~/.factory/settings.json`:\n\n```json\n{\n  \"customModels\": [\n    { \"model\": \"claude-sonnet-4-6\",       \"name\": \"Sonnet 4.6 (Meridian)\", \"provider\": \"anthropic\", \"baseUrl\": \"http://127.0.0.1:3456\", \"apiKey\": \"x\" },\n    { \"model\": \"claude-opus-4-6\",         \"name\": \"Opus 4.6 (Meridian)\",   \"provider\": \"anthropic\", \"baseUrl\": \"http://127.0.0.1:3456\", \"apiKey\": \"x\" },\n    { \"model\": \"claude-haiku-4-5-20251001\", \"name\": \"Haiku 4.5 (Meridian)\", \"provider\": \"anthropic\", \"baseUrl\": \"http://127.0.0.1:3456\", \"apiKey\": \"x\" }\n  ]\n}\n```\n\nThen pick any `custom:claude-*` model in the Droid TUI. No plugin needed — Droid is automatically detected.\n\n### Cline\n\n**1. Authenticate:**\n\n```bash\ncline auth --provider anthropic --apikey \"dummy\" --modelid \"claude-sonnet-4-6\"\n```\n\n**2. Set the proxy URL** in `~/.cline/data/globalState.json`:\n\n```json\n{\n  \"anthropicBaseUrl\": \"http://127.0.0.1:3456\",\n  \"actModeApiProvider\": \"anthropic\",\n  \"actModeApiModelId\": \"claude-sonnet-4-6\"\n}\n```\n\n**3. Run:**\n\n```bash\ncline --yolo \"refactor the login function\"\n```\n\nNo plugin needed — Cline uses the standard Anthropic SDK.\n\n### Aider\n\n```bash\nANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 \\\n  aider --model anthropic/claude-sonnet-4-5-20250929\n```\n\n\u003e **Note:** `--no-stream` is incompatible due to a litellm parsing issue — use the default streaming mode.\n\n### OpenAI-compatible tools (Open WebUI, Continue, etc.)\n\nMeridian speaks the OpenAI protocol natively — no LiteLLM or translation proxy needed.\n\n**`POST /v1/chat/completions`** — accepts OpenAI chat format, returns OpenAI completion format (streaming and non-streaming)\n\n**`GET /v1/models`** — returns available Claude models in OpenAI format\n\nPoint any OpenAI-compatible tool at `http://127.0.0.1:3456` with any API key value:\n\n```bash\n# Open WebUI: set OpenAI API base to http://127.0.0.1:3456, API key to any value\n# Continue: set apiBase to http://127.0.0.1:3456 with provider: openai\n# Any OpenAI SDK: set base_url=\"http://127.0.0.1:3456\", api_key=\"dummy\"\n```\n\n\u003e **Note:** Multi-turn conversations work by packing prior turns into the system prompt. Each request is a fresh SDK session — OpenAI clients replay full history themselves and don't use Meridian's session resumption.\n\n### Pi\n\nPi uses the `@mariozechner/pi-ai` library which supports a configurable `baseUrl` on the model. Add a provider-level override in `~/.pi/agent/models.json`:\n\n```json\n{\n  \"anthropic\": {\n    \"baseUrl\": \"http://127.0.0.1:3456\"\n  }\n}\n```\n\nThen start Meridian with the pi default adapter:\n\n```bash\nMERIDIAN_DEFAULT_AGENT=pi meridian\n```\n\nPi mimics Claude Code's User-Agent, so automatic detection isn't possible. The `MERIDIAN_DEFAULT_AGENT` env var tells Meridian to use the pi adapter for all unrecognized requests. If you run other agents alongside pi, use the `x-meridian-agent: pi` header instead (requires pi-ai support for custom headers).\n\n### Any Anthropic-compatible tool\n\n```bash\nexport ANTHROPIC_API_KEY=x\nexport ANTHROPIC_BASE_URL=http://127.0.0.1:3456\n```\n\n## Tested Agents\n\n| Agent | Status | Notes |\n|-------|--------|-------|\n| [OpenCode](https://github.com/anomalyco/opencode) | ✅ Verified | Requires `meridian setup` — full tool support, session resume, streaming, subagents |\n| [Droid (Factory AI)](https://factory.ai/product/ide) | ✅ Verified | BYOK config (see above) — full tool support, session resume, streaming |\n| [Crush](https://github.com/charmbracelet/crush) | ✅ Verified | Provider config (see above) — full tool support, session resume, headless `crush run` |\n| [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see above) — full tool support, file read/write/edit, bash, session resume |\n| [Aider](https://github.com/paul-gauthier/aider) | ✅ Verified | Env vars — file editing, streaming; `--no-stream` broken (litellm bug) |\n| [Open WebUI](https://github.com/open-webui/open-webui) | ✅ Verified | OpenAI-compatible endpoints — set base URL to `http://127.0.0.1:3456` |\n| [Pi](https://github.com/mariozechner/pi-coding-agent) | ✅ Verified | models.json config (see above) — requires `MERIDIAN_DEFAULT_AGENT=pi` |\n| [Continue](https://github.com/continuedev/continue) | 🔲 Untested | OpenAI-compatible endpoints should work — set `apiBase` to `http://127.0.0.1:3456` |\n\nTested an agent or built a plugin? [Open an issue](https://github.com/rynfar/meridian/issues) and we'll add it.\n\n## Architecture\n\n```\nsrc/proxy/\n├── server.ts              ← HTTP orchestration (routes, SSE streaming, concurrency)\n├── adapter.ts             ← AgentAdapter interface\n├── adapters/\n│   ├── detect.ts          ← Agent detection from request headers\n│   ├── opencode.ts        ← OpenCode adapter\n│   ├── crush.ts           ← Crush adapter\n│   ├── droid.ts           ← Droid adapter\n│   ├── pi.ts              ← Pi adapter\n│   └── passthrough.ts     ← LiteLLM passthrough adapter\n├── query.ts               ← SDK query options builder\n├── errors.ts              ← Error classification\n├── models.ts              ← Model mapping (sonnet/opus/haiku, agentMode)\n├── tokenRefresh.ts        ← Cross-platform OAuth token refresh\n├── openai.ts              ← OpenAI ↔ Anthropic format translation (pure)\n├── setup.ts               ← OpenCode plugin configuration\n├── session/\n│   ├── lineage.ts         ← Per-message hashing, mutation classification (pure)\n│   ├── fingerprint.ts     ← Conversation fingerprinting\n│   └── cache.ts           ← LRU session caches\n├── profiles.ts            ← Multi-profile: resolve, list, switch auth contexts\n├── profileCli.ts          ← CLI commands for profile management\n├── sessionStore.ts        ← Cross-proxy file-based session persistence\n└── passthroughTools.ts    ← Tool forwarding mode\ntelemetry/\n├── ...\n├── profileBar.ts          ← Shared profile switcher bar\n└── profilePage.ts         ← Profile management page\nplugin/\n└── meridian.ts            ← OpenCode plugin (session headers + agent mode)\n```\n\n### Session Management\n\nEvery incoming request is classified:\n\n| Classification | What Happened | Action |\n|---------------|---------------|--------|\n| **Continuation** | New messages appended | Resume SDK session |\n| **Compaction** | Agent summarized old messages | Resume (suffix preserved) |\n| **Undo** | User rolled back messages | Fork at rollback point |\n| **Diverged** | Completely different conversation | Start fresh |\n\nSessions are stored in-memory (LRU) and persisted to `~/.cache/meridian/sessions.json` for cross-proxy resume.\n\n### Agent Detection\n\nAgents are identified from request headers automatically:\n\n| Signal | Adapter |\n|---|---|\n| `x-meridian-agent` header | Explicit override (any adapter) |\n| `Charm-Crush/` User-Agent | Crush |\n| `factory-cli/` User-Agent | Droid |\n| `litellm/` UA or `x-litellm-*` headers | LiteLLM passthrough |\n| *(anything else)* | `MERIDIAN_DEFAULT_AGENT` env var, or OpenCode |\n\n### Adding a New Agent\n\nImplement the `AgentAdapter` interface in `src/proxy/adapters/`. See [`adapters/opencode.ts`](src/proxy/adapters/opencode.ts) for a reference.\n\n## Configuration\n\n| Variable | Alias | Default | Description |\n|----------|-------|---------|-------------|\n| `MERIDIAN_PORT` | `CLAUDE_PROXY_PORT` | `3456` | Port to listen on |\n| `MERIDIAN_HOST` | `CLAUDE_PROXY_HOST` | `127.0.0.1` | Host to bind to |\n| `MERIDIAN_PASSTHROUGH` | `CLAUDE_PROXY_PASSTHROUGH` | unset | Forward tool calls to client instead of executing |\n| `MERIDIAN_MAX_CONCURRENT` | `CLAUDE_PROXY_MAX_CONCURRENT` | `10` | Maximum concurrent SDK sessions |\n| `MERIDIAN_MAX_SESSIONS` | `CLAUDE_PROXY_MAX_SESSIONS` | `1000` | In-memory LRU session cache size |\n| `MERIDIAN_MAX_STORED_SESSIONS` | `CLAUDE_PROXY_MAX_STORED_SESSIONS` | `10000` | File-based session store capacity |\n| `MERIDIAN_WORKDIR` | `CLAUDE_PROXY_WORKDIR` | `cwd()` | Default working directory for SDK |\n| `MERIDIAN_IDLE_TIMEOUT_SECONDS` | `CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS` | `120` | HTTP keep-alive timeout |\n| `MERIDIAN_TELEMETRY_SIZE` | `CLAUDE_PROXY_TELEMETRY_SIZE` | `1000` | Telemetry ring buffer size |\n| `MERIDIAN_NO_FILE_CHANGES` | `CLAUDE_PROXY_NO_FILE_CHANGES` | unset | Disable \"Files changed\" summary in responses |\n| `MERIDIAN_SONNET_MODEL` | `CLAUDE_PROXY_SONNET_MODEL` | `sonnet` | Sonnet context tier: `sonnet` (200k, default) or `sonnet[1m]` (1M, requires Extra Usage†) |\n| `MERIDIAN_DEFAULT_AGENT` | — | `opencode` | Default adapter for unrecognized agents: `opencode`, `pi`, `crush`, `droid`, `passthrough`. Requires restart. |\n| `MERIDIAN_PROFILES` | — | unset | JSON array of profile configs (overrides disk discovery). See [Multi-Profile Support](#multi-profile-support). |\n| `MERIDIAN_DEFAULT_PROFILE` | — | *(first profile)* | Default profile ID when no header is sent |\n\n†Sonnet 1M requires Extra Usage on all plans including Max ([docs](https://code.claude.com/docs/en/model-config#extended-context)). Opus 1M is included with Max/Team/Enterprise at no extra cost.\n\n## Endpoints\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /` | Landing page |\n| `POST /v1/messages` | Anthropic Messages API |\n| `POST /messages` | Alias for `/v1/messages` |\n| `POST /v1/chat/completions` | OpenAI-compatible chat completions |\n| `GET /v1/models` | OpenAI-compatible model list |\n| `GET /health` | Auth status, mode, plugin status |\n| `POST /auth/refresh` | Manually refresh the OAuth token |\n| `GET /telemetry` | Performance dashboard |\n| `GET /telemetry/requests` | Recent request metrics (JSON) |\n| `GET /telemetry/summary` | Aggregate statistics (JSON) |\n| `GET /telemetry/logs` | Diagnostic logs (JSON) |\n| `GET /profiles` | Profile management page |\n| `GET /profiles/list` | List profiles with auth status (JSON) |\n| `POST /profiles/active` | Switch the active profile |\n\nHealth response example:\n\n```json\n{\n  \"status\": \"healthy\",\n  \"auth\": { \"loggedIn\": true, \"email\": \"you@example.com\", \"subscriptionType\": \"max\" },\n  \"mode\": \"internal\",\n  \"plugin\": { \"opencode\": \"configured\" }\n}\n```\n\n`plugin.opencode` is `\"configured\"` when `meridian setup` has been run, `\"not-configured\"` otherwise.\n\n## CLI Commands\n\n| Command | Description |\n|---------|-------------|\n| `meridian` | Start the proxy server |\n| `meridian setup` | Configure the OpenCode plugin in `~/.config/opencode/opencode.json` |\n| `meridian profile add \u003cname\u003e` | Add a profile and authenticate via browser |\n| `meridian profile list` | List all profiles and their auth status |\n| `meridian profile switch \u003cname\u003e` | Switch the active profile (requires running proxy) |\n| `meridian profile login \u003cname\u003e` | Re-authenticate an expired profile |\n| `meridian profile remove \u003cname\u003e` | Remove a profile and its credentials |\n| `meridian refresh-token` | Manually refresh the Claude OAuth token (exits 0/1) |\n\n## Programmatic API\n\n```typescript\nimport { startProxyServer } from \"@rynfar/meridian\"\n\nconst instance = await startProxyServer({\n  port: 3456,\n  host: \"127.0.0.1\",\n  silent: true,\n})\n\n// instance.server — underlying http.Server\nawait instance.close()\n```\n\n## Docker\n\n```bash\ndocker run -v ~/.claude:/home/claude/.claude -p 3456:3456 meridian\n```\n\n## Testing\n\n```bash\nnpm test       # unit + integration tests\nnpm run build  # build with bun + tsc\n```\n\n| Tier | What | Speed |\n|------|------|-------|\n| Unit | Pure functions, no mocks | Fast |\n| Integration | HTTP layer with mocked SDK | Fast |\n| E2E | Real proxy + real Claude Max ([`E2E.md`](E2E.md)) | Manual |\n\n## FAQ\n\n**Is this allowed by Anthropic's terms?**\nMeridian uses the official Claude Code SDK — the same SDK Anthropic publishes and documents for programmatic access. It does not intercept credentials, modify binaries, or bypass any authentication. All requests flow through the SDK's own authentication and rate-limiting mechanisms.\n\n**How is this different from using an API key?**\nAPI keys provide direct API access billed per token. Claude Max includes programmatic access through the Claude Code SDK. Meridian translates SDK responses into the standard Anthropic API format, allowing compatible tools to connect through Claude Code.\n\n**What happens if my OAuth token expires?**\nTokens expire roughly every 8 hours. Meridian detects the expiry, refreshes the token automatically, and retries the request — so requests continue transparently. If the refresh fails (e.g. the refresh token has expired after weeks of inactivity), Meridian returns a clear error telling you to run `claude login`.\n\n**Can I trigger a token refresh manually?**\n\n```bash\n# CLI — works whether the proxy is running or not\nmeridian refresh-token\n\n# HTTP — while the proxy is running\ncurl -X POST http://127.0.0.1:3456/auth/refresh\n```\n\n**I'm hitting rate limits on 1M context. What do I do?**\nMeridian defaults Sonnet to 200k context because Sonnet 1M is always billed as Extra Usage on Max plans — even when regular usage isn't exhausted. This is [Anthropic's intended billing model](https://code.claude.com/docs/en/model-config#extended-context), not a bug. Set `MERIDIAN_SONNET_MODEL=sonnet[1m]` to opt in if you have Extra Usage enabled and understand the billing implications. Opus defaults to 1M context, which is included with Max/Team/Enterprise subscriptions at no extra cost. Note: there is a [known upstream bug](https://github.com/anthropics/claude-code/issues/39841) where Claude Code incorrectly gates Opus 1M behind Extra Usage on Max — this is Anthropic's to fix.\n\n**Why does the health endpoint show `\"plugin\": \"not-configured\"`?**\nYou haven't run `meridian setup`. Without the plugin, OpenCode requests won't have session tracking or subagent model selection. Run `meridian setup` and restart OpenCode.\n\n## Contributing\n\nIssues and PRs welcome. Join the [Discord](https://discord.gg/7vNVFYBz) to discuss ideas before opening issues. See [`ARCHITECTURE.md`](ARCHITECTURE.md) for module structure and dependency rules, [`CLAUDE.md`](CLAUDE.md) for coding guidelines, [`E2E.md`](E2E.md) for end-to-end test procedures, and [`MONITORING.md`](MONITORING.md) for understanding token usage and prompt cache health.\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frynfar%2Fmeridian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frynfar%2Fmeridian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frynfar%2Fmeridian/lists"}