OpenCode plugin that connects to Cursor's API, giving you access to Cursor models inside OpenCode with full tool-calling support.
npx cursor-oauth-opencode setup --global
opencode auth login --provider cursorFor project-local setup:
npx cursor-oauth-opencode setup --project
opencode auth login --provider cursorThe setup command is idempotent. It adds the npm plugin entry and fallback
model registries for both provider.cursor and provider.cursor-code in
opencode.json, preserving existing user model overrides. Login stays on the
primary provider only (opencode auth login --provider cursor).
The plugin registers two OpenCode providers that share one OAuth login, proxy, and model catalog. Mode is selected in the model picker:
| Provider | Picker path | Behavior |
|---|---|---|
| Cursor | cursor/<model> |
Native tool passthrough (code mode OFF) |
| Cursor Code | cursor-code/<model> |
Single code sandbox tool (code mode ON) |
Breaking default: cursor/* is native-tools / code mode OFF. To keep the
previous sandbox-collapse behavior, pick cursor-code/<model>. There is no
auto-rewrite of a previously selected cursor/* model.
Fleet-wide kill switch (forces OFF even for cursor-code/*): set
CURSOR_OPENCODE_CODE_MODE=0 or provider.cursor.plugin.codeMode.enabled: false.
That knob is read only under provider.cursor.plugin — a block under
provider.cursor-code.plugin has no effect.
Reasoning-tier Cursor models are listed once in the picker; effort is chosen in OpenCode's Select variant menu (not as separate model slugs):
| Picker model | Variants | Notes |
|---|---|---|
grok-4.5 / grok-4.5-fast |
low / medium / high |
Cursor slug suffixes are shifted (high → …-xhigh) |
claude-4.6-opus, gpt-5.4, glm-5.2, … |
only levels Cursor exposes | e.g. opus → high, gpt-5.4 → medium |
composer-2.5 / composer-2.5-fast |
default only |
Non-effort axis; stay as separate models |
Legacy full slugs (grok-4.5-fast-high, …) still resolve as aliases. After
upgrade, re-run setup so opencode.json seeds the collapsed base ids.
If you do not want to use the setup command, add this to
~/.config/opencode/opencode.json:
The fallback API URLs are placeholders so OpenCode can list the providers
before login. After auth, the plugin starts a local proxy and replaces Cursor
models with live Cursor model discovery. cursor-code must keep the /v1/code
path suffix (merge-safety if a request falls back to provider.api).
opencode auth login --provider cursorThis opens Cursor OAuth in the browser. Tokens are stored in
~/.local/share/opencode/auth.json and refreshed automatically. Both
providers use that single cursor auth entry.
Start OpenCode and select a model under Cursor (native tools) or
Cursor Code (sandbox code tool). The plugin starts a local
OpenAI-compatible proxy on demand and routes requests through Cursor's gRPC API.
- OAuth — browser-based login to Cursor via PKCE (
cursorprovider only). - Model discovery — queries Cursor's gRPC API for all available models.
- Local proxy — translates chat completions into Cursor's protobuf/HTTP/2
Connect protocol. Path selects mode:
/v1/*(native) vs/v1/code/*(sandbox). - Code mode (
cursor-code) — advertises a singlecodetool to the Cursor model; the model writes one async JavaScript program that the proxy runs in a Node child sandbox, bridging eachawait tools.X()wave to OpenCode's tools while the Cursor bridge stays parked.cursor/*passes client tools through directly as MCP tools.
HTTP/2 transport runs through a Node child process (h2-daemon.mjs / legacy
h2-bridge.mjs) because Bun's node:http2 support is not reliable against
Cursor's API.
On every outbound Cursor Run the proxy can:
- Caveman-compress tool-description prose and the OpenCode system prompt
(
plugin.caveman.level/CURSOR_OPENCODE_CAVEMAN, defaultfull; system inherits viaplugin.caveman.system/CURSOR_OPENCODE_CAVEMAN_SYSTEM). - Append a verbatim reasoning guard for runaway-reasoning models
(
claude-fable-5,claude-opus-4-8;CURSOR_OPENCODE_REASONING_GUARD=0). - Inject a coalesced harness appendix (interstitial text, overthink
<system-reminder>, throttled continuity, single-shot[reasoning tail]/[thinking exhaustion]notes) for those same models.
Savings counters live under CURSOR_OPENCODE_SAVINGS=1 (plugin log file).
OpenCode --> /v1/chat/completions (cursor = code OFF)
--> /v1/code/chat/completions (cursor-code = code ON)
|
Bun.serve (proxy)
|
Node child (h2-daemon / h2-bridge)
|
HTTP/2 Connect stream
|
api2.cursor.sh gRPC
/agent.v1.AgentService/Run
1. Proxy advertises ONE code tool to the Cursor model (its description embeds
OpenCode's real tool catalog)
2. Model emits code({script}); proxy runs the script in a Node child sandbox
3. Each await tools.X() wave -> proxy emits OpenAI tool_calls SSE, parks BOTH
the Cursor bridge and the sandbox
4. OpenCode executes the tools, sends results in the follow-up request
5. Proxy feeds results to the parked sandbox; the next wave repeats step 3
6. Script returns -> its value becomes the code tool result; the Cursor bridge
resumes and streams the model's final answer
With cursor/* (or the kill switch forcing OFF) the proxy passes each client
tool through directly: the model calls a tool via mcpArgs, the proxy emits
OpenAI tool_calls, pauses the H2 stream, and resumes with mcpResult on the
follow-up request.
bun install
bun run build
bun test/smoke.tsPlugin diagnostics never print to the terminal (they would paint over the
OpenCode TUI). They land in ~/.cache/cursor-oauth-opencode/plugin.log.
Override the path with CURSOR_OPENCODE_LOG_FILE; set
CURSOR_OPENCODE_LOG_STDERR=1 to also mirror lines to stderr when debugging
outside the TUI.
{ "$schema": "https://opencode.ai/config.json", "plugin": [ "cursor-oauth-opencode@latest" ], "provider": { "cursor": { "name": "Cursor", "npm": "@ai-sdk/openai-compatible", "api": "http://127.0.0.1:65535/v1", "models": { "composer-2.5-fast": { "name": "Composer 2.5 Fast", "reasoning": true, "temperature": true, "attachment": false, "tool_call": true, "limit": { "context": 200000, "output": 64000 } } } }, "cursor-code": { "name": "Cursor Code", "npm": "@ai-sdk/openai-compatible", "api": "http://127.0.0.1:65535/v1/code", "models": { "composer-2.5-fast": { "name": "Composer 2.5 Fast", "reasoning": true, "temperature": true, "attachment": false, "tool_call": true, "limit": { "context": 200000, "output": 64000 } } } } } }