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.9.0

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: ef04b24c884aaca67e7645f3051a2745b4897c754f95b7cf5ce572b5060d0744
bundle · manifest · loader.mjs

android-mcp v0.4.2 · UIAutomator2 10.2.4

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: b116553c1a9ce03589051200e8eb90866e28b13a847f2205e05b5a631e38f4d1
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: fb47bea7e8030a0a1c16dbaa058cf7e602bcc74c2f78490028f29458db3d5326
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: 8e4e158d48124e8be8a95cf08a149079c92b951e226cc7d6c8f7698011bf962d
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: 1ef8be24c47089409fd43d7c91f15ec3f578912bc214f2fd207f6b32935c001a
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