Drive real Chrome, Android, macOS, Windows — and other Claude sessions — from Claude Code

Five MCP servers. Four of them let an agent tap, type, and reason through your actual apps — the browser you're logged into, the device in your hand, the desktop in front of you. The fifth lets one Claude session drive others. No headless sandbox, no fragile selectors, no install script.

chrome-mcp v0.2.14

Attaches over CDP to your running Chrome. Auto-launches it the first time you send a command. macOS, Linux, Windows. The agent sees your logged-in tabs — Gmail, internal dashboards, whatever.

Paste into ~/.claude.json and restart Claude Code:

{
  "mcpServers": {
    "chrome": {
      "command": "node",
      "args": [
        "-e",
        "(async()=>{const o=await import(\"node:fs\"),{homedir:s}=await import(\"node:os\"),{join:e}=await import(\"node:path\"),{pathToFileURL:a}=await import(\"node:url\"),i=process.env.CHROME_MCP_ENDPOINT||\"https://chrome-mcp.actuallyroy.com\",c=process.env.CHROME_MCP_CACHE_DIR||e(s(),\".chrome-mcp\"),r=e(c,\"loader.mjs\");if(!o.existsSync(r)||process.env.CHROME_MCP_REFRESH_LOADER){o.mkdirSync(c,{recursive:!0});const t=await fetch(i+\"/loader.mjs\");if(!t.ok)throw new Error(\"loader download failed: \"+t.status);o.writeFileSync(r,Buffer.from(await t.arrayBuffer()))}await import(a(r).href)})().catch(o=>{console.error(\"[chrome-mcp]\",o.message||o),process.exit(1)});"
      ]
    }
  }
}

sha256: 9115a7a110ef3bf48917074ff6ce35e2295cf6b8ef939b69fa1646adf2957b1c
bundle · manifest · loader.mjs

android-mcp v0.1.27 · UIAutomator2 10.2.1

Drives a real Android device or emulator via UIAutomator2. Same locator ergonomics as the Chrome version, but against the native view hierarchy (text, resource-id, content-desc, or XPath — no pixel matching). Requires adb on PATH. APK installs itself on first use.

Paste into ~/.claude.json and restart Claude Code:

{
  "mcpServers": {
    "android": {
      "command": "node",
      "args": [
        "-e",
        "(async()=>{const o=await import(\"node:fs\"),{homedir:s}=await import(\"node:os\"),{join:a}=await import(\"node:path\"),{pathToFileURL:i}=await import(\"node:url\"),c=process.env.ANDROID_MCP_ENDPOINT||\"https://chrome-mcp.actuallyroy.com\",e=process.env.ANDROID_MCP_CACHE_DIR||a(s(),\".android-mcp\"),r=a(e,\"loader.mjs\");if(!o.existsSync(r)||process.env.ANDROID_MCP_REFRESH_LOADER){o.mkdirSync(e,{recursive:!0});const t=await fetch(c+\"/android/loader.mjs\");if(!t.ok)throw new Error(\"android loader download failed: \"+t.status);o.writeFileSync(r,Buffer.from(await t.arrayBuffer()))}await import(i(r).href)})().catch(o=>{console.error(\"[android-mcp]\",o.message||o),process.exit(1)});"
      ]
    }
  }
}

sha256: 6a0d84a62cc72b5981cbbc4509c5abd883ba601ffbbce9b0dbf3a2df308fdc0c
bundle · manifest · loader.mjs · u2 APK

macos-mcp v0.2.12 · Swift helper 332 KB

Drives any macOS desktop app — AppKit, Catalyst, SwiftUI, Electron — through the OS Accessibility tree. Same locator ergonomics (text, role, AXIdentifier). Posts real CGEvent mouse + keyboard input and grabs PNGs via ScreenCaptureKit. Bundled Swift sidecar binary; macOS 14+; arm64 (Apple Silicon) only for now.

One-time TCC permission setup: grant Accessibility (required for AX inspection + input) and Screen Recording (required for screenshots) when the OS prompts. Call open_permissions_settings from the agent to jump to the right pane.

Paste into ~/.claude.json and restart Claude Code:

{
  "mcpServers": {
    "macos": {
      "command": "node",
      "args": [
        "-e",
        "(async()=>{const o=await import(\"node:fs\"),{homedir:c}=await import(\"node:os\"),{join:t}=await import(\"node:path\"),{pathToFileURL:e}=await import(\"node:url\"),i=process.env.MACOS_MCP_ENDPOINT||\"https://chrome-mcp.actuallyroy.com\",a=process.env.MACOS_MCP_CACHE_DIR||t(c(),\".macos-mcp\"),r=t(a,\"loader.mjs\");if(!o.existsSync(r)||process.env.MACOS_MCP_REFRESH_LOADER){o.mkdirSync(a,{recursive:!0});const s=await fetch(i+\"/macos/loader.mjs\");if(!s.ok)throw new Error(\"macos loader download failed: \"+s.status);o.writeFileSync(r,Buffer.from(await s.arrayBuffer()))}await import(e(r).href)})().catch(o=>{console.error(\"[macos-mcp]\",o.message||o),process.exit(1)});"
      ]
    }
  }
}

