Skip to content

Latest commit

 

History

History
39 lines (26 loc) · 3.96 KB

mcp.md

File metadata and controls

39 lines (26 loc) · 3.96 KB

Use from an MCP client

desktop-tools-mcp (dist/mcp.js) serves the desktop tools over stdio MCP, with Ace's native-first guidance as its instructions. By default it uses the Desktop Tools app's signed client and socket in ~/.local/state/desktop-tools. Run it from this repository with npx, pinned to a release tag, or from a checkout with node:

claude mcp add desktop -- npx -y --package=github:githubnext/desktop-tools#v0.20.0 desktop-tools-mcp
claude mcp add desktop -- node <path>/dist/mcp.js

The README has the setup for Codex and for clients configured with JSON.

The app bundles its own MCP server (since 0.17.0), Desktop Tools.app/Contents/MacOS/desktop-tools-mcp, written in Swift. Run with no options, dist/mcp.js finds it beside the client the app links and hands stdio over to it, so an npx setup needs no change. Any option keeps the Node server, which forwards each call through the host's desktop-tools-client as createDesktop does; --wire 2 is accepted for that, and --wire 1, the removed Peekaboo stack, is refused. An app from before 0.17.0 has no server of its own and its client speaks the Peekaboo stack's wire, which the Node server no longer does, so every call fails, ending with both versions when they differ: updating the npx tag doesn't update the app.

The app's server needs no Node. It serves the socket in its app's state folder (DESKTOP_TOOLS_STATE, else the folder its bundle ID chooses), or --socket <path>, and names its app in the instructions and rules: the app it ships in, or --name <app>. So the development app's server, Desktop Tools Dev.app/Contents/MacOS/desktop-tools-mcp, serves ~/.local/state/desktop-tools-dev as Desktop Tools Dev with no options.

To use another signed host's grants instead, pass its client, socket, and name. The host must embed the Swift runtime from native2/; one that still embeds the Peekaboo stack's can't be served. For Ace Canary:

claude mcp add desktop -- node <path>/dist/mcp.js \
	--client ~/.local/state/ace-canary/desktop-client \
	--socket ~/.local/state/ace-canary/desktop.sock \
	--name "Ace Canary"

For Codex, in ~/.codex/config.toml:

[mcp_servers.desktop]
command = "npx"
args = ["-y", "--package=github:githubnext/desktop-tools#v0.20.0", "desktop-tools-mcp"]

The tool names, descriptions, and argument schemas are declared in Swift (native2/Sources/DesktopProtocol/ContractTools.swift), and @githubnext/desktop-tools/tools (desktopTools, desktopRequest) exports them, generated from that file, for Ace's desktop_* tools and other embedders. Tools lists them. The descriptions don't repeat the rules every tool shares: exact targets, single-use snapshots, background and foreground input, what each outcome means, pausing, desktop_wait and expect, and the recipes. desktopToolRules(app) returns those rules, and the server adds them to its instructions; a host with its own agent guidance should include them too.

The server names the agent in the host's window highlight from the MCP client's clientInfo: Claude, Codex, Cursor, Gemini or Copilot when the client name contains one, otherwise the client's title, otherwise "Agent".

Action results start with Outcome: completed, refused, or unknown. Actions run one at a time, and notifications/cancelled aborts a call; cancellation does not undo input already delivered.

Warning: the server acts with the host app's macOS Accessibility, Screen Recording, and clipboard permissions. The Desktop Tools app asks once per agent, such as Claude Code or a terminal, before serving it, and refuses it with CALLER_NOT_APPROVED until you choose Allow. It asks for no approval per call beyond that and what the MCP client itself requires, so any session of an allowed agent can read the screen and clipboard and send input to any app but Desktop Tools itself.