Skip to content

feat(desktop): Computer Use for macOS, Windows, and Linux - #6537

Open
sheehanmunim wants to merge 81 commits into
pingdotgg:mainfrom
sheehanmunim:mac/computer-use
Open

feat(desktop): Computer Use for macOS, Windows, and Linux#6537
sheehanmunim wants to merge 81 commits into
pingdotgg:mainfrom
sheehanmunim:mac/computer-use

Conversation

@sheehanmunim

@sheehanmunim sheehanmunim commented Aug 13, 2026

Copy link
Copy Markdown

Summary

  • Adds local Computer Use via t3-desktop MCP (Swift on macOS, Rust on Windows/Linux), including agent cursor overlay and background clicks that do not steal the user mouse.
  • Ships agent-owned Chrome tab control through a bundled extension, plus Settings → Computer Use for enablement, extension status, and macOS privacy permission status.
  • Validated against latest origin/main (ahead of current nightly v0.0.34-nightly.20260813.1087): contracts/desktop/web typecheck clean for this work; Windows Blade E2E confirmed list_apps, background click … in background, and screenshot.

Test plan

  • macOS: enable Computer Use in Settings, grant Accessibility + Screen Recording, confirm agent cursor overlay and app click/type/screenshot
  • Windows: confirm Nightly/server resolves t3-desktop-mcp.exe, list_apps works, coordinate click returns in background
  • Linux: smoke list_apps / screenshot on X11 (or expected Wayland messaging)
  • Chrome: load unpacked native/t3-chrome-extension, verify Settings shows extension installed and browser_* tools work in a labelled tab group
  • Toggle Computer Use off and confirm providers no longer inject the desktop MCP
  • CI green on this branch against main

Made with Cursor

Note

Add Computer Use desktop control for macOS, Windows, and Linux

  • Introduces a new Swift MCP server for macOS (native/t3-desktop-mcp) and a new Rust MCP server for Windows/Linux (native/t3-desktop-mcp-rs) that expose tools for screen capture, app listing, accessibility-tree inspection, and synthetic input.
  • Adds a Chrome extension (native/t3-chrome-extension) with a native messaging bridge so the MCP server can control browser tabs; includes install scripts for macOS/Linux and Windows.
  • Wires the desktop MCP binary into Claude, Codex, Cursor, and Grok adapters as a stdio MCP server, gated by a new desktopControl settings block (enabled, agentCursorEnabled, browserControlEnabled).
  • Adds a cross-platform translucent agent cursor overlay (Swift on macOS, Rust on Windows/Linux) that appears during tool execution and fades on completion.
  • Surfaces MCP tool and Computer Use permissions approvals as canonical tool_approval/permissions_approval request types through the Codex session runtime, pending-approvals list, and chat UI.
  • Adds a /settings/computer-use settings page with permission status polling, privacy pane links, and browser extension setup guidance.
  • Risk: the desktop MCP binary runs as a stdio child process with access to the OS accessibility APIs and synthetic input; it is only launched when the feature is enabled in server settings and a binary is found on disk.

Macroscope summarized f35e608.

