@QingQ77: A high-performance API proxy bridging the OpenCode Zen protocol to Anthropic/OpenAI-compatible format, enabling tools like Claude Code and Codex CLI to transparently use domestic models. https://github.com/Kiowx/ope…

X AI KOLs Timeline Tools

Summary

opencode-cc is a high-performance Go API proxy that bridges the Anthropic/OpenAI-compatible protocol to the OpenCode Zen protocol, enabling tools such as Claude Code and Codex CLI to transparently use domestic large models including GLM, Kimi, DeepSeek, and Qwen. It supports automatic protocol routing, tool calling, web control panel, and other features.

A high-performance API proxy bridging the OpenCode Zen protocol to the Anthropic/OpenAI-compatible format, enabling tools like Claude Code and Codex CLI to transparently use domestic models. https://t.co/MvxRMNiMKa https://t.co/9wBWNhzmVE
Original Article
View Cached Full Text

Cached at: 07/03/26, 06:31 AM

A high-performance API proxy bridging OpenCode Zen protocol to Anthropic/OpenAI compatible format, enabling tools like Claude Code and Codex CLI to transparently use domestic models. https://t.co/MvxRMNiMKa https://t.co/9wBWNhzmVE — # Kiowx/opencode-cc Source: https://github.com/Kiowx/opencode-cc # opencode-cc 简体中文 | English > Anthropic / OpenAI → OpenCode Zen multi-protocol bridge proxy with embedded web dashboard. opencode-cc is a high-performance Go proxy that exposes Anthropic Messages API and OpenAI Chat Completions API (both streaming and non-streaming) and forwards requests to the OpenCode Zen [https://opencode.ai/docs/zen/] native protocol. Zen hosts 49 models across 4 protocols—this proxy automatically routes to the correct protocol translator based on target model id, allowing Claude Code to transparently run on models like GLM / Kimi / DeepSeek / Qwen / Claude / GPT / Gemini. Friend link: linuxdo [https://linux.do/] ┌────────────┐ POST /v1/messages ┌──────────────┐ route by model ↓ ┌──────────┐ │ Claude Code│ ───────────────────> │ opencode-cc │ ──────────────────────> │ OpenCode │ │ │ <── Anthropic SSE ── │ (Go) │ <── per-protocol SSE ─── │ Zen │ └────────────┘ └──────────────┘ └──────────┘ Embedded React dashboard ▲ /api/* SQLite (pure Go, no CGO) ## 4 Protocol Auto-Routing Zen is not a single OpenAI-compatible endpoint—it has 4 paths based on model source. The proxy selects automatically based on model id prefix: | Protocol | Path | Applicable Models | Translation Method | |–––––|——|——————|––––––––––| | OpenAI | /v1/chat/completions | GLM, Kimi, DeepSeek, MiniMax, MiMo, Grok, free models | Anthropic ↔ OpenAI bidirectional translation | | Anthropic | /v1/messages | Claude, Qwen | Nearly passthrough (only model id rewrite) | | Responses | /v1/responses | GPT family | Anthropic ↔ OpenAI Responses API | | Google | /v1beta/models/{id} | Gemini | Anthropic ↔ Google Generative Language | Each protocol implements the upstream.Protocol interface’s TranslateRequest / TranslateResponse / TranslateStream methods. ## Features - Full tool calling support. Anthropic tool_use / tool_result blocks are bidirectionally translated to tool_calls / role:"tool" messages under OpenAI protocol; Claude Code’s tool definitions are converted to OpenAI function tools. The reasoning_content extension field from domestic models is translated to Anthropic thinking blocks. - 4 protocol auto-routing. Based on model id prefix (claude-/qwen → Anthropic, gpt- → Responses, gemini- → Google, rest → OpenAI), no manual configuration needed. - OpenAI client compatible. Supports POST /v1/chat/completions and OpenAI-format /v1/models, can be directly used with OpenAI SDK, Cherry Studio, ChatBox and other compatible clients. The OpenAI path directly passes through the native JSON/SSE response from Zen /go/v1/chat/completions, without converting to Anthropic format. - Codex CLI compatible. Provides POST /v1/responses, converting Responses API requests, streaming text events and function calls used by Codex to Zen; OpenAI-compatible models go through /go/v1/chat/completions, while Claude/Qwen target models can go through native /v1/messages. - Anthropic smart native routing. native_anthropic is enabled by default. When the target model is claude-* / qwen*, it directly connects to the upstream /v1/messages and preserves cache_control, Anthropic native SSE and other extension fields; GLM, DeepSeek, Kimi and other target models continue using the conversion mode. - 49 preset model catalog. The Models page shows all models’ pricing, context, capability tags, protocol badges; the Config page allows one-click model selection and mapping from the catalog. - Single static binary. React SPA is embedded via embed.FS, no Node at runtime; SQLite uses pure Go driver, no CGO, clean cross-compilation possible. - Web dashboard. Dashboard (traffic graph, health status, model distribution), Models (model library browsing + filtering), Inspector (real-time request list, showing which protocol used), Config (Zen config, proxy authentication, model mappings—hot-reloaded). - Dashboard password protection. After setting panel_token in config.json or the Settings page, accessing the web dashboard requires password verification via a login page. After login, a 24-hour HttpOnly session cookie maintains the session; supports one-click logout from the sidebar. When panel_token is not set, the dashboard remains open (suitable for local single-machine use). - Client API Key management. Create, disable, and delete client keys from the dashboard, and set total token quota, daily token limit, and allowed IPs for each key; usage is automatically tallied. - Secure by default. Constant-time Bearer token authentication, request body size limits, per-request panic recovery, graceful shutdown. ## Quick Start ### Prerequisites - Go 1.22+ - Node 20+ (only needed for building UI; not at runtime) - An OpenCode Zen API key—login at opencode.ai/auth [https://opencode.ai/auth], add billing info, copy API key ### Build and Run bash make # build frontend + Go binary -> ./opencode-cc ./opencode-cc # starts on :8787, auto-creates config.json and data/opencode-cc.db Open the dashboard at http://localhost:8787/, go to the Config tab: 1. Fill in your API key in the “Upstream (OpenCode Zen)” card 2. Click “Test connection” to verify connectivity 3. Adjust the mapping from Claude Code model names to Zen models in “Model mappings” (default includes several cost-saving combos, e.g., claude-sonnet-4-5 → glm-5.1) Then point Claude Code: bash export ANTHROPIC_BASE_URL=http://localhost:8787 export ANTHROPIC_AUTH_TOKEN=local # any value works if no token set claude If your upstream Base URL supports Anthropic Messages (e.g., /v1/messages), the Anthropic smart native routing is used by default. You can also explicitly set: bash export OPENCODE_CC_UPSTREAM=https://opencode.ai/zen/go export OPENCODE_CC_NATIVE_ANTHROPIC=true When enabled, the proxy first resolves the target model via the mapping table: claude-* / qwen* target models directly connect to the upstream /v1/messages, only rewriting model; other target models are still converted to OpenAI Chat Completions. For example, in the configuration above, Claude/Qwen target models will be forwarded to https://opencode.ai/zen/go/v1/messages. The native path sends both Authorization: Bearer <key> and x-api-key: <key> headers, compatible with both OpenAI and Anthropic authentication styles. When using OpenAI SDK or compatible clients, set the Base URL to http://localhost:8787/v1: > The client’s interface type must be set to OpenAI. If the request path is /v1/messages, it means the client is still in Anthropic mode, > and the response will be converted according to Anthropic Messages protocol, not native OpenAI passthrough. bash export OPENAI_BASE_URL=http://localhost:8787/v1 export OPENAI_API_KEY=local # change to an API Key created in the dashboard if client auth is enabled bash curl http://localhost:8787/v1/chat/completions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"glm-5.1","messages":[{"role":"user","content":"你好"}]}' ### Integrate with Codex CLI Codex custom model provider configuration must be written to the user-level config file: - Linux/macOS: ~/.codex/config.toml - Windows: %USERPROFILE%\.codex\config.toml toml model = "kimi-k2.7-code" model_provider = "opencode_cc" [model_providers.opencode_cc] name = "opencode-cc" base_url = "http://localhost:8787/v1" env_key = "OPENCODE_CC_API_KEY" wire_api = "responses" Set a client key before starting Codex. Any non-empty value can be used if client API Key auth is not enabled; after enabling, use a key created in the dashboard: bash export OPENCODE_CC_API_KEY=local codex PowerShell: powershell $env:OPENCODE_CC_API_KEY = "local" codex model can be a Zen model ID directly, or a mapping alias configured in the dashboard. Recommended coding model: kimi-k2.7-code. If the model maps to claude-* / qwen*, Codex’s /v1/responses requests are converted to native Anthropic Messages upstream requests; other models continue to be converted to OpenAI Chat Completions. ### Development Mode (HMR Hot Reload) bash make dev # Vite on :5174 + Go on :8787, API proxied through Vite ### Docker Images published to GitHub Container Registry: ghcr.io/kiowx/opencode-cc. One-command run (no need to clone the repo; replace API key and dashboard password): bash docker run -d --name opencode-cc --restart unless-stopped \ -p 8787:8787 \ -v opencode-cc-data:/data \ -e ZEN_API_KEY=sk-your-key \ -e OPENCODE_CC_PANEL_TOKEN=your-dashboard-password \ ghcr.io/kiowx/opencode-cc:latest After startup, visit http://server-ip:8787, log in using OPENCODE_CC_PANEL_TOKEN. The named volume opencode-cc-data persists configuration and SQLite data. It is strongly recommended to set a dashboard password when exposing the port externally, otherwise anyone who can reach the address can open the dashboard. The docker-compose.yml in the repo provides the same persistent configuration: bash docker compose up -d Update to the latest version: bash docker compose pull docker compose up -d docker image prune -f When deploying with docker run, the update command is: bash docker pull ghcr.io/kiowx/opencode-cc:latest docker rm -f opencode-cc # then re-run the one-command run command above You can also pin the image tag from latest to e.g., 1.3.0. To build locally: bash docker build -t opencode-cc . docker run -d --name opencode-cc -p 8787:8787 -v opencode-cc-data:/data opencode-cc Every time a v* tag is pushed, GitHub Actions automatically publishes Linux amd64/arm64 images and updates the version, minor version, and latest tags. After the first GHCR package publication, you need to set the visibility to Public in the GitHub Packages settings for anonymous pulls. ## Translation Principles ### Request Routing When POST /v1/messages arrives, the proxy looks up req.Model in the mapping table to find the target Zen model id (if not found, passes through as-is), then selects the protocol using upstream.Router.For(modelID). All 4 protocols share a unified output side (anthropicEmitter) that emits translated content blocks as standard Anthropic SSE event sequences. ### Anthropic ↔ OpenAI Translation (Most Complex) | Anthropic | OpenAI Chat Completions | |———–|———————––| | tools[]{name, description, input_schema} | tools[]{type:"function", function:{name, description, parameters}} | | content[]{type:"tool_use", id, name, input(object)} | assistant.tool_calls[]{id, function:{name, arguments(JSON string)}} | | user.content[]{type:"tool_result", tool_use_id, content} | {role:"tool", tool_call_id, content} | | content[]{type:"thinking"} | delta.reasoning_content (DeepSeek/GLM/Kimi extension field) | | stop_reason:"tool_use" | finish_reason:"tool_calls" | | Streaming input_json_delta (accumulated by index) | Streaming delta.tool_calls[].function.arguments (accumulated by index) | On streaming output, tool call arguments fragments are accumulated by index, then emitted as a single tool_use block when complete, ensuring Claude Code receives structurally complete tool calls. ### Anthropic Passthrough (Claude/Qwen) Zen directly provides the native Anthropic Messages API for Claude and Qwen models. The proxy only rewrites the model id; the request body, response body, and SSE stream are forwarded as-is. This is the simplest path — even tool calls need no translation. ### Codex Responses ↔ OpenAI Chat / Anthropic Messages When Codex requests /v1/responses, the proxy intelligently selects the upstream based on the target model: non-Anthropic native models are converted to OpenAI Chat Completions messages; claude-* / qwen* target models are converted to Anthropic Messages requests. Zen’s returned text deltas, tool call argument fragments, and usage are reassembled into standard Responses SSE events such as response.output_text.delta, response.function_call_arguments.delta, response.completed, etc. This compatibility layer is done locally by the proxy and does not require the Zen upstream to natively provide /v1/responses. ### Anthropic ↔ Google (Gemini) messages[] → contents[] (role: user/model), tools[] → tools[].functionDeclarations. Gemini streaming returns a JSON array (each chunk contains candidates[].content.parts[]), which the proxy parses and translates into Anthropic deltas. ## Configuration config.json (auto-created on first run): jsonc { "listen_addr": ":8787", "upstream_base": "https://opencode.ai/zen", "native_anthropic": true, // true: claude-* / qwen* target models intelligently connect directly to /v1/messages "zen_api_key": "", // Bearer token, required "panel_token": "", // dashboard login password; "" = open (local use) "require_api_key": false, // true: /v1/* must carry valid API Key "default_model": "glm-4.6", "model_mappings": [ // model string sent by Claude Code → real Zen model id { "match": "claude-sonnet-4-5", "target": "glm-5.1" }, { "match": "*", "target": "" } // passthrough fallback ], "web_search_mode": "auto", // auto / native / translate "web_search_model": "", // dedicated model for web_search; "" = use main model "web_search_base_url": "", // dedicated Anthropic upstream for native web_search; "" = use main upstream "web_search_api_key": "", // dedicated API key for native web_search; "" = use main upstream key "thinking_budget_mappings": [ // map Claude Code thinking budget_tokens to supported OpenAI extension fields { "match": "glm-", "field": "thinking" }, { "match": "kimi-", "field": "thinking_budget", "low": 1024, "medium": 4096, "high": 8192, "max": 16384 }, { "match": "moonshot-", "field": "thinking_budget", "low": 1024, "medium": 4096, "high": 8192, "max": 16384 } ], "log_requests": true, "request_timeout_seconds": 0 } All fields can be edited in the Config tab; after saving, the bridge hot-reloads (rebuilds the upstream client) without restart. Model strings not in the mapping table are forwarded as-is to Zen. If an OpenAI-compatible reasoning model returns reasoning_content, the proxy converts it into an Anthropic thinking block, and in the next request sends reasoning_content back to the upstream, meeting the thinking mode continuous dialogue requirement of models like DeepSeek / GLM. thinking_budget_mappings only adds thinking control fields for explicitly matched models: GLM sends thinking:{"type":"enabled","clear_thinking":false} by default, Kimi/Moonshot send thinking_budget by default, DeepSeek does not send thinking_budget by default to avoid incompatible parameters. Claude Code’s web_search_* server tool supports three modes: auto keeps default smart routing; native sends requests with web_search as-is to the Anthropic Messages upstream, can be used with web_search_base_url / web_search_api_key to separately connect to the DeepSeek Anthropic API; translate uses the proxy’s local search shim and uses web_search_model to organize results. The search-specific API key is only returned masked in the dashboard and /api/config responses. ## API Endpoints ### Model Proxy API | Method | Path | Description | |––––|——|———––| | POST | /v1/messages | Smart routing by target model: Claude/Qwen directly to Anthropic, others to OpenAI Chat | | POST | /v1/messages/count_tokens | Best-effort token estimation | | POST | /v1/chat/completions | OpenAI Chat Completions, supports streaming and non-streaming | | POST | /v1/responses | OpenAI Responses API, compatible with Codex CLI, can convert to OpenAI Chat or native Anthropic upstream | | GET | /v1/models | Model list compatible with both OpenAI and Anthropic | | GET | /healthz | Liveness probe | ### Dashboard (for UI) | Method | Path | Auth | Description | |––––|——|——|———––| | GET | /api/health | None | Liveness probe | | GET | /api/auth/check | None | Returns whether login is required and current authentication status | | POST | /api/auth/login | None | Password verification, writes session cookie on success | | POST | /api/auth/logout | None | Destroys session, clears cookie | | GET | /api/config | Required | Current config snapshot (ZenAPIKey masked) | | PUT | /api/config | Required | Update + persist + hot reload | | GET | /api/stats/summary | Required | Request count, token summary | | GET | /api/stats/hourly | Required | Hourly time series | | GET | /api/stats/models | Required | Usage per model | | GET | /api/stats/latency | Required | P50/P95/P99 latency | | GET | /api/logs | Required | Request log list | | GET | /api/logs/{id} | Required | Single log detail (including request/response body) | | GET/POST | /api/keys | Required | List / Create API Key | | GET/PUT/DELETE | /api/keys/{id} | Required | Query / Update / Delete API Key | | POST | /api/keys/{id}/reset | Required | Reset API Key usage | | GET | /api/keys/{id}/usage | Required | Query API Key usage details | | GET | /api/test | Required | Test upstream connectivity | ## Project Structure opencode-cc/ ├── cmd/opencode-cc/ # entry point ├── internal/ │ ├── config/ # JSON config + hot reload │ ├── anthropic/ # Anthropic Messages API line types + SSE writer │ ├── upstream/ # Zen gateway client + 4 protocol translators │ │ ├── protocol.go # Protocol interface + router │ │ ├── anthropic.go # Anthropic passthrough │ │ ├── openai.go # OpenAI Chat Completions translation (domestic models) │ │ ├── responses.go # OpenAI Responses translation (GPT) │ │ ├── google.go # Google Gemini translation │ │ ├── stream_emit.go # shared Anthropic SSE output side │ │ ├── models.go # 49 Zen model catalog │ │ └── client.go # HTTP client (Bearer + GET /v1/models) │ ├── bridge/ # /v1/messages handler: route → translate → forward │ ├── store/ # SQLite (modernc, pure Go) │ └── web/ # dashboard API + embedded SPA ├── web/ # React + Vite + Tailwind source │ └── src/pages/{Dashboard,Inspector,Models,Config}.tsx └── Dockerfile # multi-stage: node → go → distroless ## Notes - Cost of protocol routing. Anthropic → OpenAI / Responses / Google is a lossy translation—certain Anthropic-specific concepts (such as cache_control, thinking signature integrity) have no counterpart in the backend protocols. The Claude/Qwen passthrough path avoids this issue. - Token usage. Under the OpenAI protocol, streaming include_usage chunks give real counts; other protocols read from upstream usage fields, falling back to estimation when missing. - Model catalog may become outdated. internal/upstream/models.go is a manually curated snapshot; prices and capabilities change. Pull the latest list via GET https://opencode.ai/zen/v1/models (requires API key) for calibration. - Old SQLite library incompatibility. Schema changed after refactoring (dropped session_map table, requests fields changed). During development, delete data/*.db; a versioned migration will be added in the release version. ## License MIT

Similar Articles

OpenCode 2.0

Product Hunt

OpenCode 2.0 作为开源 AI 编程代理正式发布,支持终端、桌面和 Web 应用,可搭配任意模型使用。

OpenCode: Setup & Get Free Frontier Models in 5 mins

Reddit r/ArtificialInteligence

This article introduces the installation and configuration of the open-source AI coding agent OpenCode, focusing on demonstrating how to use frontier models for free through the built-in OpenCode Zen service, as well as basic terminal interface operations.