claude-code - 💡(How to fix) Fix computer-use MCP silently returns "not_installed" for every app on native-installer CLI (NSMetadataQuery needs NSRunLoop.main) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#47110Fetched 2026-04-13 05:41:11
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×4

On the native-installer build of Claude Code (Bun-compiled single binary, not npm), mcp__computer-use__request_access silently returns reason: "not_installed" for every requested app — including obviously installed Apple apps like Calculator, TextEdit, Safari, and Finder. There is no crash, no error, no telemetry event, and no user-visible log. The response looks structurally valid.

This is a distinct failure mode from the existing cluster of "Cannot read properties of undefined (reading 'registerEscape' / 'checkAccessibility')" issues (#41118, #41190, #41278, #41355, #41404, #41538, #41606, #41807, #42263, #42380, #45923). Those all describe the npm install path where the computer-use-swift.node addon is missing and vS() is undefined. On the native installer, the addon IS embedded at /$bunfs/root/computer-use-swift.node and loads successfully — the failure happens one layer deeper.

Error Message

return T.setRequestHandler(lg, async (A) => { O.warn([${K}] tool call "${A.params.name}" reached the stub handler — no session context bound...); return { content: [{ type: "text", text: "This computer-use server instance is not wired to a session. Per-session app permissions are not available on this code path." }], isError: true }; }), T;

Root Cause

Note that bundleId in each denied entry is just the echoed input string (literally "Calculator", not com.apple.Calculator), because H.resolved is undefined and oP1 falls back to H.requestedName.

Fix Action

Fix / Workaround

The only reason tools appear to work at all from the interactive CLI is that getComputerUseMCPToolOverrides (H21) replaces the call function for every tool with one that dispatches through eP1()PC_(oeH(), MyH(), Uh9()), bypassing the stub's CallTool handler. So the JS/dispatcher layer is fine; the problem is specifically in the native listInstalled() Cocoa path.

  1. It's silent. A user sees "Calculator not_installed" on a machine where Calculator is obviously installed, and there's no indication that this is a context issue vs. a real enumeration failure. The existing Cannot read properties of undefined crash is ugly but at least actionable.
  2. It's the next layer users hit after the #41190 workaround. The canonical fix for the crude crash is "use the native installer." Users who follow that advice don't crash — they land here, with request_access silently denying everything.
  3. It's not documentable as a TCC permission issue. #41099 describes the genuine permission dead-end where Claude surfaces "Accessibility and Screen Recording permission(s) not yet granted" with no binary path. Once that user granted perms to the CLI binary, their computer-use worked. This failure mode survives full permission grants — NSMetadataQuery doesn't need TCC, it needs an NSRunLoop.

Code Example

{
  "granted": [],
  "denied": [
    {"bundleId": "Calculator", "reason": "not_installed"},
    {"bundleId": "TextEdit", "reason": "not_installed"},
    {"bundleId": "Safari", "reason": "not_installed"},
    {"bundleId": "Terminal", "reason": "not_installed"},
    {"bundleId": "com.apple.finder", "reason": "not_installed"}
  ],
  "screenshotFiltering": "native"
}

---

function oP1(H) {
  return {
    bundleId: H.resolved?.bundleId ?? H.requestedName,
    reason: H.resolved ? "user_denied" : "not_installed"
  };
}

---

async listInstalledApps() { return sp(() => _.apps.listInstalled()) }

---

chicagoListInstalled
performSpotlightQuery()
kMDItemContentType == %@      → com.apple.application-bundle
kMDItemCFBundleIdentifier
NSMetadataQuery failed to start (Spotlight may be indexing or disabled)
v16@?0@"NSNotification"8       ← callback taking an NSNotification

---

function A91(H) { H._drainMainRunLoop() }
tsH = setInterval(A91, 1, vS())   // pump every 1ms from a Bun worker

---

return T.setRequestHandler(lg, async (A) => {
  O.warn(`[${K}] tool call "${A.params.name}" reached the stub handler — no session context bound...`);
  return {
    content: [{
      type: "text",
      text: "This computer-use server instance is not wired to a session. Per-session app permissions are not available on this code path."
    }],
    isError: true
  };
}), T;
RAW_BUFFERClick to expand / collapse

Summary

On the native-installer build of Claude Code (Bun-compiled single binary, not npm), mcp__computer-use__request_access silently returns reason: "not_installed" for every requested app — including obviously installed Apple apps like Calculator, TextEdit, Safari, and Finder. There is no crash, no error, no telemetry event, and no user-visible log. The response looks structurally valid.

This is a distinct failure mode from the existing cluster of "Cannot read properties of undefined (reading 'registerEscape' / 'checkAccessibility')" issues (#41118, #41190, #41278, #41355, #41404, #41538, #41606, #41807, #42263, #42380, #45923). Those all describe the npm install path where the computer-use-swift.node addon is missing and vS() is undefined. On the native installer, the addon IS embedded at /$bunfs/root/computer-use-swift.node and loads successfully — the failure happens one layer deeper.

Environment

  • Claude Code 2.1.104 (native installer, not npm), binary at ~/.local/share/claude/versions/2.1.104 (Mach-O arm64)
  • macOS 15 (Darwin 24.6.0), Apple Silicon
  • Pro/Max plan, computer-use feature flag enabled (tool calls route through getComputerUseMCPToolOverrides / H21 / eP1)

Steps to reproduce

  1. Confirm you're on the native installer (file $(which claude)Mach-O 64-bit executable, not a JS shebang).
  2. Invoke mcp__computer-use__list_granted_applications — works, returns {"allowedApps":[],"grantFlags":{...}}.
  3. Invoke mcp__computer-use__cursor_position — works, returns real coordinates.
  4. Invoke mcp__computer-use__request_access with any mix of real apps, e.g. {"apps": ["Calculator", "TextEdit", "Safari", "Terminal", "com.apple.finder"], "reason": "..."}.

Actual result:

{
  "granted": [],
  "denied": [
    {"bundleId": "Calculator", "reason": "not_installed"},
    {"bundleId": "TextEdit", "reason": "not_installed"},
    {"bundleId": "Safari", "reason": "not_installed"},
    {"bundleId": "Terminal", "reason": "not_installed"},
    {"bundleId": "com.apple.finder", "reason": "not_installed"}
  ],
  "screenshotFiltering": "native"
}

Note that bundleId in each denied entry is just the echoed input string (literally "Calculator", not com.apple.Calculator), because H.resolved is undefined and oP1 falls back to H.requestedName.

Expected result: either the permission dialog appears with the requested apps resolved to their real bundle IDs, or a clear error indicates why the installed-apps enumeration failed.

Root cause (from bytecode analysis)

The JS resolver b91 builds lookup maps from whatever H.executor.listInstalledApps() returns, then maps each request to {requestedName, resolved: map.get(...)}. When resolved is undefined, the dialog serializer oP1 emits:

function oP1(H) {
  return {
    bundleId: H.resolved?.bundleId ?? H.requestedName,
    reason: H.resolved ? "user_denied" : "not_installed"
  };
}

So "not_installed" is not an affirmative "this app is absent" signal — it's just "the resolver found nothing." For every requested app to hit this path, listInstalledApps() must be returning an empty array (or a list that doesn't contain Calculator, TextEdit, Safari, Terminal, or Finder — which is effectively impossible on any real Mac).

listInstalledApps() in UF6 is:

async listInstalledApps() { return sp(() => _.apps.listInstalled()) }

where _ = vS() is the native computer-use-swift.node module. From the binary's Swift symbol table:

chicagoListInstalled
performSpotlightQuery()
kMDItemContentType == %@      → com.apple.application-bundle
kMDItemCFBundleIdentifier
NSMetadataQuery failed to start (Spotlight may be indexing or disabled)
v16@?0@"NSNotification"8       ← callback taking an NSNotification

So the Swift implementation enumerates installed apps by starting an NSMetadataQuery for kMDItemContentType == "com.apple.application-bundle" and listening for NSMetadataQueryDidFinishGatheringNotification.

NSMetadataQuery requires an active NSRunLoop.main on the AppKit main thread to deliver its completion notification (it's a notification-based async Cocoa API; see Apple's NSMetadataQuery docs). The CLI binary doesn't call NSApplicationMain and has no AppKit main run loop, so the query either (a) returns false from start() and hits the "NSMetadataQuery failed to start (Spotlight may be indexing or disabled)" error path, or (b) starts but its delegate never fires.

The JS side has a drainMainRunLoop pump:

function A91(H) { H._drainMainRunLoop() }
tsH = setInterval(A91, 1, vS())   // pump every 1ms from a Bun worker

but pumping _drainMainRunLoop() via setInterval from a Bun worker is not equivalent to NSApplicationMain's main thread run loop. Notification-based Cocoa APIs won't deliver their callbacks through a side loop.

Why other tools still work

  • cursor_position works because GI().mouseLocation() goes through a different native addon (computer-use-input.node, the Rust crate) and uses CGEventSource read APIs that don't need a main run loop or TCC entitlements.
  • list_granted_applications works because it's pure JS state (just returns the allowlist).
  • request_access fails silently because its gating dependency (listInstalled()) depends on a Cocoa async API that silently produces nothing in the CLI runtime.
  • By extension, screenshots via SCShareableContent and the CGEventTapCreate Esc hotkey would also fail — the binary contains their error strings (Missing screen recording permission, [cu-esc] CGEvent.tapCreate failed — check Accessibility permission) but those are at least user-visible, unlike this silent not_installed case.

Design signal

The binary already carries two bundle identities that signal this was designed for the desktop app, not the CLI:

  • JS: IS6 = "com.anthropic.claude-code.cli-no-window" — literal "cli-no-window" sentinel host bundle ID used when TQq() can't detect the hosting terminal.
  • Swift: com.anthropic.claudefordesktop — the intended host bundle ID (Claude for Desktop).

There's also an explicit "CLI variant" factory, createComputerUseMcpServerForCli (oh9), that creates a server via xF6(H, _) with only two arguments, which takes the stub branch of xF6:

return T.setRequestHandler(lg, async (A) => {
  O.warn(`[${K}] tool call "${A.params.name}" reached the stub handler — no session context bound...`);
  return {
    content: [{
      type: "text",
      text: "This computer-use server instance is not wired to a session. Per-session app permissions are not available on this code path."
    }],
    isError: true
  };
}), T;

The only reason tools appear to work at all from the interactive CLI is that getComputerUseMCPToolOverrides (H21) replaces the call function for every tool with one that dispatches through eP1()PC_(oeH(), MyH(), Uh9()), bypassing the stub's CallTool handler. So the JS/dispatcher layer is fine; the problem is specifically in the native listInstalled() Cocoa path.

Why this is report-worthy even though it's "just" running in the wrong context

  1. It's silent. A user sees "Calculator not_installed" on a machine where Calculator is obviously installed, and there's no indication that this is a context issue vs. a real enumeration failure. The existing Cannot read properties of undefined crash is ugly but at least actionable.
  2. It's the next layer users hit after the #41190 workaround. The canonical fix for the crude crash is "use the native installer." Users who follow that advice don't crash — they land here, with request_access silently denying everything.
  3. It's not documentable as a TCC permission issue. #41099 describes the genuine permission dead-end where Claude surfaces "Accessibility and Screen Recording permission(s) not yet granted" with no binary path. Once that user granted perms to the CLI binary, their computer-use worked. This failure mode survives full permission grants — NSMetadataQuery doesn't need TCC, it needs an NSRunLoop.

Suggested fixes (pick any)

  • Short term / docs: Detect CLI runtime (process.env.__CFBundleIdentifier is unset and host bundle resolves to the cli-no-window sentinel) and emit a clear error from listInstalled(): "computer-use enumeration requires Claude for Desktop; see docs." Route the computer-use MCP through the desktop app's helper when the desktop app is installed.
  • Medium term: Replace NSMetadataQuery with a synchronous enumeration that doesn't depend on the main run loop — e.g., walk /Applications and /System/Applications + ~/Applications, read each Contents/Info.plist with CFPropertyListCreateFromXMLData, extract CFBundleIdentifier / CFBundleName. Slower but CLI-safe.
  • Long term: Spin up a minimal NSApplication in the Swift addon's init path (NSApplication.shared, activationPolicy = .accessory, start an NSRunLoop on a dedicated thread) so Cocoa notification-based APIs work from a non-.app host. This is the same pattern Electron / CEF use for headless AppKit hosting.

Related

  • #41355 — @ant/computer-use-swift missing from npm (different install path, same subsystem)
  • #41099 — request_access permission dead-end in CLI
  • #41118, #41190, #41278, #41404, #41538, #41606, #41807, #42263, #42380, #45923 — registerEscape / checkAccessibility undefined crash cluster
  • #41209 — computer-use-input.node missing on npm darwin-arm64
  • #38471 — original feature request for CLI computer-use

extent analysis

TL;DR

The mcp__computer-use__request_access call silently returns "not_installed" for all requested apps due to the native listInstalledApps() function failing to enumerate installed applications in the CLI runtime, likely because NSMetadataQuery requires an active NSRunLoop on the AppKit main thread.

Guidance

  • Verify that the issue is specific to the CLI runtime by checking the process.env.__CFBundleIdentifier and host bundle resolution.
  • Consider implementing a short-term fix by detecting the CLI runtime and emitting a clear error from listInstalled() indicating that computer-use enumeration requires the desktop app.
  • Explore medium-term solutions such as replacing NSMetadataQuery with a synchronous enumeration of applications that doesn't depend on the main run loop.
  • Investigate long-term solutions like spinning up a minimal NSApplication in the Swift addon's init path to enable Cocoa notification-based APIs in the CLI runtime.

Example

// Example of detecting CLI runtime and emitting a clear error
if (!process.env.__CFBundleIdentifier || process.env.__CFBundleIdentifier === 'com.anthropic.claude-code.cli-no-window') {
  throw new Error('Computer-use enumeration requires Claude for Desktop; see docs.');
}

Notes

The issue is specific to the native installer and CLI runtime, and the suggested fixes aim to address the underlying problem of NSMetadataQuery not working in this context. The choice of fix depends on the desired trade-off between implementation complexity, performance, and user experience.

Recommendation

Apply a short-term workaround by detecting the CLI runtime and emitting a clear error from listInstalled(), as this provides an immediate solution for users and allows for further investigation into more permanent fixes.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix computer-use MCP silently returns "not_installed" for every app on native-installer CLI (NSMetadataQuery needs NSRunLoop.main) [1 participants]