Copilot AI lite review requested due to automatic review settings August 13, 2026 21:37
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 30a540e4-fda9-486d-a662-7c32838add2c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 13, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding: the new desktop-control resolver hides its ServerSettingsService dependency behind Effect.serviceOption, so the requirement is absent from the type of every adapter that calls it and a missing layer silently resolves to "Computer Use enabled". Everything else in the changed TypeScript (subpath namespace imports for effect/*, Effect.fn helpers acquiring FileSystem/Path/HostProcess* from the environment, new Schema.is predicates that add real logic, static CodexAppServerRequestError.methodNotFound factory, no new ManagedRuntime/runPromise/fake Layer.succeed) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/desktopControl/resolveEnabledDesktopMcp.ts Outdated
Comment thread apps/server/src/provider/Layers/ClaudeAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/CursorAdapter.ts Outdated
Comment thread native/t3-chrome-extension/background.js

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds cross-platform “Computer Use” to T3 Code Desktop by bundling a local desktop-control MCP server (Swift on macOS, Rust on Windows/Linux) and an agent-owned Chrome tab workflow via a bundled extension, plus end-to-end settings/approvals plumbing across contracts, server adapters, desktop IPC, and the web UI.

Changes:

  • Bundle and resolve a t3-desktop MCP server across macOS/Windows/Linux, staging it into desktop build artifacts and injecting it into supported provider adapters when enabled.
  • Add Chrome extension bridge + install scripts, plus a new Settings → Computer Use page that surfaces enablement, extension status, and macOS privacy permission readiness.
  • Extend contracts and runtime ingestion to support new approval kinds (tool / permissions) and Codex MCP form-elicitation flows, with accompanying test coverage.

Reviewed changes

Copilot reviewed 60 out of 75 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
vite.config.ts Adjust staged formatter globs to avoid native/ files breaking commits
scripts/build-desktop-artifact.ts Stage desktop MCP binaries (Swift/Rust) and macOS Info.plist Apple Events usage text
scripts/build-desktop-artifact.test.ts Add tests for MCP executable naming/targets and macOS usage description
packages/effect-codex-app-server/src/errors.ts Extend identifier purpose literals for new approval request types
packages/contracts/src/settings.ts Add desktopControl settings subtree + patch schema
packages/contracts/src/providerRuntime.ts Add canonical request types for tool/permissions approvals
packages/contracts/src/orchestration.ts Extend ProviderRequestKind to include tool/permissions
packages/contracts/src/ipc.ts Add IPC schemas + DesktopBridge methods for Computer Use readiness
package.json Add build script for Swift desktop MCP server
native/t3-desktop-mcp/Sources/BrowserBridge.swift Swift Chrome-extension bridge via unix socket + native messaging framing
native/t3-desktop-mcp/Package.swift SwiftPM package definition for macOS desktop MCP server
native/t3-desktop-mcp/.gitignore Ignore SwiftPM build output
native/t3-desktop-mcp-rs/src/tools.rs Rust tool schema definitions matching macOS tool surface (with browser_* gating)
native/t3-desktop-mcp-rs/src/platform/mod.rs Cross-platform desktop backend trait + shared formatting/types
native/t3-desktop-mcp-rs/src/main.rs Rust MCP server: newline-delimited JSON-RPC over stdio + tool dispatch
native/t3-desktop-mcp-rs/src/capture.rs Shared display/window capture with Wayland/X11 fallbacks and PNG encoding
native/t3-desktop-mcp-rs/src/browser.rs Rust Chrome bridge + native-host relay over local socket
native/t3-desktop-mcp-rs/src/apps.rs App/window enumeration and pid resolution shared across backends
native/t3-desktop-mcp-rs/Cargo.toml New Rust crate definition + deps for Windows/Linux automation/capture
native/t3-desktop-mcp-rs/.gitignore Ignore Rust target output
native/t3-chrome-extension/manifest.json MV3 extension manifest (native messaging + debugger/tabGroups perms)
native/t3-chrome-extension/install.sh macOS/Linux host registration script for native messaging
native/t3-chrome-extension/install.ps1 Windows registry-based host registration script
native/t3-chrome-extension/background.js Chrome service worker implementing agent-owned tab group + DevTools-based automation
apps/web/src/session-logic.ts Teach pending approval derivation about tool/permissions kinds
apps/web/src/session-logic.test.ts Tests for deriving/retaining new approval kinds in UI work log
apps/web/src/routeTree.gen.ts Add generated route for Settings → Computer Use
apps/web/src/routes/settings.computer-use.tsx Route entry for the Computer Use settings page
apps/web/src/components/settings/SettingsSidebarNav.tsx Add sidebar icon mapping for Computer Use section
apps/web/src/components/settings/settingsSearch.ts Add search items/labels for Computer Use settings and permissions
apps/web/src/components/settings/ComputerUseSettings.tsx New settings UI: toggles, extension status, macOS permission readiness + deep links
apps/web/src/components/settings/browserBrandIcons.tsx Brand icons for browser/agent cursor display in settings
apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx UI labels for tool vs permission pending approvals
apps/web/src/components/chat/ComposerPendingApprovalPanel.test.tsx Tests for new pending-approval labels
apps/web/src/assets/computer-use/google-chrome.svg Add Chrome brand asset
apps/web/src/assets/computer-use/firefox.svg Add Firefox brand asset (not supported yet)
apps/web/src/assets/computer-use/edge.svg Add Edge brand asset
apps/web/src/assets/computer-use/brave.svg Add Brave brand asset
apps/server/src/provider/Layers/GrokAdapter.ts Inject resolved desktop MCP server into Grok adapter MCP server list
apps/server/src/provider/Layers/CursorAdapter.ts Inject resolved desktop MCP server into Cursor adapter MCP server list
apps/server/src/provider/Layers/CodexSessionRuntime.ts Add permissions approval handling + MCP elicitation (tool/permissions) request plumbing
apps/server/src/provider/Layers/CodexSessionRuntime.test.ts Unit tests for permission approvals + MCP elicitation mapping/response building
apps/server/src/provider/Layers/CodexProvider.ts Advertise Codex MCP form-elicitation capability
apps/server/src/provider/Layers/CodexProvider.test.ts Test for Codex initialize capability flags
apps/server/src/provider/Layers/CodexAdapter.ts Map new request kinds/types and include elicitation detail extraction
apps/server/src/provider/Layers/CodexAdapter.test.ts Lifecycle tests for tool/permissions canonical approval mapping
apps/server/src/provider/Layers/ClaudeAdapter.ts Add desktop MCP server into Claude mcpServers map when resolvable
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts Preserve new approval kinds through activity summaries/payload
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.approval.test.ts Tests ensuring new approval kinds reach clients correctly
apps/server/src/desktopControl/resolveEnabledDesktopMcp.ts Resolve desktop MCP only when enabled; pass env flags for cursor/browser toggles
apps/server/src/desktopControl/resolveEnabledDesktopMcp.test.ts Tests for enable/disable and env flag behavior
apps/server/src/desktopControl/desktopMcpBinary.ts Locate bundled/override MCP binary across platforms and dev layouts
apps/server/src/desktopControl/desktopMcpBinary.test.ts Tests for override resolution and unsupported platform behavior
apps/desktop/src/preload.ts Expose Computer Use IPC methods to renderer via preload bridge
apps/desktop/src/ipc/methods/computerUse.ts IPC handlers for permissions readiness + opening privacy panes
apps/desktop/src/ipc/DesktopIpcHandlers.ts Register new Computer Use IPC handlers
apps/desktop/src/ipc/channels.ts Add IPC channel constants for Computer Use
apps/desktop/src/computerUse/permissions.ts Read macOS TCC statuses + detect Chrome extension/host registration
apps/desktop/src/computerUse/permissions.test.ts Tests for permission status mapping, Settings deep links, and extension detection
apps/desktop/scripts/electron-launcher.mjs Patch macOS Info.plist to include Apple Events usage description

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift
Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift Outdated
Comment thread apps/server/src/provider/Layers/GrokAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/CursorAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/ClaudeAdapter.ts Outdated
Comment thread scripts/build-desktop-artifact.ts Outdated
Comment thread apps/server/src/desktopControl/resolveEnabledDesktopMcp.ts Outdated
Comment thread native/t3-desktop-mcp/Sources/AgentCursor.swift Outdated
Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift
Comment thread native/t3-desktop-mcp/Sources/AgentCursor.swift Outdated
Comment thread native/t3-desktop-mcp/Sources/AgentCursor.swift Outdated
Comment thread native/t3-desktop-mcp/Sources/AgentCursor.swift Outdated
Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift
Comment thread native/t3-desktop-mcp/Sources/AgentCursor.swift Outdated
Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift Outdated
Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

11 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

Comment thread apps/server/src/desktopControl/desktopMcpBinary.ts
Comment thread native/t3-desktop-mcp/Sources/AgentCursor.swift
@sheehanmunim

Copy link
Copy Markdown
Author

Babysit update

Branch is merge-clean onto current origin/main, and the latest nightly tag (v0.0.34-nightly.20260813.1088) points at the same commit as main.

Review fixes pushed (settings fail-closed + per-session resolve without widening session R, platform-specific binary candidates + executable validation, Chrome profile enumeration, Swift bridge/cursor hardening). High/clear threads resolved; 4 remaining are explicitly deferred follow-ups (extension owned-tab persistence, Chrome window-id reuse, bridge bind TOCTOU, SkyLight coord fallback).

Note for reviewers / Macroscope: the Effect Service Conventions check keeps reporting the old Effect.serviceOption finding against resolveEnabledDesktopMcp.ts, but that code path is gone — current tip uses yield* ServerSettings.ServerSettingsService plus makeResolveEnabledDesktopMcp for adapters. Local t3 typecheck is clean.

Fork CI workflows may still need org approval; Vercel marketing fails with “Authorization required to deploy” (expected for fork PRs).

@sheehanmunim

Copy link
Copy Markdown
Author

Follow-up correctness gaps from review are now fixed on tip:

  • Chrome extension owned-tab/group state survives service-worker restarts via `chrome.storage.session`
  • Agent window id is scoped to the live Chrome pid (no reclaim after Chrome restart)
  • Browser bridge ownership uses an exclusive flock around bind
  • Background SkyLight input requires `setWindowLocation` or falls back

Windows/Linux desktop MCP packaging was already wired through `stageDesktopMcpRust` in desktop artifact builds — once this lands in Nightly, hand-copying the binary should no longer be needed.

Comment thread native/t3-desktop-mcp/Sources/main.swift Outdated
Comment thread native/t3-chrome-extension/background.js
Comment thread native/t3-desktop-mcp/Sources/main.swift
Comment thread native/t3-desktop-mcp/Sources/main.swift
Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts Outdated
Comment thread apps/desktop/src/computerUse/permissions.ts
@sheehanmunim

Copy link
Copy Markdown
Author

Note on Macroscope Effect Service Conventions

This check is still failing with a retained finding against the deleted path `apps/server/src/desktopControl/resolveEnabledDesktopMcp.ts` (`Effect.serviceOption` / `onNone` defaults).

That file was renamed to `desktopMcpLaunch.ts`, and the current implementation acquires settings with `yield* ServerSettings.ServerSettingsService` (fail-closed on `getSettings` errors). There is no `serviceOption` / `onNone` path left in the PR.

Correctness Check is green; unresolved review threads are at 0. Remaining merge blockers appear to be human review + fork Vercel auth, not code correctness.

Comment thread apps/server/src/provider/Layers/CodexAdapter.ts
Comment thread apps/server/src/provider/Layers/CodexAdapter.ts
Comment thread native/t3-desktop-mcp-rs/src/platform/agent_cursor.rs Outdated
Comment thread native/t3-desktop-mcp-rs/src/platform/windows.rs Outdated
Comment thread native/t3-desktop-mcp-rs/src/platform/windows.rs
Comment thread native/t3-chrome-extension/install.ps1 Outdated
Comment thread native/t3-chrome-extension/background.js
Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts
Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts Outdated
Comment thread native/t3-chrome-extension/background.js Outdated
Comment thread native/t3-chrome-extension/background.js Outdated
Comment thread native/t3-chrome-extension/background.js Outdated
@sheehanmunim

Copy link
Copy Markdown
Author

Addressed remaining bot findings on cdb9503a5:

  • MCP elicitation correlations use the JSON-RPC request id (fiber-local), so concurrent same-serverName prompts no longer collide
  • Chrome extension: dwell/message-based session teardown, reply only on the originating port, and disconnect cleanup only touches the captured tab set
  • resolveEnabledDesktopMcp.ts restored as a fail-closed re-export of desktopMcpLaunch.ts (acquires ServerSettingsService directly; settings failures disable Computer Use)

Vercel marketing deploy auth failure is outside this PR’s scope.

Comment thread native/t3-chrome-extension/background.js Outdated
Comment thread native/t3-desktop-mcp-rs/src/platform/linux.rs Outdated
Comment thread native/t3-desktop-mcp-rs/src/platform/linux.rs
Comment thread native/t3-desktop-mcp-rs/src/platform/linux.rs Outdated
Comment thread native/t3-desktop-mcp-rs/src/platform/linux.rs Outdated
Comment thread native/t3-desktop-mcp-rs/src/platform/linux.rs
Comment thread native/t3-desktop-mcp-rs/src/platform/linux.rs
sheehanmunim and others added 20 commits August 15, 2026 12:35
Match the Computer History Macroscope fixes on Computer Use without
pulling in the History daemon entrypoint.

Co-authored-by: Cursor <cursoragent@cursor.com>
Tag extension disconnect sentinels with connection generation, preserve
AT-SPI ambiguity errors in resolve_pid, and reject non-representable
click coordinates on Computer Use.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep under-point clicks on the resolved app PID, reject off-window
coordinate fallbacks, and sample browser disconnect generation under the
outgoing lock.

Co-authored-by: Cursor <cursoragent@cursor.com>
Prefer result tab IDs in select/close confirmations, only constrain
under-point clicks when `app` is explicit, and surface not-visible
element errors from resolvePoint for right-click and drag.

Co-authored-by: Cursor <cursoragent@cursor.com>
Skip T3AgentCursor when resolving the window under a click/drag point,
and route coordinate-only drags through that under-point window instead
of the last inspected Registry.targetPid.

Co-authored-by: Cursor <cursoragent@cursor.com>
Look up args.app via resolveApp instead of resolveTargetPid, and reject
any drag whose under-point destination is not the source window.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep cross-window rejection for endpoints outside the source frame, but
do not use frontmost-under-point alone — that falsely rejects in-window
background drags covered by another app.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align Computer Use settings with the Source Control / Provider disclosure
pattern so UI Consistency checks see the shared Collapsible primitives.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Guard bridge socket probing and writes by connection generation, keep
minimized apps in list_apps, fail closed on partial control files, escape
app-list delimiters, drop grim all-outputs display fallback, and tighten
Chrome agent-window persistence and screenshot scaling.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Serialize ensureAgentWindow under the state lock and keep the Computer Use
permission dialog open when System Settings fails to launch.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep History-only CLI wiring off the mac/computer-use branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ttings error handling

Only unlink bridge sockets this listener owns, stop advertising grim-only displays without capture support, and catch rejected privacy-settings open promises.

Co-authored-by: Cursor <cursoragent@cursor.com>
… branch

Apply Macroscope fixes for agent cursor lifecycle, browser bridge writes,
AX window pairing, app resolution, JSON-RPC parse errors, and Linux cursor
init without History-only daemon changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Prevent whitespace-only app arguments from matching every running app via
substring fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Also strip accidental computer-history CLI from the Computer Use branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep Computer Use History-free and avoid short PID queries binding
unrelated apps via substring name matches.

Co-authored-by: Cursor <cursoragent@cursor.com>
A leftover closing brace from stripping the History CLI broke the top-level
Swift script structure after native-host.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread native/t3-desktop-mcp-rs/src/browser.rs
Comment thread native/t3-desktop-mcp-rs/src/browser.rs Outdated
Comment thread native/t3-desktop-mcp/Sources/main.swift
query: &str,
) -> Result<(ElementRef, String, u32)> {
let trimmed = query.trim();
if let Ok(pid) = trimmed.parse::<u32>() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High platform/linux.rs:655

A numeric query of "0" returns the first application whose PID lookup failed, so get_app_state or activate_app targets an arbitrary unresolved app instead of rejecting the invalid or ambiguous PID. Because applications() uses PID 0 as its failure sentinel, reject pid == 0 before searching the application list.

+        if pid == 0 {
+            return Err(DesktopError::new("pid 0 is not a valid application pid"));
+        }
         if let Some(hit) = applications.iter().find(|(_, _, app_pid)| *app_pid == pid) {
🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/platform/linux.rs around line 655:

A numeric query of `"0"` returns the first application whose PID lookup failed, so `get_app_state` or `activate_app` targets an arbitrary unresolved app instead of rejecting the invalid or ambiguous PID. Because `applications()` uses PID `0` as its failure sentinel, reject `pid == 0` before searching the application list.

Comment thread native/t3-desktop-mcp/Sources/main.swift
lines.push(String::new());
lines.push(format!("── window {index}: \"{title}\""));
let window_start = element_lines.len();
self.walk(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High platform/windows.rs:435

get_app_state can traverse far more than max_elements nodes and stall on large UIA trees. walk checks lines.len(), but describe returns None for unnamed Pane nodes, so those visited nodes do not consume the budget; track visited nodes with a separate counter (as the Linux backend does) and enforce max_elements against it.

🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/platform/windows.rs around line 435:

`get_app_state` can traverse far more than `max_elements` nodes and stall on large UIA trees. `walk` checks `lines.len()`, but `describe` returns `None` for unnamed `Pane` nodes, so those visited nodes do not consume the budget; track visited nodes with a separate counter (as the Linux backend does) and enforce `max_elements` against it.

fn keycode_for(&self, keysym: u32) -> Result<Option<(Keycode, bool)>> {
let (mapping, first) = self.keyboard_mapping()?;
let per = mapping.keysyms_per_keycode as usize;
for (index, chunk) in mapping.keysyms.chunks(per).enumerate() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High platform/linux.rs:389

keycode_for ignores keyboard-layout symbols beyond slots 0 and 1, so type_char reports valid AltGr/group characters as absent and fails with “not on the current keyboard layout” when no unused scratch keycode exists. Extend the lookup and returned modifier information to handle all layout levels, rather than falling back to scratch remapping for characters already available in the active layout.

🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/platform/linux.rs around line 389:

`keycode_for` ignores keyboard-layout symbols beyond slots 0 and 1, so `type_char` reports valid AltGr/group characters as absent and fails with “not on the current keyboard layout” when no unused scratch keycode exists. Extend the lookup and returned modifier information to handle all layout levels, rather than falling back to scratch remapping for characters already available in the active layout.


/// Parse an `e12`-style element id into its numeric handle.
fn element_id(raw: &str) -> Result<u32, DesktopError> {
raw.trim()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium src/main.rs:216

element_id accepts malformed IDs such as ee12 and EeE12 as handle 12, so click/type/scroll/set-value operations can act on a real element instead of returning the advertised invalid-ID error. trim_start_matches(['e', 'E']) removes every consecutive prefix character; remove at most one optional prefix with strip_prefix.

🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/main.rs around line 216:

`element_id` accepts malformed IDs such as `ee12` and `EeE12` as handle `12`, so click/type/scroll/set-value operations can act on a real element instead of returning the advertised invalid-ID error. `trim_start_matches(['e', 'E'])` removes every consecutive prefix character; remove at most one optional prefix with `strip_prefix`.

Comment on lines +817 to +819
let clickCount = (args["click_count"] as? Int) ?? 1
guard clickCount > 0, clickCount <= 3 else {
return "error: click_count must be an integer between 1 and 3"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High Sources/main.swift:817

A non-Int click_count such as "bad" is treated as 1, so toolClick performs an unintended real click instead of returning the stated validation error. Distinguish an omitted value (default to 1) from a malformed supplied value and reject the latter.

-    let clickCount = (args["click_count"] as? Int) ?? 1
-    guard clickCount > 0, clickCount <= 3 else {
+    let clickCount: Int
+    if let rawClickCount = args["click_count"] {
+        guard let parsedClickCount = rawClickCount as? Int else {
+            return "error: click_count must be an integer between 1 and 3"
+        }
+        clickCount = parsedClickCount
+    } else {
+        clickCount = 1
+    }
+    guard clickCount > 0, clickCount <= 3 else {
🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp/Sources/main.swift around lines 817-819:

A non-`Int` `click_count` such as `"bad"` is treated as `1`, so `toolClick` performs an unintended real click instead of returning the stated validation error. Distinguish an omitted value (default to `1`) from a malformed supplied value and reject the latter.

Comment on lines +849 to +852
let is_frontmost = focused_pid
.filter(|front| *front != 0 && *front == pid)
.is_some()
|| (!focused_name.is_empty() && name.to_lowercase() == focused_name);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium platform/linux.rs:849

list_apps marks every same-named application as FRONTMOST when the focused app and entry both have usable, nonmatching PIDs, so multiple instances produce contradictory frontmost results. The name fallback must only run when either PID is unavailable.

                         .filter(|front| *front != 0 && *front == pid)
                         .is_some()
-                        || (!focused_name.is_empty() && name.to_lowercase() == focused_name);
+                        || (pid == 0
+                            || focused_pid.is_none_or(|front| front == 0))
+                            && !focused_name.is_empty()
+                            && name.to_lowercase() == focused_name;
🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/platform/linux.rs around lines 849-852:

`list_apps` marks every same-named application as `FRONTMOST` when the focused app and entry both have usable, nonmatching PIDs, so multiple instances produce contradictory frontmost results. The name fallback must only run when either PID is unavailable.

Nonblocking bridge writes, owner-only agent-cursor sockets, no WAYLAND_DISPLAY
mutation, Wayland type_text/X11 cookie checks, create-first bridge binding, and
inert browser when control is disabled.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread native/t3-desktop-mcp/Sources/BrowserBridge.swift Outdated

// Clicks pass through: empty ShapeInput region. Without Shape, a topmost
// override-redirect window would eat clicks under the hotspot — fail closed.
if conn.shape_query_version().is_err()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High platform/agent_cursor_linux.rs:403

The overlay is still initialized and mapped when the X server rejects the Shape setup, so its default input region can intercept user clicks instead of passing them through. shape_query_version() and shape_rectangles() only enqueue requests here; their cookies are never checked with reply()/check_for_error(), and the later poll_for_event() consumes the server error after initialization has already proceeded. Validate both request cookies synchronously before setting UI_LIVE or allowing the window to be mapped.

🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/platform/agent_cursor_linux.rs around line 403:

The overlay is still initialized and mapped when the X server rejects the Shape setup, so its default input region can intercept user clicks instead of passing them through. `shape_query_version()` and `shape_rectangles()` only enqueue requests here; their cookies are never checked with `reply()`/`check_for_error()`, and the later `poll_for_event()` consumes the server error after initialization has already proceeded. Validate both request cookies synchronously before setting `UI_LIVE` or allowing the window to be mapped.

let ready = poll(&pollFd, 1, waitMs)
if ready <= 0 { return false }
continue
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nonblocking writes abort socket readers

High Severity

writeAll now sets O_NONBLOCK on the shared bridge socket. serve and the native-host reader treat any read result <= 0 as a hang-up, so EAGAIN during a write tears down the extension connection. A slow or full socket keeps the flag set for the whole deadline, which is exactly when the reader is most likely to wake and exit.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2fb6531. Configure here.

connection
.set_input_focus(InputFocus::PARENT, window_id, 0u32)
.map_err(|error| DesktopError::new(format!("could not set input focus: {error}")))?
.check()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

X11 raise checks fail activation

Medium Severity

raise_x11_window now check()s best-effort restack and set_input_focus nudges. Those requests commonly return BadMatch (unmapped or not-yet-viewable windows) even after a successful _NET_ACTIVE_WINDOW. Activation then errors and never reports success, including cases where the EWMH request already went out.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2fb6531. Configure here.

…MOST

Avoid O_NONBLOCK on the shared socket, revalidate the fd under writeLock,
propagate Shift release failures, and stop fabricating FRONTMOST markers.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment on lines +368 to +370
connection
.xtest_fake_input(if press { 4 } else { 5 }, button, 0, root, 0, 0, 0)
.map_err(|error| DesktopError::new(format!("could not send button event: {error}")))?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High platform/linux.rs:368

button returns success after transmitting an XTEST request without checking the X11 protocol result, so rejected button presses are reported as successful and clicks, right-clicks, or scroll events silently do nothing. Chain .check() before flush(), as move_pointer does.

             .xtest_fake_input(if press { 4 } else { 5 }, button, 0, root, 0, 0, 0)
-            .map_err(|error| DesktopError::new(format!("could not send button event: {error}")))?;
+            .map_err(|error| DesktopError::new(format!("could not send button event: {error}")))?
+            .check()
+            .map_err(|error| DesktopError::new(format!("could not send button event: {error}")))?;
🤖 Copy this AI Prompt to have your agent fix this:
In file @native/t3-desktop-mcp-rs/src/platform/linux.rs around lines 368-370:

`button` returns success after transmitting an XTEST request without checking the X11 protocol result, so rejected button presses are reported as successful and clicks, right-clicks, or scroll events silently do nothing. Chain `.check()` before `flush()`, as `move_pointer` does.

Pick the advertised press/click/activate action instead of hard-coding index 0.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 15 total unresolved issues (including 13 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f35e608. Configure here.

DesktopError::new(
"element exposes no press/click action — use coordinates or set_value",
)
})?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Matcher uses localized action names

Medium Severity

get_actions() returns localized action names, but the new matcher only compares English press/click/activate. On non-English locales those names do not match, so invoke fails. Native Wayland then has no coordinate fallback, so element clicks stop working after this change.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f35e608. Configure here.

DesktopError::new(
"element exposes no press/click action — use coordinates or set_value",
)
})?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Toggle actions are never selected

Medium Severity

The new action picker never treats toggle as a click. Checkboxes, radios, and toggle buttons that only advertise toggle (common in Qt) no longer invoke. That used to work via do_action(0) and now fails on Wayland, where coordinate fallback is blocked.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f35e608. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants