| name | native-devtools-mcp-automation |
| description | MCP server for computer use & browser automation - screenshot, OCR, click, type, find_text, Chrome/Electron CDP, template matching on macOS, Windows & Android |
| triggers | ["automate a desktop application","take a screenshot and find text with OCR","control a native app with accessibility tree","automate Chrome or Electron with CDP","click or type into a window using coordinates","automate an Android device over ADB","use template matching to find UI elements","set up native devtools MCP server"] |
Native Devtools MCP Automation
Skill by ara.so โ Devtools Skills collection.
native-devtools-mcp is an MCP server that gives AI agents direct control over native desktop apps, Chrome/Electron browsers, and Android devices. It provides screenshots, OCR, accessibility-first element lookup, input simulation, window management, Chrome DevTools Protocol (CDP), and ADB โ all in one local server.
Works with Claude Desktop, Claude Code, Cursor, and any MCP-compatible client.
Platform Support
- macOS: Full support with Accessibility tree dispatch (preferred), screenshots, OCR (Vision), input simulation
- Windows: UI Automation, screenshots, OCR (Windows Media OCR), input simulation
- Android: ADB-based screenshots, uiautomator text lookup, input, app management
- Chrome/Electron: CDP-based DOM automation for web content and Electron apps
Installation
Quick Start (no install)
npx -y native-devtools-mcp
Global Install
npm install -g native-devtools-mcp
Build from Source (Rust)
git clone https://github.com/sh3ll3x3c/native-devtools-mcp
cd native-devtools-mcp
cargo build --release
Setup Wizard
Run the setup wizard to configure permissions and MCP clients:
npx native-devtools-mcp setup
This will:
- Check permissions (Accessibility and Screen Recording on macOS)
- Detect MCP clients (Claude Desktop, Claude Code, Cursor)
- Write the correct configuration
MCP Client Configuration
Claude Desktop (macOS)
Config file: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"native-devtools": {
"command": "/Applications/NativeDevtools.app/Contents/MacOS/native-devtools-mcp"
}
}
}
Claude Desktop (Windows)
Config file: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"native-devtools": {
"command": "C:\\path\\to\\native-devtools-mcp.exe"
}
}
}
Claude Code / Cursor / Other MCP Clients
{
"mcpServers": {
"native-devtools": {
"command": "npx",
"args": ["-y", "native-devtools-mcp"]
}
}
}
Claude Code Auto-Approval
To avoid approving every tool call, add to .claude/settings.local.json:
{
"permissions": {
"allow": ["mcp__native-devtools__*"]
}
}
Three Approaches to Interaction
1. Visual (Universal)
Works with any app โ games, Qt apps, custom renderers, anything without an accessible tree.
Key Tools: take_screenshot, find_text, click, type_text, find_image
2. AX Dispatch (macOS - Preferred for Native Apps)
Element-precise automation for AppKit/SwiftUI apps. Doesn't move the cursor or steal focus.
Key Tools: take_ax_snapshot, ax_click, ax_set_value, ax_select
3. CDP (Chrome/Electron)
DOM-level automation for web content and Electron apps.
Key Tools: cdp_connect, cdp_find_elements, cdp_click, cdp_fill, cdp_navigate
Core Tools Reference
Screenshot & OCR
take_screenshot()
take_screenshot(window_id: number)
take_screenshot(
x: number,
y: number,
width: number,
height: number
)
find_text(
text: string,
window_id?: number,
x?: number,
y?: number,
width?: number,
height?: number
)
Input Simulation
click(x: number, y: number)
click(x: number, y: number, window_id: number)
click(x: number, y: number, screenshot_id: string)
click(x: number, y: number, double_click: true)
click(x: number, y: number, right_click: true)
drag(
from_x: number,
from_y: number,
to_x: number,
to_y: number,
window_id?: number
)
type_text(text: string)
(: , ?: [])
(: , : )
Window Management
list_windows()
focus_window(window_id: number)
launch_app(app_name: string, args?: string[])
quit_app(app_name: string)
record_window(
window_id: number,
duration_ms: number,
interval_ms?: number
)
macOS Accessibility Tree (AX Dispatch)
take_ax_snapshot(
window_id?: number,
include_descriptions?: boolean
)
ax_click(uid: string)
ax_set_value(uid: string, value: string)
ax_select(uid: string)
ax_inspect(uid: string)
AX Dispatch Flow Example:
const windows = await list_windows();
const settingsWindow = windows.find(w => w.app_name === "System Settings");
const snapshot = await take_ax_snapshot(settingsWindow.id);
await ax_click("a12");
const updatedSnapshot = await take_ax_snapshot(settingsWindow.id);
Template Matching
load_image(
path: string,
name: string
)
find_image(
template_name: string,
screenshot_id?: string,
window_id?: number,
threshold?: number
)
Template Matching Flow:
await load_image("/path/to/button.png", "submit_button");
const screenshot = await take_screenshot();
const matches = await find_image("submit_button", screenshot.id);
if (matches.length > 0) {
await click(matches[0].x, matches[0].y);
}
Chrome DevTools Protocol (CDP)
cdp_connect(
port: number,
host?: string
)
cdp_navigate(url: string)
cdp_find_elements(
query: string,
limit?: number
)
cdp_take_dom_snapshot()
cdp_click(uid: string)
cdp_hover(uid: string)
cdp_fill(uid: string, value: string)
cdp_type(uid: string, text: string)
cdp_press_key(key: string)
cdp_wait_for(
text?: string[],
selector?: string,
timeout_ms?:
)
(: )
(: , ?: )
(?: )
(: )
()
(: )
(: )
(: )
(: )
CDP Flow Example:
await launch_app(
"Google Chrome",
["--remote-debugging-port=9222", "--user-data-dir=/tmp/chrome-profile"]
);
await cdp_connect(9222);
await cdp_navigate("https://example.com");
const elements = await cdp_find_elements("search");
await cdp_fill("d1", "search query");
await cdp_press_key("Enter");
await cdp_wait_for(["Results"], null, 5000);
const dom = await cdp_take_dom_snapshot();
Android (ADB)
adb_devices()
adb_screenshot(device_id?: string)
adb_find_text(
text: string,
device_id?: string
)
adb_tap(
x: number,
y: number,
device_id?: string
)
adb_type(text: string, device_id?: string)
adb_press_key(
key: string,
device_id?: string
)
adb_swipe(
from_x: number,
from_y: number,
to_x: number,
to_y: number,
duration_ms?: number,
device_id?: string
)
adb_launch_app(
package: string,
device_id?:
)
(
: ,
?:
)
Common Patterns
Pattern 1: Visual Navigation with OCR
const screenshot = await take_screenshot();
const matches = await find_text("Submit");
if (matches.length > 0) {
await click(matches[0].center_x, matches[0].center_y, screenshot.id);
}
Pattern 2: AX Dispatch on macOS (Preferred)
const windows = await list_windows();
const targetWindow = windows.find(w => w.title.includes("Notes"));
const snapshot = await take_ax_snapshot(targetWindow.id, true);
await ax_click("a5");
await ax_set_value("a8", "Meeting notes for 2026-05-18");
Pattern 3: Web Automation with CDP
await launch_app(
"Google Chrome",
["--remote-debugging-port=9222", "--new-window", "https://github.com/login"]
);
await cdp_connect(9222);
const elements = await cdp_find_elements("login");
await cdp_fill("d1", process.env.GITHUB_USERNAME);
await cdp_fill("d2", process.env.GITHUB_PASSWORD);
const submitElements = await cdp_find_elements("Sign in");
await cdp_click(submitElements[0].uid);
await cdp_wait_for(null, "header", 10000);
Pattern 4: Electron App Automation
await launch_app(
"Signal",
["--remote-debugging-port=9223"]
);
await cdp_connect(9223);
await cdp_find_elements("compose");
await cdp_click("d1");
await cdp_type("d1", "Hello from automation!");
await cdp_press_key("Enter");
Pattern 5: Template Matching for Custom UI
await load_image("/path/to/gear-icon.png", "settings_icon");
const screenshot = await take_screenshot();
const matches = await find_image("settings_icon", screenshot.id, null, 0.85);
if (matches.length > 0) {
await click(matches[0].x, matches[0].y, screenshot.id);
}
Pattern 6: Android UI Automation
const devices = await adb_devices();
const deviceId = devices[0].id;
const screenshot = await adb_screenshot(deviceId);
const matches = await adb_find_text("Settings", deviceId);
if (matches.length > 0) {
await adb_tap(matches[0].center_x, matches[0].center_y, deviceId);
}
await adb_tap(500, 300, deviceId);
await adb_type("Hello Android", deviceId);
await adb_press_key("KEYCODE_ENTER", deviceId);
Operational Safety
- Hands off: When the agent is clicking/typing, don't move your mouse or type. Real hardware inputs conflict with simulated ones.
- Focus matters: Ensure the target window is visible. If a popup steals focus, clicks may land in the wrong window.
- Prefer AX Dispatch on macOS: For native apps, use
take_ax_snapshot + ax_click / ax_set_value to avoid moving the cursor and stealing focus.
Permissions (macOS)
The server requires:
- Accessibility: For input simulation and AX tree access
- Screen Recording: For screenshots
Grant both in System Settings โ Privacy & Security โ Accessibility and Screen Recording.
Without these, clicks silently fail and screenshots return black rectangles.
Troubleshooting
macOS: Clicks don't work
- Cause: Missing Accessibility permission
- Fix: System Settings โ Privacy & Security โ Accessibility โ enable the app
macOS: Screenshots are black
- Cause: Missing Screen Recording permission
- Fix: System Settings โ Privacy & Security โ Screen Recording โ enable the app
CDP: Can't connect
ADB: No devices found
- Cause: USB debugging not enabled or device not connected
- Fix:
- Enable USB debugging on Android device (Settings โ Developer options)
- Connect via USB or Wi-Fi (
adb tcpip 5555, then adb connect <ip>:5555)
- Run
adb devices to verify
OCR finds nothing
- Cause: Text too small, low contrast, or obscured
- Workarounds:
- Use template matching instead (
load_image + find_image)
- Use AX Dispatch on macOS (
take_ax_snapshot)
- Use CDP for web content (
cdp_find_elements)
Windows: UI Automation elements missing
- Cause: Some Qt/Electron apps don't expose UI Automation
- Workaround: Use visual approach (screenshots + OCR) or CDP for Electron
Real-World Examples
Example 1: Automate System Settings on macOS
await launch_app("System Settings");
const windows = await list_windows();
const settingsWindow = windows.find(w => w.app_name === "System Settings");
const snapshot = await take_ax_snapshot(settingsWindow.id, true);
await ax_click("a12");
const privacySnapshot = await take_ax_snapshot(settingsWindow.id, true);
await ax_click("a25");
Example 2: Fill a Web Form with CDP
await launch_app("Google Chrome", [
"--remote-debugging-port=9222",
"--new-window",
"https://example.com/contact"
]);
await cdp_connect(9222);
const fields = await cdp_find_elements("contact form");
await cdp_fill("d1", "John Doe");
await cdp_fill("d2", "john@example.com");
await cdp_fill("d3", "This is a test message.");
const submitBtn = await cdp_find_elements("Submit");
await cdp_click(submitBtn[0].uid);
await cdp_wait_for(["Thank you"], null, );
Example 3: Android App Testing
const devices = await adb_devices();
const device = devices[0].id;
await adb_launch_app("com.example.app", device);
await new Promise(resolve => setTimeout(resolve, 2000));
const screenshot = await adb_screenshot(device);
const signInMatches = await adb_find_text("Sign In", device);
if (signInMatches.length > 0) {
await adb_tap(signInMatches[0].center_x, signInMatches[0].center_y, device);
}
const usernameMatches = await adb_find_text("Username", device);
await adb_tap(usernameMatches[0].center_x, usernameMatches[0].center_y, device);
await adb_type(process.env., device);
(, device);
(process.., device);
(, device);
Example 4: Automate VS Code with CDP
await launch_app("Visual Studio Code", ["--remote-debugging-port=9224"]);
await cdp_connect(9224);
await cdp_press_key("Meta+Shift+P");
await cdp_wait_for(null, ".quick-input-widget", 2000);
await cdp_type(".quick-input-widget input", "File: Open File");
await cdp_press_key("Enter");
await cdp_eval(`
document.querySelector('.monaco-inputbox input').value = '/path/to/file.js';
document.querySelector('.monaco-inputbox input').dispatchEvent(new Event('input'));
`);
await cdp_press_key("Enter");
Best Practices
- Always verify state: Take a screenshot or snapshot after an action to confirm it succeeded.
- Use the right tool for the job:
- Native macOS apps โ AX Dispatch
- Web/Electron โ CDP
- Custom/legacy UI โ Visual (screenshots + OCR or template matching)
- Handle timing: Add
cdp_wait_for or manual delays after navigation/clicks before the next action.
- Reference env vars for secrets: Never hardcode credentials in automation scripts.
- Use
record_window for debugging: Record a window's state over time to understand UI behavior.
- Test permissions early: Run
npx native-devtools-mcp setup before writing automation scripts.
Additional Resources