plonk-mcp
The Plonk MCP server lets AI agents control the macOS window manager: inspect and arrange windows, manage saved workspaces and snap zones, control keep-awake, take annotated screenshots, and manage agent access.
Inspect state: Return all monitors, open windows, zone sets, saved workspaces, keep-awake status, and agents.
Arrange windows: Move/resize windows with fractional frames and snap into zones.
Manage workspaces: Save, launch, and delete named desktop setups (including per-app launch items).
Manage zones: Create, assign, and remove snap-zone configs per monitor.
Control keep-awake: Toggle display/system awake on/off, optionally timed.
Screenshots & annotation: Capture screen/region/window; draw on images.
Control agent access: Select active agent; optionally restrict to exclusive control.
Provides macOS window management capabilities, including arranging windows into zones, saving and launching workspaces, capturing and annotating screenshots, and controlling keep-awake settings.
Drag a window, the zones light up, drop it in. Or skip the dragging and say it:
browser on the left 60%, terminal top right, notes bottom right
save that as a workspace called "review"
keep the screen awake for the next hour
screenshot the screen and tell me what looks off
Everything runs on your Mac. No account, no cloud, no telemetry.
Install
macOS 13+.
brew install --cask ostapondo/plonk/plonkOr download the latest release, unzip, and drop Plonk.app into Applications. The build is not notarized yet, so macOS will balk at the first launch — approve it under System Settings → Privacy & Security → Open Anyway.
Grant Accessibility when asked, then relaunch. Screen Recording is asked for separately, the first time you capture. Nothing else — no Full Disk Access, no Automation, no Keychain.
If you later move or rename Plonk.app (or its folder), macOS quietly ties the old grant to the old path: windows of newly launched apps stop being seen. Remove Plonk from Privacy & Security → Accessibility and grant it again.
To let an agent drive it (Node 18+):
claude mcp add plonk -- npx -y plonk-mcp # Claude Code
codex mcp add plonk -- npx -y plonk-mcp # Codex CLIAny MCP client works the same way — give it npx -y plonk-mcp as a stdio
server. One-pagers: Cursor (with a one-click
install button), Zed, Cline.
Several clients at once is fine; see Agents below.
A client that cannot spawn a process connects over HTTP instead:
npx -y plonk-mcp --http serves Streamable HTTP at
http://127.0.0.1:43918/mcp (loopback only, many clients per process,
--port to change).
Or build everything from source: clone the repo, run ./scripts/build.sh, and
point claude mcp add plonk -- node …/mcp/dist/server.js at a locally built
server (cd mcp && npm install && npm run build).
Related MCP server: computer-use
Check it yourself
Accessibility is the only way macOS lets one app move another's windows, and Screen Recording is what a screenshot costs. That is a lot to hand something you installed a minute ago, so none of this is a promise — it is all checkable.
One thing dials out, and you can switch it off. Every socket the app has open:
lsof -nP -i -a -p "$(pgrep -f 'Plonk.app/Contents/MacOS/plonk')"
plonk … TCP 127.0.0.1:43917 (LISTEN)One listener on loopback. The only outbound connection Plonk makes is the
update check: on launch and once a day it asks api.github.com for the latest
release, and sends nothing but a User-Agent naming the app and its version — no
identifier, no account, no analytics, no crash reporter. Turn it off under
Updates and it stops happening — including for agents, which get a 409 rather
than a connection made on your behalf, so the buttons on that page are the only
thing that can trigger one. nettop or Little Snitch will then show a process
that only ever listens. The URLs compiled into the app are that endpoint, the
releases page, and the issue tracker that opens when you click Report a bug —
Release.swift has all three.
A web page cannot drive it. The API is loopback-only and unauthenticated, so it refuses anything carrying headers a browser cannot suppress:
curl -so /dev/null -w '%{http_code}\n' -H 'Origin: https://example.com' \
http://127.0.0.1:43917/state
403There is not much else to hide. Package.swift declares
no third-party dependencies, so a build from source is this repo and nothing
else. Config is plain JSON at ~/Library/Application Support/Plonk/config.json.
Screenshots go where you send them. There is no account to make.
The MCP server is a separate npm package that depends only on the official MCP
SDK and zod. It speaks to 127.0.0.1:43917 and nowhere else.
Workspaces
A workspace is a desk you can put away. It remembers the apps, the frame of
every window, the monitor each one belongs on, and what each app should open on
the way up. Launching one opens whatever is closed, waits for the windows, and
puts them back — from the Workspaces page, or right-click the menu bar icon.
Rename, recapture or delete from the workspace's ⋯ menu.
Per app | Files, folders or URLs to open with it: a project folder for an editor, a set of tabs for a browser |
Per monitor | Windows return to the display they were captured on, keyed by display UUID so unplugging a monitor does not scramble them. Or pull the whole workspace onto one screen |
Already open | Running apps get moved, not relaunched. Turn that off to leave them alone and only open what is missing |
The catch | macOS cannot open an app straight into a position, so windows appear first and jump a moment later. A second window of the same app cannot be conjured — give it a file to open instead |
Zones
Five sets ship with it. Everything past that you draw yourself: any number of zones, any size, overlapping if you want — a narrow rail for chat, a wide middle split in two, a strip for the terminal. Or describe it and let the agent build it.
Editor | Click to split, |
Per monitor | Each screen gets its own set, remembered by display, not by index |
Overlap | Allowed — the smallest zone under the cursor wins |
Trigger | On drag, or only with a modifier held. Holding it inverts the mode, so a free move stays one keypress away |
Or none | Edge snapping instead: middles are halves, top is maximize, corners are quarters |
Hotkeys
And the rest
Keep awake | IOKit power assertions, not a jiggler. Display-on or system-only, pause on battery, auto while charging, timed sessions, and a menu bar icon that glows while it holds |
Screenshots | Region, window or screen through the native picker, then pen, arrow, rectangle, ellipse and highlighter. Saves at native resolution |
Notices | A panel in the top-right corner, not Notification Center: no permission to ask for, nothing left in your history, and it can show the screenshot instead of describing it |
Updates | One button on the Updates page. Plonk installs a build only if it is signed with the same certificate as the copy you are running — which is the same test macOS applies, so your Accessibility and Screen Recording grants carry over instead of being asked for again. Anything that fails the check is discarded and nothing is replaced. Switch the check off and the app never looks |
For agents
Frames are fractions of a monitor's visible area, origin top-left — which is why
"left 60%" is just {x: 0, y: 0, w: 0.6, h: 1}.
Tool | |
| Monitors, every open window and where it sits, zone sets, saved workspaces, awake status |
| Place any set of windows, across any number of monitors, in one call |
| Named desktops, launched from nothing |
| Drop a window into a numbered zone |
| Snap zones, per monitor |
| Keep-awake, optionally time-limited |
| Capture, mark up, hand the image back |
| Make an agent the user's active one, optionally the only one allowed to control |
Several agents can be connected at once. Every client registers itself, so
get_state lists who is online; the user picks an active agent from the menu
bar or the settings — or an agent does it with select_agent. An optional
strict mode locks changes to the active agent: everyone else keeps reading
state and taking screenshots, but gets a clear 409 on anything that moves
windows or edits config. Set PLONK_AGENT_NAME in a client's MCP config to
tell two sessions of the same client apart.
Under the hood
The app is the single source of truth; the MCP server is a stateless bridge.
The API binds to
127.0.0.1and refuses anything carrying browser headers, so an open web page cannot drive your desktop — see Check it yourself.Config is plain JSON at
~/Library/Application Support/Plonk/config.json.
Build
cd App && swift build # the app
./scripts/test.sh # 149 unit tests
./scripts/build.sh # produces Plonk.app
cd mcp && npm run build # the MCP serverApp/ is the Swift menu bar app, mcp/ the TypeScript MCP server. Point an
agent at AGENTS.md before it touches either.
build.sh signs with a Plonk Dev keychain identity and stops if it is
missing. macOS ties Accessibility and Screen Recording to the signature, and an
ad-hoc one changes every build, so create that certificate once (Keychain
Access → Certificate Assistant → Create a Certificate → type "Code Signing",
name it Plonk Dev) and rebuilds stop resetting permissions. Set
PLONK_SIGN_IDENTITY to sign with a different one.
If a permission was first granted while the app was ad-hoc signed, the old grant is pinned to a signature that no longer exists and every rebuild looks like a reset. Clear it once and grant again:
tccutil reset ScreenCapture dev.plonk.app
tccutil reset Accessibility dev.plonk.appReleases: bump MARKETING_VERSION and BUILD_NUMBER in
version.env. scripts/build.sh reads both into Info.plist.
scripts/release.sh then builds with a Developer ID certificate, notarizes,
staples the ticket into the bundle and writes Plonk-<version>.zip with the
sha256 the cask needs. It needs a paid Apple account and stored notarytool
credentials, and says how to get both if either is missing.
License
MIT © ostapondo
Maintenance
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to interact with macOS windows through yabai, providing window listing organized by Spaces and screenshot capture capabilities.2MIT
- Alicense-qualityCmaintenanceStandalone MCP server that gives AI agents full GUI control over macOS — screenshots, mouse, keyboard, apps, clipboard, and multi-display — with zero private dependencies.16MIT
- Alicense-qualityDmaintenanceProvides native macOS desktop automation for AI agents, enabling screen capture, mouse/keyboard control, window management, and iOS/Android simulator control in both foreground and background modes without focus stealing.3MIT
- Flicense-qualityBmaintenanceA local autonomous AI agent that watches your screen, understands the visual layout, and executes native OS commands (clicking, typing) without cloud APIs.2
Related MCP Connectors
Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.
User-owned memory for AI agents, Copilot, Claude, IDEs, CLIs, and chat apps over remote MCP.
SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ostapondo/Plonk'
If you have feedback or need assistance with the MCP directory API, please join our Discord server