opencode-cursor-oauth-plus
v0.1.5
Published
OpenCode plugin that connects Cursor's API to OpenCode via OAuth, model discovery, and a local OpenAI-compatible proxy.
Downloads
781
Maintainers
Readme
opencode-cursor-oauth-plus
OpenCode plugin that connects to Cursor's API, giving you access to Cursor models inside OpenCode with full tool-calling support.
Requirements
- OpenCode >= 1.18
- Bun >= 1.3 — required by OpenCode's plugin runtime (not just development)
- Node.js >= 18 for the HTTP/2 bridge process
- Active Cursor subscription
Installing Bun
This plugin uses Bun APIs (Bun.serve, Bun.spawn, Bun.sleep) and OpenCode
runs plugins via its bundled Bun runtime. Bun must be installed on the
system and available on PATH before starting OpenCode:
curl -fsSL https://bun.sh/install | bash
# Restart your shell or exec $SHELL, then verify:
bun --versionIf OpenCode is started before Bun is installed, the daemon won't find it. Kill any running opencode processes and restart after installing Bun.
Install in OpenCode
Add this to ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"opencode-cursor-oauth-plus"
],
"provider": {
"cursor": {
"name": "Cursor",
"models": {
"composer-2.5": {
"name": "Composer 2.5",
"reasoning": true,
"limit": { "context": 200000, "output": 64000 },
"modalities": { "input": ["text", "image"], "output": ["text"] },
"variants": {
"low": { "effort": "low" },
"medium": { "effort": "medium" },
"high": { "effort": "high" },
"max": { "effort": "max" },
"xhigh": { "effort": "xhigh" }
}
}
}
}
}
}The cursor provider stub is required because OpenCode drops providers that do
not already exist in its bundled provider catalog.
The model list above is an example, not a default. Cursor exposes a different set of models per plan. Generate your own with
cursor-modelsbelow — do not copy this block verbatim.
Static model definitions
OpenCode v1.18.x does not call the provider.models hook for non-built-in
providers. Models must be defined statically in the config. The plugin's
auth.loader handles dynamic connection details (proxy port, token refresh) at
inference time, so the models above work without specifying npm or options.
This is the single most common source of breakage: an id in the models block
that your account cannot use is rejected by Cursor with an opaque error such as
Connect error internal: Error, not_found, or resource_exhausted. Since
0.1.1 the proxy rejects unknown ids up front with the list of ids your account
does have, instead of forwarding them and surfacing the opaque failure.
Listing the models your plan has
Do not hand-write the models block. Generate it:
# Prints a ready-to-paste provider block for YOUR account
npx opencode-cursor-oauth-plus cursor-models
# Or, if the package is already installed by OpenCode:
bun ~/.cache/opencode/packages/opencode-cursor-oauth-plus@latest/node_modules/opencode-cursor-oauth-plus/dist/cli-models.jsMerge the printed provider block into ~/.config/opencode/opencode.json,
then restart OpenCode. Verify with:
opencode models | grep '^cursor/'Every id it prints came back from Cursor for your plan, so ids that do not exist cannot creep in. Run it again whenever you change plans or Cursor ships a new model — new models will not appear until you add them to the config.
Raw discovery output, without the config scaffolding:
npx opencode-cursor-oauth-plus cursor-models --jsonFields the generator cannot infer
| Field | Why | What to do |
|---|---|---|
| reasoning | Cursor derives it from thinkingDetails, which it leaves unset even for models that do reason. The generator therefore emits true for everything. | Set false for any model that never streams thinking output. |
| modalities | Discovery does not report input modalities. The generator emits ["text","image"] because live probes show both models accept images via Cursor's selectedImages path. | Drop "image" only if a model rejects vision; add "pdf" if needed. |
| variants | Effort levels are a Cursor UI feature, not part of discovery. Applied by family: effort for composer*, reasoningEffort for grok*. | Add or remove per model. Extra variants on a model that ignores them are harmless. |
limit.context, limit.output, name and the model id all come straight from
Cursor and need no editing.
Troubleshooting
| Symptom | Cause |
|---|---|
| Cursor missing from the model picker entirely | No provider.cursor.models block, or opencode.json is invalid JSON. Check with opencode debug config. |
| Connect error not_found / internal | The model id is not one your plan has. Re-run cursor-models. |
| Connect error resource_exhausted | Plan quota for that model is exhausted, or you are being rate limited. |
| Connect error unauthenticated | Token rejected — re-run opencode auth login --provider cursor. |
| Request hangs, then reports no output | Cursor accepted the stream but never produced output. The proxy aborts after CURSOR_NO_OUTPUT_TIMEOUT_MS (default 90s). |
OpenCode installs npm plugins automatically at startup, so users do not need to clone this repository.
Authenticate
opencode auth login --provider cursorThis opens Cursor OAuth in the browser. Tokens are stored in
~/.local/share/opencode/auth.json and refreshed automatically.
Use
Start OpenCode and select any Cursor model. The plugin starts a local OpenAI-compatible proxy on demand and routes requests through Cursor's gRPC API.
How it works
- Bun runtime — OpenCode loads plugins via its bundled Bun runtime. The
plugin must have
bunon systemPATHbefore the daemon starts. - OAuth — browser-based login to Cursor via PKCE.
- Model discovery — queries Cursor's gRPC API for all available models
(runs during
auth.loginbut results must be added to config statically). - Local proxy — at inference time,
auth.loaderstarts a localBun.serveproxy that translatesPOST /v1/chat/completionsinto Cursor's protobuf/HTTP/2 Connect protocol. - Native tool routing — rejects Cursor's built-in filesystem/shell tools and exposes OpenCode's tool surface via Cursor MCP instead.
HTTP/2 transport runs through a Node child process (h2-bridge.mjs) because
Bun's node:http2 support is not reliable against Cursor's API.
Architecture
OpenCode --> /v1/chat/completions --> Bun.serve (proxy)
|
Node child process (h2-bridge.mjs)
|
HTTP/2 Connect stream
|
api2.cursor.sh gRPC
/agent.v1.AgentService/RunTool call flow
1. Cursor model receives OpenAI tools via RequestContext (as MCP tool defs)
2. Model tries native tools (readArgs, shellArgs, etc.)
3. Proxy rejects each with typed error (ReadRejected, ShellRejected, etc.)
4. Model falls back to MCP tool -> mcpArgs exec message
5. Proxy emits OpenAI tool_calls SSE chunk, pauses H2 stream
6. OpenCode executes tool, sends result in follow-up request
7. Proxy resumes H2 stream with mcpResult, streams continuationDevelop locally
bun install
bun run build
bun test/smoke.ts