sha256: 02bab7f70ccd8019a6421ba9c07172b0b72246f6b0e00a1d4417361f4e0c0d21
bundle · manifest · loader.mjs · Swift helper

windows-mcp v0.1.1 · C# helper 25.0 MB

Drives any Windows desktop app — Win32, WPF, WinForms, WinUI 3, UWP, Edge / Chrome / Electron (with ARIA) — through the UI Automation tree. Same locator ergonomics (text, role, AutomationId). Synthesizes SendInput mouse + keyboard, grabs PNG screenshots via PrintWindow, runs OCR through built-in Windows.Media.Ocr for apps with no UIA tree (custom canvases, games). Bundled C# helper exe; Windows 10 1903+ (x64); requires .NET 8 Desktop Runtime (Microsoft-signed, ~55 MB install — windows-mcp prints a clear hint on first run if it's missing).

Paste into ~/.claude.json and restart Claude Code:

{
  "mcpServers": {
    "windows": {
      "command": "node",
      "args": [
        "-e",
        "(async()=>{const o=await import(\"node:fs\"),{homedir:a}=await import(\"node:os\"),{join:t}=await import(\"node:path\"),{pathToFileURL:i}=await import(\"node:url\"),c=process.env.WINDOWS_MCP_ENDPOINT||\"https://chrome-mcp.actuallyroy.com\",e=process.env.WINDOWS_MCP_CACHE_DIR||t(a(),\".windows-mcp\"),r=t(e,\"loader.mjs\");if(!o.existsSync(r)||process.env.WINDOWS_MCP_REFRESH_LOADER){o.mkdirSync(e,{recursive:!0});const s=await fetch(c+\"/windows/loader.mjs\");if(!s.ok)throw new Error(\"windows loader download failed: \"+s.status);o.writeFileSync(r,Buffer.from(await s.arrayBuffer()))}await import(i(r).href)})().catch(o=>{console.error(\"[windows-mcp]\",o.message||o),process.exit(1)});"
      ]
    }
  }
}

sha256: 0918ef1b118d27a5bd58a20616fd1c8cc978bd3eb0e0ffff5d7c89e1ad1c63c7
bundle · manifest · loader.mjs · C# helper exe

orch-mcp v0.2.1 · master-of-sessions

A different beast. Instead of driving an app, it lets one Claude Code session drive others — a master session that can adopt or spin up worker sessions, send them prompts, fork its own context into a new worker, and run them in background while you keep working. Each worker is a real persistent Claude session on disk; the master just shells out to claude --resume under the hood.

Workers are launched with Bash denied — they can analyze, plan, read files, and edit, but all shell execution stays in the master so you see it in one terminal. Send tasks in foreground (block-and-return) or background (fire, poll worker_resultlater). Fan out two workers in parallel and rejoin when both finish.

Paste into ~/.claude.json and restart Claude Code:

{
  "mcpServers": {
    "orch": {
      "command": "node",
      "args": [
        "-e",
        "(async()=>{const o=await import(\"node:fs\"),{homedir:s}=await import(\"node:os\"),{join:c}=await import(\"node:path\"),{pathToFileURL:a}=await import(\"node:url\"),i=process.env.ORCH_MCP_ENDPOINT||\"https://chrome-mcp.actuallyroy.com\",e=process.env.ORCH_MCP_CACHE_DIR||c(s(),\".orch-mcp\"),r=c(e,\"loader.mjs\");if(!o.existsSync(r)||process.env.ORCH_MCP_REFRESH_LOADER){o.mkdirSync(e,{recursive:!0});const t=await fetch(i+\"/orch/loader.mjs\");if(!t.ok)throw new Error(\"orch loader download failed: \"+t.status);o.writeFileSync(r,Buffer.from(await t.arrayBuffer()))}await import(a(r).href)})().catch(o=>{console.error(\"[orch-mcp]\",o.message||o),process.exit(1)});"
      ]
    }
  }
}

sha256: 9608857ba1fdf437f60e064b90d6de7520744eefad10bf61749e112ec0672aa9
bundle · manifest · loader.mjs

Requires the claude CLI on $PATH. Override with ORCH_MCP_CLAUDE_BIN.

Why not Playwright / Maestro / Appium?

Those are great for CI suites that run on clean sandboxes. They're frustrating for agent-driven work — screenshots on every step, flaky text matching, no live pause, no good way to see a React Native LogBox or an auto-dismissed toast. These MCPs are designed around that loop: one cheap outline call describes the page; refs are stable across calls; dev overlays get auto-dismissed before your next click; failed order? A toast popped up for 800 ms and we captured it.

How updates work

The bootstrap in the config block is a 600-char Node snippet. First run it downloads a ~4 KB loader to ~/.chrome-mcp/ or ~/.android-mcp/. The loader fetches the latest bundle on every launch, verifies its SHA-256, and runs it. Offline? It falls back to the cached bundle. No npm publish step, no manual upgrade.

What's in chrome-mcp

What's in android-mcp

What's in orch-mcp

Honest caveats