mcp-osascript
Let Claude control your Mac. Move windows, click menus, type text, read clipboard, manage browser tabs, take screenshots, run Shortcuts — 17 typed tools with input validation and security guardrails.

Quick Start
{
"mcpServers": {
"osascript": {
"command": "npx",
"args": ["-y", "mcp-osascript"]
}
}
}Add this to your Claude Desktop config (Settings → Developer → Edit Config), restart Claude, and you're ready.
Cursor / VS Code (Copilot)
{
"mcpServers": {
"osascript": {
"command": "npx",
"args": ["-y", "mcp-osascript"]
}
}
}Claude Code
claude mcp add osascript -- npx -y mcp-osascriptFrom source (development)
git clone https://github.com/m0rvayne/mcp-osascript.git
cd mcp-osascript && npm install
# then use: "command": "node", "args": ["/path/to/mcp-osascript/server/index.js"]Try These Prompts
Once installed, ask Claude:
Prompt | What happens |
"Open Safari and show me what tabs I have" | Launches Safari, reads all tab titles and URLs |
"Move the Finder window to the left half of my screen" | Resizes and positions the window |
"Click File → Export as PDF in Keynote" | Navigates the menu bar and clicks the item |
"Copy the URL from my active Chrome tab" | Reads browser tabs, finds the active one |
"Type 'Hello World' into the active text field" | Simulates keyboard input |
"Show a notification when you're done" | Displays a native macOS banner |
"What app am I using right now?" | Returns the frontmost app name and bundle ID |
"Press Cmd+Shift+4" | Triggers the screenshot shortcut |
"List all items in the Edit menu of VS Code" | Introspects the menu bar |
"Close the second window of Terminal" | Targets a specific window by index |
"Screenshot the Safari window and save it to my Desktop" | Captures just that window, not the whole screen |
"Which monitor is my Slack window on?" | Reads display geometry and window positions |
"Hide everything except my editor" | Hides apps without quitting them |
"Run my 'Daily Standup' shortcut" | Invokes an Apple Shortcut by name |
Tools
17 typed tools, each with input validation, error classification, and permission-aware error messages.
Tool | What it does | Permission |
| Execute any AppleScript or JXA script | None |
| Read clipboard as text | None |
| Write text to clipboard | None |
| Show macOS notification banner | None |
| Open URL in browser (http/https/mailto only) | None |
| Launch or bring app to front | None |
| Get active app name + bundle ID | Automation |
| List tabs in Safari, Chrome, or Arc | Automation |
| Type text into active app (max 500 chars) | Accessibility |
| Press key with modifiers (cmd+c, return, f5) | Accessibility |
| List / move / resize / minimize / fullscreen / close | Accessibility |
| List monitors — position, size, which is main | None |
| List or click menu items in any app | Accessibility |
| Capture full screen, a region, or an app window | Screen Recording |
| Hide, unhide, or quit an application | Accessibility |
| Open a file or folder, optionally in a given app | None |
| List or run Apple Shortcuts | None |
Self-Correcting Menus
When Claude tries to click a menu item that doesn't exist, the server automatically returns the list of available items at that level — so Claude can retry with the correct name. No other MCP server does this.
User: "Click File → Export as PDF in Preview"
Claude: calls app_menu click ["File", "Export as PDF"]
Server: "Menu item 'Export as PDF' not found in 'File'.
Available: ['New from Clipboard', 'Open...', 'Close', 'Save',
'Duplicate', 'Rename...', 'Export...', 'Export as PDF...']"
Claude: calls app_menu click ["File", "Export as PDF..."]
Server: "Clicked: File > Export as PDF..."Why mcp-osascript?
mcp-osascript | steipete (824★) | peakmojo (463★) | |
Typed tools with validation | 17 | 2 (generic) | 1 (generic) |
URL scheme allowlist | http/https/mailto | No | No |
Env isolation (child process) | PATH+HOME+LANG only | Full process.env | Full process.env |
Process group kill (no orphans) | SIGTERM→SIGKILL | No | No |
Error sanitization (paths, tokens) | Yes | No | No |
Prototype pollution protection | Object.create(null) | No | No |
Self-correcting menu click | Yes | No | No |
Integration tests | 80 | 0 | 0 |
Stdin piping (no temp files) | Yes | Temp files | Temp files |
Permissions
Tools work in three tiers:
No permission needed — clipboard, notifications, URLs, apps, files, displays, Shortcuts. Works immediately.
Automation — browser tabs, frontmost app. macOS prompts once per browser.
Accessibility — keyboard, windows, menus, hide/unhide. Grant once in System Settings → Privacy & Security → Accessibility.
Screen Recording — screenshots only. Grant in System Settings → Privacy & Security → Screen Recording.
When a permission is missing, the server tells you exactly what to do:
"Accessibility permission required. Grant access to 'osascript'
in System Settings > Privacy & Security > Accessibility."Testing
npm test80 integration tests covering all 17 tools — input validation, security boundaries (URL scheme blocking, prototype pollution, script size limits), timeout enforcement, permission error handling, and regressions for every finding of the security audit.
Security
run_osascriptexecutes arbitrary code — this is by design. The MCP client (Claude) is the trust boundary.Scripts piped via stdin to
/usr/bin/osascript— no temp files, no TOCTOU race conditions.Script size: 50 KB max. Output: 50K chars max, truncated on a UTF-8 character boundary (no mojibake in non-Latin output).
Error messages sanitized — filesystem paths, tokens, and passwords are stripped.
Child processes get minimal env:
PATH,HOME,LANGonly — no API keys or secrets leak.URL scheme allowlist —
file://,smb://,vnc://,javascript:all blocked.Handler dispatch uses
Object.create(null)— no prototype pollution.Externally-sourced text (browser tab titles, window titles, menu items, clipboard) is returned inside an explicit
<untrusted-data>envelope, so a web page that renames itself cannot smuggle instructions into the model's context.file_openrefuses anything that parses as a URL —open(1)resolves URLs as well as paths, so without that check it would quietly annulopen_url's scheme allowlist.screenshotnever overwrites an existing file unlessoverwrite: true, and the extension must match the format.Every list-building tool strips
|, CR and LF from app-supplied names, so a crafted window or tab title cannot forge a record.
Reliability
Process group kill on timeout — SIGTERM → 2s grace → SIGKILL. No orphaned processes.
Concurrency semaphore — max 5 simultaneous osascript processes.
Graceful shutdown —
server.close()with 10s force-exit safety net.Error classification — parses macOS error codes (-1728, -1743, -25211) into actionable messages. Supports English and Russian locales.
Requirements
macOS 13+ (Ventura or later)
Node.js 18+
License
MIT