macos-control-mcp
Provides browser automation capabilities in Google Chrome, including executing JavaScript, extracting page text, clicking elements by CSS selector, and filling form fields.
Allows AI agents to control the macOS operating system directly: capture screenshots, read screen text via OCR, click, type, scroll, manage apps, and access the accessibility tree.
Provides browser automation capabilities in Safari, including executing JavaScript, extracting page text, clicking elements by CSS selector, and filling form fields.
Enables control of the Slack desktop app through screen interaction and key inputs, allowing actions such as sending messages, switching channels, and navigating the interface.
Enables control of the Spotify desktop app through screen interaction, supporting play/pause, volume adjustment, and navigation of the player interface.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@macos-control-mcp Find the login button on the screen and click it."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
macos-control-mcp
Give AI agents eyes and hands on macOS.
What is this?
An MCP server that lets AI agents see your screen, read text on it, and interact — click, type, scroll — just like a human sitting at the keyboard. Unlike blind script runners, this MCP gives agents state awareness: they screenshot the screen, OCR it to get text with pixel coordinates, then click exactly where they need to.
Related MCP server: Daimon
The See-Think-Act Loop
┌─────────────────────────────────────────────────┐
│ │
│ 1. SEE screenshot / screen_ocr │
│ ↓ "What's on the screen?" │
│ │
│ 2. THINK AI reasons about the content │
│ ↓ "I need to click the Save btn" │
│ │
│ 3. ACT click_at / type_text / press_key│
│ "Click at (425, 300)" │
│ │
│ ↻ repeat │
└─────────────────────────────────────────────────┘This is what makes it powerful: the agent sees the result of every action and can course-correct, retry, or move on — just like you would.
Quick Start
No install needed — run directly with npx:
npx -y macos-control-mcpOn first run, a Python virtual environment is automatically created at ~/.macos-control-mcp/.venv with the required Apple Vision and Quartz frameworks. This takes ~60 seconds once and persists across updates.
Video Showcasing the MCP:
https://www.youtube.com/watch?v=aswlsElHV5o
Configure Your AI Client
All clients use the same command: npx -y macos-control-mcp
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"macos-control": {
"command": "npx",
"args": ["-y", "macos-control-mcp"]
}
}
}Restart Claude Desktop after saving.
claude mcp add macos-control -- npx -y macos-control-mcpAdd to .vscode/mcp.json in your workspace:
{
"servers": {
"macos-control": {
"command": "npx",
"args": ["-y", "macos-control-mcp"]
}
}
}Add to .cursor/mcp.json in your project:
{
"mcpServers": {
"macos-control": {
"command": "npx",
"args": ["-y", "macos-control-mcp"]
}
}
}Open Cline extension settings → MCP Servers → Add:
{
"macos-control": {
"command": "npx",
"args": ["-y", "macos-control-mcp"]
}
}Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"macos-control": {
"command": "npx",
"args": ["-y", "macos-control-mcp"]
}
}
}Permissions
macOS requires two permissions for full functionality:
Screen Recording — for screenshots and OCR
Accessibility — for clicking, typing, and reading UI elements
Go to System Settings → Privacy & Security and add your terminal app (Terminal, iTerm2, VS Code, etc.) to both lists. You'll be prompted on first use.
Tools (19)
See the screen
Tool | Description |
| Capture full screen or app window as JPEG |
| OCR the screen — returns text elements with pixel coordinates |
| Find specific text and get clickable x,y coordinates |
Interact with the screen
Tool | Description |
| Click at x,y coordinates (returns screenshot) |
| Double-click at x,y (returns screenshot) |
| Type text into the frontmost app |
| Press key combos (Cmd+S, Ctrl+C, etc.) |
| Scroll up/down/left/right |
App management
Tool | Description |
| Open or focus an application |
| List visible running apps |
Accessibility tree
Tool | Description |
| Get accessibility tree of an app window |
| Click a named UI element (returns screenshot) |
Browser automation
Tool | Description |
| Run JavaScript in the active browser tab |
| Get all visible text from the page (faster than OCR) |
| Click element by CSS selector (instant, precise) |
| Fill a form field by CSS selector |
Utilities
Tool | Description |
| Open URL in Safari or Chrome |
| Read clipboard contents |
| Write to clipboard |
Example Workflows
Fill out a web form
You: "Go to example.com/signup and fill in my details"
Agent:
1. open_url("https://example.com/signup")
2. screenshot() → sees the form
3. screen_ocr() → finds "Email" field at (300, 250)
4. click_at(300, 250) → clicks the email field
5. type_text("user@example.com")
6. find_text_on_screen("Submit") → gets button coordinates
7. click_at(350, 500) → submits the form
8. screenshot() → confirms successNavigate an unfamiliar app
You: "Change the font size to 16 in TextEdit"
Agent:
1. launch_app("TextEdit")
2. screenshot() → sees the app
3. get_ui_elements("TextEdit") → finds menu items
4. press_key("t", ["command"]) → opens Fonts panel
5. screenshot() → sees the font panel
6. find_text_on_screen("Size") → locates the size field
7. click_at(x, y) → clicks size field
8. type_text("16")
9. press_key("return")Extract info from an email
You: "Copy the tracking number from the shipping email in Mail"
Agent:
1. launch_app("Mail")
2. screenshot() → sees the inbox
3. find_text_on_screen("Your order has shipped") → locates the email
4. click_at(x, y) → opens the email
5. screenshot() → sees the email content
6. screen_ocr() → extracts all text including tracking number
7. set_clipboard("1Z999AA10123456784") → copies to clipboardTake a screenshot and describe what's on screen
You: "What do I have open right now?"
Agent:
1. screenshot() → captures the full screen
2. Describes what it sees: apps, windows, content
3. list_running_apps() → ["Safari", "Slack", "VS Code", "Spotify"]
4. "You have Safari open on Twitter, Slack with 3 unread messages, and VS Code editing index.ts"Debug a UI issue
You: "The submit button on this page isn't working, can you check what's happening?"
Agent:
1. screenshot() → sees the page
2. find_text_on_screen("Submit") → locates button at (500, 600)
3. get_ui_elements("Safari") → inspects accessibility tree
4. "The button has aria-disabled=true. There's a required field 'Phone' that's empty."
5. find_text_on_screen("Phone") → locates the field
6. click_at(x, y) → clicks the field
7. type_text("+1 555-0123")
8. click_at(500, 600) → clicks Submit again
9. screenshot() → confirms it workedControl Spotify
You: "Pause the music and turn the volume down"
Agent:
1. launch_app("Spotify")
2. screenshot() → sees Spotify is playing
3. find_text_on_screen("Pause") → locates pause button
4. click_at(x, y) → pauses playback
5. find_text_on_screen("Volume") → locates volume slider area
6. click_at(x, y) → adjusts volume
7. screenshot() → confirms paused and volume loweredWork with Finder
You: "Create a new folder on my Desktop called 'Project Assets' and move it to Documents"
Agent:
1. launch_app("Finder")
2. press_key("d", ["command", "shift"]) → opens Desktop
3. screenshot() → sees Desktop in Finder
4. press_key("n", ["command", "shift"]) → creates new folder
5. type_text("Project Assets")
6. press_key("return")
7. screenshot() → confirms folder createdSend a message in Slack
You: "Send 'build is green, ready to deploy' in the #engineering channel on Slack"
Agent:
1. launch_app("Slack")
2. screenshot() → sees Slack
3. press_key("k", ["command"]) → opens Quick Switcher
4. type_text("engineering")
5. press_key("return") → opens #engineering
6. screenshot() → confirms channel is open
7. click_at(x, y) → clicks message input
8. type_text("build is green, ready to deploy")
9. press_key("return") → sends message
10. screenshot() → confirms sentResearch and copy data from a website
You: "Look up the current price of AAPL on Google Finance and copy it"
Agent:
1. open_url("https://google.com/finance/quote/AAPL:NASDAQ")
2. screenshot() → sees the page loading
3. screen_ocr() → reads all text on the page
4. Finds the price: "$187.42"
5. set_clipboard("$187.42")
6. "Copied AAPL price $187.42 to your clipboard"Multi-app workflow
You: "Take what's in my clipboard, search for it in Safari, and screenshot the results"
Agent:
1. get_clipboard() → "best mechanical keyboards 2025"
2. launch_app("Safari")
3. press_key("l", ["command"]) → focuses address bar
4. type_text("best mechanical keyboards 2025")
5. press_key("return") → searches
6. screenshot() → captures the search results
7. "Here are the search results for 'best mechanical keyboards 2025'"Navigate System Settings
You: "Turn on Dark Mode"
Agent:
1. launch_app("System Settings")
2. screenshot() → sees System Settings
3. find_text_on_screen("Appearance") → locates the option
4. click_at(x, y) → opens Appearance settings
5. screenshot() → sees Light/Dark/Auto options
6. find_text_on_screen("Dark") → locates Dark mode option
7. click_at(x, y) → enables Dark Mode
8. screenshot() → confirms Dark Mode is onRequirements
macOS 13+ (Ventura or later)
Node.js 18+
Python 3.9+ (pre-installed on macOS — needed for OCR and mouse control)
How It Works
Screenshots — native
screencaptureCLIOCR — Apple Vision framework (VNRecognizeTextRequest) via Python bridge, returns text with bounding box coordinates
Mouse — Quartz Core Graphics events via Python bridge for precise pixel-level control
Keyboard & Apps — AppleScript via
osascriptfor key presses, app launching, and UI element interactionPython env — auto-managed venv at
~/.macos-control-mcp/.venv/with only two packages (pyobjc-framework-Vision,pyobjc-framework-Quartz)
Troubleshooting
"Permission denied" or blank screenshots → Add your terminal to System Settings → Privacy & Security → Screen Recording
Clicks don't work → Add your terminal to System Settings → Privacy & Security → Accessibility
Python setup fails
→ Ensure python3 is in your PATH. Run python3 --version to check. Non-Python tools (keyboard, apps, clipboard) still work without it.
OCR returns empty results
→ Make sure Screen Recording permission is granted. Try a full-screen OCR first (without the app parameter).
"App not found" errors → Use the exact app name as shown in Activity Monitor (e.g., "Google Chrome" not "Chrome").
License
Available Tools
22 toolsbatch_actionsA
PREFERRED: Always use this tool instead of calling individual action tools (click_at, type_text, press_key, launch_app, etc.) one at a time. Combine multiple steps into a single batch call — this is dramatically faster. Returns a single screenshot by default (disable with return_screenshot=false). Only use individual tools when you need to read the result of one action before deciding the next. Stops on first error. Max 20 actions per call.
Example — open Notes and write text (1 call instead of 6): [{ "action": "launch_app", "name": "Notes" }, { "action": "key", "key": "n", "modifiers": ["command"] }, { "action": "set_clipboard", "text": "Hello\nWorld" }, { "action": "key", "key": "v", "modifiers": ["command"] }]
Inputs: actions (required), delay_between_ms, return_screenshot.
click_atA
Click at x,y screen coordinates. Returns a screenshot by default (disable with return_screenshot=false). Use screenshot + screen_ocr to find coordinates first. Prefer batch_actions when combining with other actions.
Inputs: x (required), y (required), return_screenshot.
click_by_textA
Click a button, link, tab, radio, or checkbox by its visible text. Much more reliable than coordinate clicking. Scrolls element into view before clicking.
Inputs: text (required), element_type, index, browser.
click_elementA
Click a named UI element in an app window. Returns a screenshot by default (disable with return_screenshot=false). Use get_ui_elements to discover element names. Prefer batch_actions when combining with other actions.
Inputs: app (required), name (required), return_screenshot.
double_click_atA
Double-click at x,y screen coordinates. Returns a screenshot by default (disable with return_screenshot=false). Prefer batch_actions when combining with other actions.
Inputs: x (required), y (required), return_screenshot.
execute_javascriptB
Run JavaScript in the active browser tab. Much faster than screenshot+OCR for web pages. Returns the result.
Inputs: code (required), browser.
fill_by_labelA
Fill a form field by its label text. Finds the input/textarea associated with the label and fills it. Works with React/Vue/Angular apps. On failure, lists available field labels for debugging.
Inputs: label (required), value (required), browser.
find_text_on_screenA
Find specific text on screen and get its clickable coordinates. Like Ctrl+F for the entire screen. Returns matches with centerX/centerY for use with click_at.
Inputs: text (required), app.
get_clipboardA
Read the current text contents of the macOS clipboard.
Inputs: no named parameters.
get_page_elementsA
Get all interactive elements (buttons, inputs, selects, radios, checkboxes, links) from the current browser page. Returns structured text showing each element's type, label, value, and state. Use this instead of screenshot+OCR to understand web page content. Then use click_by_text or fill_by_label to interact.
Inputs: browser.
get_page_textA
Get all visible text from the current browser page. Faster than OCR for web content.
Inputs: browser.
get_ui_elementsA
Get the accessibility tree of an app window. Returns UI element roles, names, and positions.
Inputs: app (required).
launch_appA
Open or focus a macOS application by name. Prefer batch_actions when combining with other actions.
Inputs: name (required).
list_running_appsA
List all visible running macOS applications.
Inputs: no named parameters.
open_urlB
Open a URL in Safari or Chrome.
Inputs: url (required), browser.
press_keyA
Press a key combo (e.g. press 's' with ['command'] for Cmd+S). Supports a-z, 0-9, return, tab, space, delete, escape, arrows, f1-f12. If app is specified, focuses that app first. Prefer batch_actions when combining with other actions.
Inputs: key (required), modifiers, app.
screen_ocrA
OCR the screen using Apple Vision. Returns every text element with pixel coordinates (x, y, centerX, centerY). Use centerX/centerY with click_at to click on any text.
Inputs: app, min_confidence, max_elements, compact, +1 more.
screenshotA
Capture a screenshot of the entire screen or a specific app window. Returns a PNG image.
Inputs: app.
scrollA
Scroll in the frontmost application. Prefer batch_actions when combining with other actions.
Inputs: direction (required), amount.
select_optionA
Select a dropdown option by the dropdown's label and the option text. On failure, lists available options for debugging.
Inputs: label (required), option (required), browser.
set_clipboardC
Write text to the macOS clipboard.
Inputs: text (required).
type_textA
Type text using keyboard input. If app is specified, focuses that app first to ensure keystrokes go to the right place. Without app, types into the frontmost app. Prefer batch_actions when combining with other actions.
Inputs: text (required), app.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
Tools are mostly distinct; many clicking variants (click_at, click_by_text, click_element) and text-finding tools (screen_ocr, find_text_on_screen) have clear descriptions but could cause minor confusion. Overall, the boundaries are well-defined.
Names follow a consistent verb_noun pattern in snake_case, e.g., click_at, get_clipboard, press_key. The exception is batch_actions, which is a noun phrase, slightly breaking the pattern.
With 22 tools covering a broad range of macOS automation tasks (clicking, typing, OCR, app management, web interaction), the count is well-scoped and each tool serves a clear purpose without being excessive.
The set covers common automation needs comprehensively, including clicking, typing, clipboard, app launching, and web page element retrieval. Minor gaps like drag-and-drop or gesture actions exist, but core workflows are well-supported.
Maintenance
Related MCP Connectors
Connect AI assistants to your approved computer for files, commands, browser and desktop tasks.
- DNS-DATA RemoteDeprecatedcom.dns-data
Connect AI assistants to authorized Windows and macOS devices for files, commands, and management.
Mac & Windows: let ChatGPT, Claude & Cursor use your email, calendar, iMessage, Teams, files.
Let your AI agent control real iPhones: screenshots, tap, swipe, type and open apps.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to automate macOS desktop tasks including mouse control, keyboard input, screenshots, window management, and UI interaction.10 npm415MIT
- FlicenseNot gradedqualityBmaintenanceA local daemon for macOS that gives any MCP-capable AI client eyes, hands, and a face — screen capture, accessibility tree, mouse/keyboard actions, and an overlay — with a built-in security ceiling.-
- AlicenseNot gradedqualityDmaintenanceGives AI assistants full macOS desktop control via screenshots, mouse, keyboard, scrolling, and app management.2,318 npmMIT
- AlicenseAqualityDmaintenanceEnables AI agents to control macOS desktop apps via screenshots, mouse clicks, keyboard input, accessibility queries, and AppleScript.1112 npmMIT