Skip to content

ADR: Cursor as Model Provider via cursor-agent CLI / @cursor/sdk #6393

Description

@pfurini

ADR: Cursor as Model Provider via cursor-agent CLI / @cursor/sdk

Status: Proposed
Date: 2026-05-19
Author: @pfurini
Related: #2509 (precedent ADR), #2382, #2523, #2532, #3246, #5630, #6311, #2555, #3485


1. Context and Problem Statement

The opportunity

Cursor (Anysphere) ships a paid subscription (Pro / Pro+ / Ultra / Teams) that bundles:

  • Multi-model routing — Claude Sonnet 4.6, Claude Opus 4.7, GPT-5.x, Gemini 2.5 Pro, Composer 2.5, Grok 4 — on a single plan, no per-vendor API keys.
  • Cursor's own agent harness — codebase indexing, semantic search, MCP, Skills, Hooks, subagents.
  • Cloud Agents for sandboxed VM execution (future).

GSD users on a Cursor plan today have no first-class way to delegate slice execution to that subscription. The only "delegate to a vendor CLI" provider shipped today is claude-code-cli (#2382 + #2523), which only serves the Anthropic-subscription audience.

What GSD already has

What's missing

A cursor-agent provider that:

  • Spawns the user's locally authenticated cursor-agent binary in headless streaming mode (-p --output-format stream-json) and parses NDJSON events into GSD's AssistantMessageEvent stream.
  • Surfaces every model the user's Cursor subscription exposes through /model.
  • Never touches Cursor credentials — auth state is owned entirely by cursor-agent login and/or CURSOR_API_KEY.

2. Compliance posture (lesson from #2509)

ADR #2509 documents the boundary that kept claude-code-cli compliant with Anthropic's third-party policy: delegate to the local CLI, never handle vendor credentials, never offer a vendor-login flow, never publish benchmarks. We adopt the same posture for Cursor:

  • Never read/store Cursor OAuth tokens — only check agent status or CURSOR_API_KEY env var.
  • Never re-implement cursor login; shell out if a /cursor login helper is added later.
  • No GSD-side metering or routing across vendors.
  • Cursor ToS §1.5(v)/(vii) — GSD is an orchestrator that delegates to Cursor, not a competing service; legal review before any benchmarking content.

3. Decision

Add a self-contained extension src/resources/extensions/cursor-cli/ mirroring the claude-code-cli/ layout 1:1, plus the minimal core plumbing needed for the new provider id.

Phase 1 — CLI provider (GA path)

Register cursor-agent via pi.registerProvider("cursor-agent", { authMode: "externalCli", api: "cursor-stream-json", ... }). Spawn cursor-agent -p --output-format stream-json with --model, --workspace, --trust, optional --resume; parse the NDJSON event union (system | assistant | tool_call | tool_result | result | stream_event | error); translate to AssistantMessageEvent. result.usage displayed only — no dollar estimation (matches the Ollama-style "billed against your plan" semantics).

Models seeded statically (composer-2.5, claude-sonnet-4-6, claude-opus-4-7, gpt-5.5, gemini-2.5-pro, grok-4), refreshed via cursor-agent --list-models at startup and on /cursor models.

Kill-switch: GSD_CURSOR_DISABLE=1. Debug: GSD_CURSOR_DEBUG=1.

Phase 2 — @cursor/sdk adapter (opt-in)

Behind GSD_CURSOR_USE_SDK=1. Dynamic import("@cursor/sdk"); on resolution failure fall back to CLI path with a one-line warning. Justified by lower-latency event handoff and Cloud Agents access (Agent.create({ cloud: {...} })). Gated until SDK declares GA — @cursor/sdk is still public beta as of April 2026.

Phase 3 — /cursor slash commands

QoL on top of Phase 1: /cursor, /cursor login, /cursor logout, /cursor models, /cursor resume <session>. Pure shell-outs to cursor-agent.


4. Minimal core changes (mirrors claude-code-cli precedent)

File Change
packages/pi-ai/src/types.ts Add "cursor-agent" to KnownProvider; "cursor-stream-json" (Phase 1) and "cursor-sdk" (Phase 2) to KnownApi.
packages/pi-ai/src/env-api-keys.ts Add "cursor-agent"process.env.CURSOR_API_KEY ?? "cursor-agent" placeholder.
src/onboarding.ts Add "cursor-agent" to the provider picker, labeled "Cursor (via your subscription)".
src/wizard.ts Add cursor-agent entry (no key required).
<extension loader manifest> Register cursor-cli extension. Exact path to confirm during plan refinement.

Everything else lives under src/resources/extensions/cursor-cli/.


5. Known coupling — externalCli tool-lifecycle bridging (#5630 / #6311)

Issue #5630 and the open PR #6311 document that claude-code-cli never bridges tool lifecycle into the pi extension event bus, leaving write-gate verification as dead code under externalCli. Any new externalCli provider — including cursor-cli — inherits this gap.

Two options, maintainer steer requested:


6. Risks

Risk Mitigation
Binary name drift (cursor-agent vs agent, .cmd/.exe on Windows) Multi-candidate probe like #4582 added for claude.exe; CURSOR_AGENT_BIN override env.
NDJSON event-schema churn between Cursor releases Treat unknown event types as warnings, not errors; pin to documented vocabulary; fixture-driven integration tests.
thinking events suppressed in -p mode → no reasoning visibility in TUI Accept the gap in Phase 1; revisit in Phase 2 via SDK tool_progress.
SDK beta API churn Phase 2 gated behind flag; dynamic import degrades to CLI path on resolution failure.
Cursor plan quota exhaustion during long slices Surface result.usage in TUI footer (mirrors Claude Code token display); document quota in onboarding.
--trust / --force autonomous-edit semantics --trust always (slice cwd already trusted); --force only when the slice declares write intent — same gating as claude-code-cli.
Auth-state caching staleness post-login clearReadinessCache() on /cursor login completion and on CURSOR_API_KEY change at session start.
Cursor ToS §1.5(v) competitive / §1.5(vii) benchmarking clauses Position GSD as orchestrator; legal review before any comparative performance content.
Extension disabled — core impact Additive only; clean unregister via unregisterProvider("cursor-agent").

7. Open questions

  1. Default --force policy? Recommendation: opt-in per slice via existing write-intent flag; TUI badge when --force is on.
  2. Session resumption? Recommendation: persist session_id from system/init; opt-in /cursor resume; fresh session per slice by default.
  3. Model-catalogue refresh cadence? Recommendation: startup + /cursor models + 24h background timer; static seed wins for capabilities, dynamic adds new ids.
  4. Sandbox flag default? Recommendation: leave --sandbox unset (CLI default); document CURSOR_SANDBOX=enabled env override.
  5. Cost display semantics? Recommendation: tokens-only + "billed against Cursor plan" footer; no dollar estimation.
  6. Cloud Agents path? Recommendation: out of scope for Phase 1/2; revisit with a real use case (parallel slice execution, long-running background tasks).
  7. claude-code-cli extension never bridges tool lifecycle into pi extension event bus — write-gate verification is dead code under externalCli #5630/fix(issue): claude-code-cli extension never bridges tool lifecycle into pi extension event bus — write-gate verification is dead code under externalCli #6311 ordering — A or B from §5? Maintainer call.

8. Acceptance criteria (Phase 1)

  • User with cursor-agent installed and authenticated sees Cursor models in /model within 30 s of GSD startup.
  • /model cursor-agent/composer-2.5 switches the active model; next slice streams text/tool events into the TUI.
  • Tool calls render with their results, marked externalToolExecution so Agent Core does not redispatch.
  • result.usage is captured and displayed in the slice footer.
  • GSD_CURSOR_DISABLE=1 keeps the extension dormant at startup.
  • No regression in claude-code-cli provider behaviour — both coexist via KnownProvider discrimination.
  • Unit + fixture-driven integration tests pass in CI.

9. Out of scope

  • Cloud Agents (Agent.create({ cloud: {...} })) — Phase-2+ once a concrete use case lands.
  • Cross-vendor cost normalization.
  • Cursor-side hooks/skills/subagents projected into GSD UI — Phase-2+ via SDK.

10. Request to maintainers

  1. Approve to proceed with the design above, or downgrade to RFC first?
  2. Phase 1 ordering vs fix(issue): claude-code-cli extension never bridges tool lifecycle into pi extension event bus — write-gate verification is dead code under externalCli #6311 (externalCli tool-lifecycle bridging) — A or B in §5?
  3. Anyone already working on this? Happy to claim the issue and start once assigned.

I'm a first-time contributor to this repo; per CONTRIBUTING.md I will wait for assignment before any code lands. Lesson noted from #4101 (codex-cli was closed for missing issue link).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions