computer-use MCP: Swift continuation leak causes screenshot timeout

Resolved 💬 3 comments Opened Apr 8, 2026 by rahulmehta25 Closed Apr 11, 2026

Summary

The computer-use MCP server's captureScreenWithExclusion function leaks its Swift continuation, causing screenshot tasks to hang forever (30s timeout). All other computer-use tools (list_granted_applications, open_application, wait, cursor_position) work fine.

Error

SWIFT TASK CONTINUATION MISUSE: captureScreenWithExclusion(displayId:width:height:allowedBundleIds:imageQuality:) leaked its continuation without resuming it. This may cause tasks waiting on it to remain suspended forever.

Reproduction

  1. Open Claude Code (CLI) or Claude Desktop
  2. Use the computer-use MCP server
  3. Call request_access for an app (e.g., Google Chrome) - succeeds
  4. Call screenshot - hangs for 30s then returns timeout error: computer-use native call exceeded 30000ms
  5. All subsequent screenshot calls also fail

Environment

  • macOS 15.5 (Darwin 24.6.0)
  • Claude Desktop 1.1062.0
  • Screen Recording permission granted for "Claude" in System Settings > Privacy & Security
  • Single monitor (also reproduced with dual monitor setup)
  • Toggling Screen Recording permission off/on does not fix it
  • /mcp reconnect does not fix it

Root Cause

The Swift function captureScreenWithExclusion uses withCheckedContinuation (or withUnsafeContinuation) but has a code path where continuation.resume() is never called. This leaves the awaiting task suspended forever.

Common patterns that cause this:

  • Guard/early return without resuming
  • SCScreenshotManager completion handler not firing (e.g., invalid display ID, permission denied at capture time)
  • Error callback path missing resume

Affected Binary

/Applications/Claude.app/Contents/Resources/app.asar.unpacked/node_modules/@ant/claude-swift/build/Release/computer_use.node

Suggested Fix

Wrap the continuation in a safe-resume pattern:

return await withCheckedContinuation { continuation in
    var resumed = false
    let safeResume: (CGImage?) -> Void = { value in
        guard !resumed else { return }
        resumed = true
        continuation.resume(returning: value)
    }
    
    guard displayId != kCGNullDirectDisplay else {
        safeResume(nil)
        return
    }
    
    performCapture(...) { result in
        switch result {
        case .success(let image): safeResume(image)
        case .failure: safeResume(nil)
        }
    }
}

Workaround

Use claude-in-chrome MCP for browser interaction instead of computer-use screenshots. For native app verification, infer state from API checks rather than visual inspection.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