Skip to content

fix(integrations): pre-approve Cursor Band tools and surface agent crashes - #750

Open
AlexanderZ-Band wants to merge 12 commits into
mainfrom
fix/fixcursoracp-pre-approve-bands-mcp-tools-and-surfa-INT-1644
Open

AlexanderZ-Band wants to merge 12 commits into
mainfrom
fix/fixcursoracp-pre-approve-bands-mcp-tools-and-surfa-INT-1644

Conversation

@AlexanderZ-Band

@AlexanderZ-Band AlexanderZ-Band commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Cursor's SDK-owned Band MCP tools now receive per-call approval in every approval mode, while native and other-server tools retain the configured approval policy. Unexpected ACP subprocess exits report the exit code and a bounded stderr tail instead of losing the agent's diagnostics.

Changes

  • Parse Cursor MCP display titles once and verify the registered tool against its exact Band MCP spelling, including additional tools whose names overlap a platform tool's spelling. Select an allow-once option without changing Cursor configuration.
  • Drain subprocess stderr during startup and prompts, retain the last 20 lines, and distinguish crashes from deliberate shutdown. Serialize runtime startup and shutdown and await diagnostic cleanup. Preserve crash classification when stdout has already reached EOF or the ACP prompt reports a broken connection, even when stderr remains open. Treat caller-requested startup cancellation as deliberate shutdown.
  • Split ACP connection lifecycle, session operations, collection, chunk streaming, update parsing, result folding, permissions, and transport into focused modules. Preserve existing runtime exports and adapter hooks; client_runtime.py is now 246 lines.
  • Add a real stdio ACP test peer and crash/shutdown regressions. Enable Band tools in live Cursor approval tests and reject room approval requests for those tools.

Related Issues

Fixes INT-1644.

Testing

  • Ruff check, Ruff format, and Pyrefly check pass.
  • Local full unit suite: 6,684 passed, 155 skipped.
  • Local real stdio regressions pass on macOS, including stdout closing before stderr and failure-triggered cleanup. The new case fails on the previous code and passes with the fix.
  • Last full live backends lane green (run before the stdout-EOF cleanup fix): 146 passed, 396 skipped, 1 failed, 1 fixture error, 1 recovered retry in 1:06:23.

Live results and limitations:

  • Cursor's approve, decline, timeout, two-command decisions, exactly-one session approval, repair, restart, and recovery checks passed with Band tools enabled.
  • Copilot's session-replay test timed out on its initial seed turn, before reaching session-load fallback: last delivery status: none. Captured logs contained platform HTTP 500 (Internal Server Error) and WebSocket phx_error. An isolated recheck passed without code changes; this does not make the full lane green.
  • Cursor's authorized-member fixture failed before the test body: BAND_API_KEY_USER_2 was rejected (403), plan_required — durable human API keys require an Enterprise plan. This acceptance case still needs an eligible second-user credential.
  • OpenCode's task lifecycle timed out on its first attempt with last delivery status: processing; the configured retry passed. Its first attempt remains unsuccessful.

Latest focused validation

On commit 81f28453, using the pinned Cursor CLI 2026.09.28-64d2043, selected only Cursor approval and compound workflow tests with retries disabled:

  • 6 passed, 1 failed, 1 fixture error, 19 deselected in 8:55. Approve, decline, timeout, separate two-command decisions, exactly-one session approval, and repair passed with Band tools enabled.
  • Recovery timed out on an initial seed turn, before restart: last delivery status: processing. Captured logs included platform HTTP 500 (Internal Server Error) and WebSocket phx_error. An isolated recovery recheck passed in 82.24 seconds without code changes; this does not erase the failed first attempt.
  • The authorized-member fixture remains blocked by BAND_API_KEY_USER_2 was rejected (403), plan_required.
  • The six selected local stdio checks passed, including the cleanup-race regression.
  • On c42e9f19, all 27 applicable PR checks passed, including Windows and Ubuntu Python 3.11, 3.12, and 3.13 unit jobs: CI run. The weekly coverage report is intentionally excluded for PR events; the coverage gate passed.
  • Fixed the Windows stdout-eof timeout in the test fixture: CPython’s virtualenv redirector retains stdout handles while waiting for the child interpreter. The fixture now launches sys._base_executable directly and preserves the active virtualenv using CPython’s launcher environment. A real intermediary probe reproduced the deadlock; the unchanged crash-warning regression now passes on all three Windows versions.
  • Latest local validation: Ruff check/format and Pyrefly passed; 6,684 unit tests passed, 155 skipped; all six selected real stdio checks passed. The live E2E results above remain unsuccessful on their first attempt; this fixture-only fix does not resolve the external credential and platform failures.

Checklist

  • PR title follows Conventional Commits format.
  • Code follows project style guidelines.
  • Behavioral tests added and updated.
  • Cursor approval documentation updated.

Review fixes and current validation

