A local Claude desktop agent that actually uses your Mac, not just your chat window
Claude Desktop can reach MCP servers if you hand-edit claude_desktop_config.json. Fazm ships a signed Mac app that spawns the Claude Agent SDK as a local Node.js subprocess, pre-wired with four native MCP servers so Claude can read your focused window, click a real button by role, and reply to a WhatsApp thread, all without a screenshot and without you editing a config file. The subprocess literally runs on your host.
“acp-bridge/package.json line 15 depends on @agentclientprotocol/claude-agent-acp ^0.29.2. At startup, Swift's ACPBridge calls findNodeBinary(), then spawns `node --max-old-space-size=256 --max-semi-space-size=16 acp-bridge/dist/index.js` (ACPBridge.swift line 343). The bridge resolves Contents/MacOS/mcp-server-macos-use (index.ts line 63) and, if existsSync() is true, registers it as the MCP server `macos-use`. All session/request_permission calls are auto-approved inside index.ts lines 573-585. Tool watchdog budgets: 10s internal / 120s MCP / 300s default (index.ts lines 77-79).”
acp-bridge and Desktop/Sources/Chat/ACPBridge.swift in the Fazm open-source repo
Apps a local Claude desktop agent can reach on Fazm
Any app that implements macOS Accessibility. The macos-use MCP hands Claude the focused window's AX tree. The whatsapp MCP drives the Catalyst app directly. The google-workspace MCP hits Gmail, Drive, and Calendar under your own Google account. No special integration required per app.
The bridge is a subprocess, not a library
Swift does not link the Agent SDK. It spawns it. That isolation is what makes the watchdog and auto-approve work. Here is what the process graph looks like once Fazm is up.
What Fazm spawns when you press Download and open the app
The one dependency line that makes Fazm a Claude agent
Open the repo, read the package.json of the bridge, you see which SDK is doing the real work. Fazm is not reimplementing Claude Code. It is running the same agent SDK that Claude Code uses, wrapped in an ACP connection so Swift can stream events from it.
The Swift call that spawns your local Claude
This is the real call. The flags are real. A 256 MB heap is deliberate, it keeps the agent's memory footprint under control while still leaving headroom for tool result buffers. The env switch below the Process spawn is how Fazm decides whether to use your Claude Max subscription or the bundled key.
The five MCP servers every new install gets for free
There is nothing to configure. The bridge walks its own app bundle on startup, and for every binary it finds at an expected path, it registers that binary as an MCP server over stdio. No UI, no JSON editor, no clipboard-pasted tokens.
playwright
Real Chromium with an extension hook. Fazm's default browser MCP when Claude needs a web surface. Spawned from node_modules/@playwright/mcp/cli.js inside the app bundle.
macos-use
A bundled Go binary at Contents/MacOS/mcp-server-macos-use. Exposes the live AXUIElement tree as MCP tools. Claude can read the focused window, click by role, type into fields, without ever taking a screenshot.
Contents/MacOS/whatsapp-mcp. Drives the native WhatsApp Catalyst app through accessibility APIs. Claude can search chats, open a thread, read messages, and send a reply.
google-workspace
Python MCP server at Contents/Resources/google-workspace-mcp. PYTHONHOME is pinned to the bundled .venv so it runs without any system Python. Claude reads Gmail, Drive, Calendar, Docs from your own Google account.
fazm_tools
Unix-socket MCP server Swift exposes for in-app operations: capturing the active window with ScreenCaptureKit, reading clipboard, triggering recordings, manipulating skills. Claude calls it like any other MCP tool.
Your own MCP servers
Drop JSON into ~/.fazm/mcp-servers.json and the bridge appends them at startup. Same schema as Claude Code's mcpServers. Your custom stdio or HTTP MCPs sit next to the bundled ones with no UI work.
How macos-use gets added to the local Claude's toolbelt
One existsSync check. One push. The rest is the Agent SDK's MCP plumbing. If you drop a JSON entry into ~/.fazm/mcp-servers.json, the loop beneath this block appends your server next to the bundled ones on the very next restart.
What Fazm does in the first 400 milliseconds after launch
The sequence is linear and deterministic. Nothing depends on a user toggling a setting. This is why the agent reaches every MCP tool the first time you type a prompt, not after a restart.
1. Locate a bundled Node runtime
Swift's findNodeBinary() picks a Node runtime shipped inside the signed app bundle. No Homebrew, no nvm, no user-installed Node on PATH. ACPBridge.swift line 1443.
2. Spawn the bridge with a 256 MB heap
Swift runs node --max-old-space-size=256 --max-semi-space-size=16 against acp-bridge/dist/index.js. A tight heap so the agent does not eat your laptop. ACPBridge.swift line 343.
3. Pick the auth mode
personalOAuth strips ANTHROPIC_API_KEY from the subprocess env so the local Claude Agent SDK reaches Anthropic over the user's own OAuth, i.e. their Claude Pro or Max subscription. bundledKey sets ANTHROPIC_API_KEY for direct API billing. ACPBridge.swift lines 193-204, 349-353.
4. Boot the patched ACP entry
Node loads patched-acp-entry.mjs, which imports ClaudeAcpAgent and runAcp from @agentclientprotocol/claude-agent-acp@^0.29.2, then monkey-patches createSession and prompt to surface real token cost, rate-limit events, compaction boundaries, and tool progress. acp-bridge/src/patched-acp-entry.mjs.
5. Register four native MCP servers
The bridge walks the Contents/Resources and Contents/MacOS directories of the app bundle, checks existsSync() on each binary, and registers four MCP servers in one shot: playwright, macos-use (accessibility), whatsapp (Catalyst), google-workspace (Python). acp-bridge/src/index.ts lines 1049-1100.
6. Auto-approve tool permissions
When the ACP agent sends session/request_permission, the bridge replies with outcome.selected using the first allow_always or allow_once option. This matches Claude Code's bypassPermissions behaviour so the agent finishes tasks without prompting every 10 seconds. acp-bridge/src/index.ts lines 573-585.
The path a single prompt takes through the local agent
Prompt goes in. Token deltas and tool calls come out. Everything between those two events happens on your Mac, with one exception, the HTTPS round trip to Anthropic's model endpoint.
Prompt to action, inside Fazm
Your prompt
Typed into the Fazm chat, or spoken into the mic
Swift ACPBridge
Passes the prompt to the Node subprocess as JSON lines over stdin
Patched ACP agent
ClaudeAcpAgent.prompt() runs inside the local subprocess
Claude API call
Anthropic over HTTPS, billed to your Claude Max or Fazm's key
Tool use round
Claude emits a tool_use targeting one of the bundled MCP servers
macos-use or fazm_tools
Executes locally on your Mac, returns structured text or AX data
Result stream
Token deltas, thinking, tool progress forward to the Fazm UI
Why the agent does not prompt you on every tool call
If you have tried to automate a macOS workflow with Claude Desktop, you have seen the approval dialog. Fazm removes the dialog the same way Claude Code's bypassPermissions flag does: the bridge intercepts the JSON-RPC session/request_permission call and returns the first allow_always option. You can still stop an agent run from the Fazm UI at any time.
What the startup log looks like on a real Mac
The bridge writes one line per MCP registration and one line per tool decision. Tail the log file and you can watch the local Claude agent boot, load its servers, and respond to the first prompt.
Fazm vs Claude Desktop, for using your Mac
Both run locally. One of them is built to reach your apps out of the box.
| Feature | Claude Desktop | Fazm local Claude agent |
|---|---|---|
| Where Claude actually runs | A chat window that talks to api.anthropic.com from a renderer process | A signed Mac app that spawns @agentclientprotocol/claude-agent-acp as a child process on your Mac |
| How Claude reaches macOS apps | It does not, unless the user configures MCP servers themselves | Bundled mcp-server-macos-use binary reads the live AXUIElement tree over an MCP stdio socket |
| Permission to act on your behalf | Per-tool approval dialog on every call | All session/request_permission calls auto-approved in the bridge (bypassPermissions equivalent) |
| Runaway-tool safety | No budget: tools can hang forever | Tool timeout watchdog: 10s internal, 120s MCP, 300s default, user-overridable via FAZM_TOOL_TIMEOUT_SECONDS |
| Auth | One mode: your Claude account via Anthropic's first-party OAuth | Two modes, both local: personalOAuth (your Claude Pro/Max) or bundledKey (Fazm's Anthropic key) |
| MCP configuration surface | Hand-edit claude_desktop_config.json and restart | Four servers pre-wired out of the box; ~/.fazm/mcp-servers.json for extras |
| Shape of the deliverable | Official Mac .dmg but MCP servers are your problem | Signed, notarized DMG. Double-click install |
Claude Desktop is excellent at what it does, which is conversation plus optional MCP. Fazm's angle is the pre-wired agent-over-subprocess shape.
What the local Claude desktop agent actually does, after boot
Every behavior below is a line in the repo. Click any and the FAQ points you to the file.
Runtime behaviors shipped in every Fazm install
- Reads the focused window's AX tree without screenshots
- Clicks by role and description, not by pixel
- Stays on your Mac between model calls (AX stays local)
- Runs concurrent sessions with per-session ContinuationBox
- Survives stuck tools via a per-tool wall-clock watchdog
- Forwards compaction, rate limits, and task events to the UI
- Accepts user MCP servers from ~/.fazm/mcp-servers.json
- Uses Playwright MCP extension mode against real Chrome
Questions worth answering before you install
Frequently asked questions
What does 'local Claude desktop agent' actually mean in Fazm?
It means Claude's agent loop runs as a child process on your Mac, not in a web tab. Fazm ships a bundled Node.js runtime, and Swift spawns node --max-old-space-size=256 against acp-bridge/dist/index.js. The bridge imports ClaudeAcpAgent from @agentclientprotocol/claude-agent-acp@^0.29.2 (see acp-bridge/package.json line 15) and hosts the agent on your box. All MCP servers it talks to are also local processes on the same machine. The only thing that leaves your Mac is the actual HTTPS call to Anthropic's model endpoint.
How is this different from just opening Claude Desktop and editing claude_desktop_config.json?
Claude Desktop is a chat app that can call MCP servers you configure yourself. Fazm ships the Claude Agent SDK (the same subprocess-oriented runtime Claude Code uses) plus four native MCP servers pre-wired: mcp-server-macos-use for accessibility, whatsapp-mcp, google-workspace-mcp, and Playwright MCP. No config editing, no path juggling, no missing binaries. The agent also auto-approves tool permissions inside the bridge (session/request_permission is answered with the first allow_always option, see acp-bridge/src/index.ts lines 573-585), so it finishes multi-step tasks without prompting you on every tool call.
Which MCP servers are bundled, and where are they on disk?
Five in total. playwright from node_modules/@playwright/mcp/cli.js inside the bundle. macos-use as a Go binary at Contents/MacOS/mcp-server-macos-use. whatsapp at Contents/MacOS/whatsapp-mcp. google-workspace as a Python server at Contents/Resources/google-workspace-mcp with its own .venv. fazm_tools as a Unix-socket server Swift exposes for in-app operations. The registration code is in acp-bridge/src/index.ts, the BUILTIN_MCP_NAMES set on line 1266 names them all.
Do I need an Anthropic API key?
No. Fazm ships with two auth modes. personalOAuth uses your own Claude account via Anthropic's OAuth (your Pro or Max subscription covers the usage). bundledKey uses Fazm's Anthropic key. The mode is picked inside ACPBridge.swift at lines 193-204 and applied to the subprocess environment at lines 349-353. When personalOAuth is selected, ANTHROPIC_API_KEY is explicitly removed from the env so the SDK falls back to OAuth.
Does the agent hang forever if a tool call gets stuck?
No. The bridge runs a watchdog per tool call. TOOL_TIMEOUT_INTERNAL_MS is 10 seconds for ToolSearch and similar lookups. TOOL_TIMEOUT_MCP_MS is 120 seconds for anything starting with mcp__. TOOL_TIMEOUT_DEFAULT_MS is 300 seconds otherwise. On timeout the bridge synthesizes a completed-with-error event so Claude can recover, and the Swift side unblocks. You can override the budget via the Settings > Advanced > Tool Timeout control, which sets FAZM_TOOL_TIMEOUT_SECONDS. See acp-bridge/src/index.ts lines 77-120.
Where does my screen data go?
Nowhere by default. macos-use reads the live AXUIElement tree on your Mac, so what the agent sees is a structured string graph, not a bitmap. Only when a tool explicitly asks for a screenshot does ScreenCaptureManager in the Swift side grab a window capture, and that still only leaves your Mac if Claude includes it in the next model call. The AX tree never leaves your Mac. You can tail /tmp/fazm.log to audit every tool invocation.
Can I run two Claude tasks at once in one Fazm instance?
Yes. The Swift ACPBridge keeps per-session continuation boxes, pending message queues, interrupt flags, and tool counters. ACPBridge.swift lines 272-285 hold those maps. Each call to query() gets its own ContinuationBox, so a long-running automation in one session does not block a quick Q&A in another. The ACP subprocess routes responses back by sessionId.
How is this different from Claude's computer-use model?
Computer-use is a vision-based agent that sees a screenshot and outputs coordinates. Fazm's local Claude desktop agent uses tool calls into mcp-server-macos-use and talks to the real AXUIElement tree. No screenshot round trip, no pixel coordinates, no resolution dependence. You still get the computer-use model as a fallback when you ask for it, but it is the exception, not the default.
Can I drop my own MCP server in without rebuilding Fazm?
Yes. Put a JSON entry into ~/.fazm/mcp-servers.json using the same schema as Claude Code's mcpServers block: command, args, env, enabled. On next bridge start, the loop in acp-bridge/src/index.ts lines 1102-1130 appends your server to the four bundled ones and exposes its tools to the local Claude agent.
Is any of this open source so I can verify the claims?
Yes. Fazm is MIT licensed at github.com/mediar-ai/fazm. Every file and line number cited on this page exists in that repo on 2026-04-19. The ACP bridge lives in acp-bridge/src/. The Swift bridge lives in Desktop/Sources/Chat/ACPBridge.swift. The package.json declaring @agentclientprotocol/claude-agent-acp@^0.29.2 is at acp-bridge/package.json line 15.
Try the local Claude desktop agent
A signed Mac app, a Node subprocess running the Claude Agent SDK, and four bundled MCP servers. Bring your Claude Pro or Max subscription, or let Fazm use its own key. Read the source on github.com/mediar-ai/fazm.
Download Fazm for Mac →
Comments (••)
Leave a comment to see what others are saying.Public and anonymous. No signup.