A local OpenAI-compatible /v1 server backed by Cursor Composer. One Bun process, no cloud, no app — your Cursor API key goes straight through as the Bearer token and nothing is stored.
Initially a fork of: Composer AI
bun install
bun run serverThe server listens on http://127.0.0.1:8787/v1 and starts the Cursor SDK bridge automatically. Get a Cursor user API key from the Cursor Dashboard under Integrations and use it as the Bearer token; do not commit it to source control.
Note: the bridge runs as a Node subprocess (auto-detected, auto-restarted). The Cursor SDK speaks gRPC over
node:http2, which Bun's http2 client cannot handle yet (NGHTTP2_FRAME_SIZE_ERROR), so Node is required alongside Bun. Override the binary withCURSOR_SDK_BRIDGE_RUNTIME=/path/to/node.
curl http://127.0.0.1:8787/v1/chat/completions \
-H "Authorization: Bearer $CURSOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"composer-2.5","messages":[{"role":"user","content":"Hello"}]}'import OpenAI from "openai"
const client = new OpenAI({
apiKey: process.env.CURSOR_API_KEY,
baseURL: "http://127.0.0.1:8787/v1",
})
const completion = await client.chat.completions.create({
model: "composer-2.5",
messages: [{ role: "user", content: "Write a TypeScript debounce." }],
})POST /v1/chat/completionsPOST /v1/responses, plusGET /v1/responses/{id},GET /v1/responses/{id}/input_items,POST /v1/responses/{id}/cancel,DELETE /v1/responses/{id}GET /v1/models/opencodev2/v1/*— alias surface for the OpenCode provider (adds the SDK harness model labels)GET /health
Response state and SDK sessions are held in memory; restarting the server invalidates previous_response_id continuations by design.
All settings are optional environment variables (Bun auto-loads .env; see .env.example):
| Variable | Default | Purpose |
|---|---|---|
PORT / HOST |
8787 / 127.0.0.1 |
API listen address |
CURSOR_API_BASE |
https://api.cursor.com |
Cursor public API |
CURSOR_SDK_BRIDGE_PORT |
8792 |
SDK bridge subprocess port |
CURSOR_SDK_BRIDGE_RUNTIME |
auto-detected node |
Binary used to run the bridge subprocess |
CURSOR_SDK_BRIDGE_URL |
unset | Use an external bridge instead of starting one (pair with CURSOR_SDK_BRIDGE_TOKEN) |
CURSOR_SDK_BRIDGE_TIMEOUT_MS |
180000 |
Bridge request timeout |
CURSOR_CLIENT_VERSION / CURSOR_SDK_CLIENT_VERSION |
2.6.22 / sdk-1.0.13 |
Version strings sent to Cursor |
"cursorapi": {
"models": {
"composer-2.5": {
"cost": {
"input": 0.5,
"output": 2.5
},
"limit": {
"context": 200000,
"output": 65536
},
"name": "Composer 2.5"
},
"composer-2.5-fast": {
"cost": {
"input": 3,
"output": 15
},
"limit": {
"context": 200000,
"output": 65536
},
"name": "Composer 2.5 Fast"
},
"grok-4.5": {
"cost": {
"input": 2,
"output": 6
},
"limit": {
"context": 256000,
"output": 65536
},
"variants": {
"medium": {
"thinking": "medium"
},
"low": {
"thinking": "low"
},
"high": {
"thinking": "high"
}
},
"name": "Grok 4.5"
},
"grok-4.5-fast": {
"cost": {
"input": 4,
"output": 18
},
"limit": {
"context": 256000,
"output": 65536
},
"variants": {
"medium": {
"thinking": "medium"
},
"low": {
"thinking": "low"
},
"high": {
"thinking": "high"
}
},
"name": "Grok 4.5 Fast"
}
},
"name": "API for Cursor",
"npm": "@ai-sdk/openai-compatible",
"options": {
"apiKey": "crsr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"baseURL": "http://127.0.0.1:8787/v1"
}
}By default all completions run through the Cursor SDK bridge. Setting CURSOR_FORCE_LEGACY_BACKEND=1 skips the bridge and talks to Cursor's chat backend directly; this requires CURSOR_BACKEND_BASE_URL and CURSOR_CHAT_ENDPOINT, whose values are private — never commit them.
This project supports text and image input, non-streaming and streaming output, JSON-output prompt constraints, and the common SDK response shapes. Image inputs can be sent as Chat Completions image_url parts or Responses input_image parts; each resolved image must be 1MB or smaller.
These OpenAI features are intentionally rejected because Cursor does not expose equivalent OpenAI controls through this path:
ngreater than1logprobsandtop_logprobs- audio output
- background Responses API jobs
Token usage is estimated from character counts because Cursor's stream does not return OpenAI token accounting on this path. For Composer 2.5 and Composer 2.5 Fast, usage.cost is estimated from Cursor's published per-million-token pricing.
bun run test # vitest
bun run typecheck # tsc --noEmitThe SDK bridge can also run standalone with node scripts/cursor-sdk-local-agent-bridge.mjs (listens on 127.0.0.1:8792).