On f1f731ca, the requested review found four issues: one each in Runtime Safety, Test Quality, Edge-Case Flow Analysis, and Logical Bugs. All four are fixed, replied to, and resolved. The focused post-fix Logical Bugs pass found no new defects.

  • Startup cancellation stays quiet; connection-failure cleanup preserves crash diagnostics even before stdout EOF.
  • Exact Band MCP spelling avoids custom-name ownership collisions; the approval matrix now covers registered custom tools in all modes.
  • Regression run before fixes: 4 failed, 26 passed. After fixes: 134 focused tests passed; original real stdio and HTTP MCP reproductions pass. Ruff initially reported I001 import ordering; corrected, then Ruff check/format and Pyrefly passed.
  • Full local unit suite: 6,695 passed, 155 skipped, 59 warnings, 143.04 seconds.
  • Relevant live Cursor validation, retries disabled: 5 passed, 11 deselected, 1 warning, 198.65 seconds. Covers approve, decline, timeout, exactly-one session approval, and sequential-room restart recovery. Uses Cursor CLI 2026.09.28-64d2043 and repo-root .env.test.
  • This focused pass does not erase earlier first-attempt failures or make the full backends lane green. The second-user authorized-member case remains blocked by the existing credential entitlement failure and was not selected again.
  • All 28 applicable PR checks passed on f1f731ca, including Ubuntu and Windows Python 3.11, 3.12, and 3.13: run 37455410071. No pending or failed checks. The weekly coverage report skips for PR events; the required coverage gate passed. The new protocol-error and startup-cancellation stdio regressions passed on all three Windows versions (each Windows unit job: 6,516 passed, 773 skipped).

Shared phx_error channel recovery is tracked separately in INT-1700, with the real-WebSocket reproduction attached. It is a confirmed shared transport defect; its attribution to the original Cursor timeout remains unproven.

Subsequent style-only commit c6f488b7 uses match/case for stdio peer initialization stages. Ruff check/format and Pyrefly pass; the full local unit suite again passed 6,695 tests, 155 skipped (146.74 seconds). The CI and live E2E results above refer to f1f731ca.

Single source of truth

On aa27dff6, the adapter and live Cursor workflow guard share the exact registered Band MCP tool ownership predicate. The stderr drain and startup-cancellation observer share one log template. Tool names still come from the existing registry and BandTool vocabulary; the E2E consumer uses string-compatible membership on Python 3.11.

  • Ruff check/format and Pyrefly pass. Full local unit suite: 6,695 passed, 155 skipped, 59 warnings, 144.04 seconds.
  • Relevant live Cursor repair workflow on the final code, retries disabled: 1 passed, 1 warning, 47.22 seconds. Exercises native-tool deny/approve, project repair, and automatic Band memory/reply approval.
  • The original custom-name collision reproduction still passes against real ACP and HTTP MCP.
  • CI for this new head is pending. Earlier full-lane failures and the second-user credential blocker remain as documented above.

Test intent

On 0fe4d8f8, approval tests state two contracts separately: registered Band tools always approve without a room request, while other tools follow explicit policy expectations. Shared fixtures own custom-tool setup and stdio cleanup. Crash tests require ConnectionError and retain direct assertions on exit-code/stderr diagnostics. No production code changed.

Ruff check/format and Pyrefly pass. 134 focused tests passed (1 warning, 11.24 seconds); full local unit suite 6,695 passed, 155 skipped (60 warnings, 146.91 seconds). Live E2E was not rerun for these test-only changes; the prior relevant Cursor repair pass on aa27dff6 is documented above. New-head CI is pending.

Workflow coverage

On 61c575ed, three compound Cursor cases exercise all approval modes through real ACP JSON-RPC, actual HTTP MCP custom-tool/memory/reply calls, a permission-gated native edit subprocess, and room cleanup/rejoin. They check filesystem effects, persisted memory, exactly one reply, and turn judgment; a registered custom-name collision is present throughout. Two real stdio cases drive stdout EOF/protocol failure through adapter on_event, failure reporting, crash diagnostics, cleanup, and a successful respawned turn. Only the deterministic agent decisions and platform tools are test doubles in these local cases.

  • Ruff check/format and Pyrefly pass. 191 focused tests passed, 1 warning, 18.28 seconds. Full local unit suite: 6,700 passed, 155 skipped, 60 warnings, 146.97 seconds.
  • Real platform + pinned Cursor CLI repair and sequential-room restart E2E: 2 passed, retries disabled, 1 warning, 171.52 seconds. This focused run does not make the previously unsuccessful full backends lane green.
  • Initial new-test setup failures were corrected: the custom handler received EchoInput rather than a string; the initial adapter calls omitted required turn arguments. First run: 5 failed, 134 passed; second: 2 failed, 137 passed. Final runs above pass with stricter assertions and no retries.
  • All 28 applicable PR checks passed on 61c575ed, with no pending or failed checks. Both CI run 37462073038 and coverage run 37462073019 succeeded on attempt 1. Ubuntu and Windows Python 3.11, 3.12, and 3.13 are green. The three new compound workflow cases and both stdio crash/recovery cases passed on all three Windows versions (each Windows unit job: 6,521 passed, 773 skipped). The weekly coverage report is intentionally excluded for PR events; the coverage gate passed.

@linear-code

linear-code Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

INT-1644

INT-1700

@AlexanderZ-Band AlexanderZ-Band changed the title fix(acp): pre-approve Cursor Band tools and surface agent crashes fix(integrations): pre-approve Cursor Band tools and surface agent crashes Oct 6, 2026
@AlexanderZ-Band
AlexanderZ-Band requested a review from a team October 6, 2026 09:34

@AlexanderZ-Band AlexanderZ-Band left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Four verified in-scope findings: startup-cancellation diagnostics, custom-tool coverage, failure-triggered cleanup before stdout EOF, and custom-name ownership collision. Fixing under the requested --fix workflow. GitHub rejected REQUEST_CHANGES because the authenticated user is this PR author; submitting as COMMENT.

Comment thread src/band/integrations/acp/client_runtime.py
Comment thread tests/adapters/test_cursor_acp_adapter.py
Comment thread src/band/integrations/acp/stderr.py
Comment thread src/band/adapters/cursor_acp.py Outdated

This branch has not been deployed

No deployments
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