Skip to content

feat(auto-review): review Shell, WebFetch and MCP calls when Auto-review is on - #512

Open
pliurh wants to merge 1 commit into
leookun:mainfrom
pliurh:feat/gateway-auto-review
Open

pliurh wants to merge 1 commit into
leookun:mainfrom
pliurh:feat/gateway-auto-review

Conversation

@pliurh

@pliurh pliurh commented Oct 10, 2026

Copy link
Copy Markdown

Summary

Addresses #389: when Cursor's Run Mode is Auto-review, BYOK runs now review Shell, WebFetch and MCP calls instead of asking for approval on every call that is not in the allowlist.

Cursor's official backend runs a classifier on these calls and sends skip_approval for the ones it judges safe. The gateway replaces that backend but had no classifier, so skip_approval was always false and Auto-review behaved like Allowlist with an empty allowlist. This PR uses the Run's own model as the classifier.

How it works

Shell / WebFetch / MCP call
  ├─ model already asked for the approval card ─► sent unchanged
  ├─ Shell the Cursor sandbox can contain       ─► sent unchanged (client runs it sandboxed)
  └─ otherwise: hold the exec / interaction, ask the Run model (30 s timeout)
        ├─ allow                     ─► skip_approval = true
        └─ block / error / timeout   ─► smart_mode_approval { reason }
                                         (Cursor's native approval card shows the reason;
                                          allowlisted commands, domains and MCP tools still run)
  • Review only happens when the client sends RequestContextEnv.smart_mode_classifier_auto_mode_enabled = true, i.e. the user selected Auto-review. Other Run Modes are unchanged.
  • The reviewer sees the user's Auto-review allow/block instructions (*_permissions_auto_run), the workspace paths, the last three user messages, the agent's latest reply, and the action. The action is passed as JSON inside one <review_input> tag, with < escaped so reviewed text cannot close the tag.
  • Every failure falls back to the approval card, never to auto-run.
  • A held request is only sent if its exec or interaction is still pending, checked under the same lock that interrupts use, so an interrupted call is never sent after its abort.
  • Models now advertise supports_smart_mode_classifier (field 45), so the client offers Auto-review for BYOK models.
  • Only user-message and plan actions carry a request context. Runs started by other actions (for example resumes) reuse the conversation's latest Auto-review state; a conversation not seen since the gateway started keeps Cursor's own approvals.

Structure

server/
├─ prompt/cursor/auto_review/prompt.md      reviewer instructions for shell, URL fetch and MCP actions
└─ src/cursor/
   ├─ tools/auto_review.rs                  new: when to review, model call, verdict parsing, verdict → wire fields
   ├─ tools/runtime.rs                      send a held request only while its exec / interaction is pending
   ├─ tools/mod.rs, tool_call_dispatch/*    Shell, WebFetch, CallMcpTool and dynamic MCP dispatch can hold a call
   ├─ compile/run.rs                        Auto-review state for each Run
   ├─ conversation/{registry,runtime}.rs    per-conversation Auto-review state; reviewer per Run generation
   └─ services/model_catalog.rs             supports_smart_mode_classifier

Testing

  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace --all-targets and npm --prefix apps/desktop run check pass.
  • New unit tests in auto_review.rs cover: verdict parsing, which calls are reviewed (sandbox, escalation, tool kind), the Auto-review flag and user instructions, prompt-input escaping, conversation excerpt selection, verdicts on ShellArgs, WebFetchRequestQuery and McpArgs, a held Shell call and a held WebFetch sent after an allow/block/unparseable review, an interrupted call that is not sent, and the per-conversation state for Runs without a request context.
  • Manually tested on macOS with Cursor 3.11 and an OpenAI-compatible model: read-only shell commands, URL fetches and MCP queries ran without approval. A delete outside the workspace, reads of credential files, and MCP writes the user had not asked for showed the approval card with the reviewer's reason.

Notes and follow-ups

  • Each reviewed call costs one extra request to the Run's model, about 1–2k input tokens and a few seconds in manual testing. A separate, cheaper reviewer model could be a follow-up setting.
  • On allow, skip_approval bypasses the client's local permission checks for that call, the same as Cursor's own Auto-review.
  • WebSearch has no approval field in the protocol. It stays governed by Cursor's own web-search auto-accept setting.
  • MCP tools that become available mid-Run (for example after mcp_auth) are still missing from that Run's MCP routes. This is existing behavior and not changed here.

🤖 Generated with Claude Code

…iew is on

Cursor's Auto-review Run Mode relies on a backend classifier that the gateway
replaces, so every non-allowlisted call fell back to the approval card. When
the client selects Auto-review, the gateway now holds Shell, WebFetch and MCP
requests, asks the Run's own model for a verdict, and sends them with
skip_approval (allow) or smart_mode_approval (block, failure or timeout) so
Cursor shows its native approval card with the reason.

- Shell calls the Cursor sandbox can contain and calls the model already
  escalated are sent unchanged.
- Held requests are sent only while their exec or interaction is pending.
- Models advertise supports_smart_mode_classifier so the client offers the mode.
- Runs without a request context reuse the conversation's latest state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant