plonk-mcp
This server is the MCP bridge to Plonk, a macOS toolbox, letting agents inspect and control the desktop, windows, workspaces, zones, screenshots, OCR, measuring, keep-awake, and app updates.
Desktop state: Read screens, windows, apps, zone sets, workspaces, app rules, keep-awake status, disabled features, and update info via
get_state.Window layouts: Move/resize windows with fractional frames (
apply_layout), snapshot them as workspaces (save_workspace/save_layout), and relaunch arrangements (launch_workspace/apply_saved_layout).Snap zones: Create, assign, and delete zone sets; snap a window into a numbered or named zone (
snap_window,save_zone_set,assign_zone_set,delete_zone_set).App rules: Make an app's new windows open into a chosen zone (
set_app_rule) or clear that rule (clear_app_rule).Screenshots and annotation: Capture full screen, a specific app window, or interactive regions/windows; annotate with rectangles, arrows, pen, highlights, etc.
OCR: Extract text from the screen or an image file, with per-line confidence and bounding boxes (
extract_text).Screen measurement: Measure distances/sizes in points and pixels, or hand the user an interactive ruler (
measure_screen).Keep-awake: Prevent sleep and optionally keep chat status available, with end conditions by process, time, or countdown (
set_awake).Agent coordination: Select the active agent or enable exclusive control (
select_agent).Updates: Check for and install newer Plonk releases while preserving permissions (
check_for_update,install_update).
Provides macOS window management capabilities, including arranging windows into zones, saving and launching workspaces, capturing and annotating screenshots, and controlling keep-awake settings.
One app instead of eight menu bar icons
Plonk is a small suite of macOS tools that share one interface, one command palette and one automation surface. Use all of it, or switch off every module you do not need.
Capture and understand the screen. Take a region, window or full-screen screenshot, annotate it, pin a live crop, copy otherwise unselectable text with on-device OCR, or measure an interface in points and pixels.
Keep the Mac and your workflow moving. Hold sleep off until a timer, time of day or process exit; find the pointer, add crosshairs or click rings; inspect the front app's real shortcuts; and run anything by name from one palette.
Arrange the desk. Draw snap zones, save workspaces that remember each monitor, move windows by drag, shortcut or voice, and let app rules put new windows where they belong.
Let an agent use the same tools. Plonk ships an MCP server and CLI for layouts, workspaces, screenshots, OCR, measuring, keep-awake and the rest. The app stays the source of truth, and the whole surface remains on your Mac.
It is early. Version 0.4.x, one author. Shortcuts, zone files and workspaces are settled. The MCP tool names and the HTTP API are not, and can still change between minor versions. CHANGELOG.md says what moved.
Related MCP server: macos-desktop-control
Install
macOS 13 or newer, Apple silicon.
brew install --cask ostapondo/plonk/plonkGrant Accessibility when it asks, then relaunch. Screen Recording is asked for separately, the first time you capture. Nothing else: no Full Disk Access, no Automation, no Keychain.
Plonk is signed but not notarized, so macOS holds a copy you download by hand. The cask takes care of that for you.
Running it alongside Rectangle or Magnet is fine, as long as their shortcuts do not collide.
Checking what you downloaded. Notarizing an app means paying Apple for a developer account, and this project does not have one, so Plonk is signed with a certificate it made itself. That means macOS cannot tell you who built the app. There is a check that answers a more useful question, and you can run it yourself: was this exact file built by GitHub from the source in this repository?
gh attestation verify Plonk-<version>.zip --repo ostapondo/PlonkPut the number from the file name in place of <version>. The command comes
with the GitHub CLI, which is brew install gh. It
prints the commit and the workflow run that built the archive. If the file was
altered after it was built, or was not built from this repository at all, the
command fails and tells you so.
Every release also carries a small Plonk-<version>.zip.sha256 file. Put it
beside the zip and run shasum -a 256 -c Plonk-<version>.zip.sha256 to confirm
the download arrived complete and unchanged. That file is signed the same way
as the zip, so gh attestation verify works on it too. The attestation itself
is also on the release as Plonk-<version>.zip.sigstore.json, for anyone who
wants to check it offline with gh attestation verify --bundle or with
cosign instead of asking GitHub.
Why macOS holds a downloaded copy. The certificate is self-signed rather than an Apple Developer ID, because notarizing needs a paid Apple account and this project does not have one. macOS cannot vouch for who built it, and says so. The cask skips that check by clearing the quarantine flag for you.
That is a check skipped on your behalf, so here is a stronger one to run before you open anything:
gh attestation verify $(brew --cache)/downloads/*--Plonk-*.zip \
-R ostapondo/plonkIt prints the commit and the GitHub Actions run that built this exact archive. Apple's stamp would tell you a build passed a malware scan. This tells you the binary came from the source in this repository, with no laptop in between.
Without Homebrew. Download the latest release, unzip, drop Plonk.app into Applications, then clear the flag yourself, which is all the cask does:
xattr -dr com.apple.quarantine /Applications/Plonk.appOr do the Gatekeeper detour once: open Plonk, dismiss the warning, then System Settings, Privacy & Security, scroll to Security, Open Anyway.
If you move or rename Plonk.app later, macOS ties the old grant to the old path and windows of newly launched apps stop being seen. Remove Plonk from Privacy & Security, Accessibility, and grant it again.
On an Intel Mac. Releases are built for Apple silicon only, so the download will not run. Building from source ought to work, see Build, but nobody has tried it and a report either way is welcome in issues.
Next to a tiling manager. yabai and Amethyst own every window on screen and will pull windows straight back out of a zone. Run one or the other.
Removing it. brew uninstall --cask plonk, or quit Plonk and drag it to the
trash. Then delete ~/Library/Application Support/Plonk/. The login item goes
with the app, and nothing was written anywhere else.
The tools
The modules share settings, shortcuts, the menu bar, the command palette and the same local API. Turning one off removes its page, menu items, shortcuts, manager and agent routes while keeping its settings for later.
Screenshots and annotation | Capture a region, window or full screen at native resolution, then add pen strokes, arrows, shapes or highlights before saving |
On-device OCR |
|
Screen ruler |
|
Live crops | Pin a changing part of the screen above everything else. It streams live and is never written to disk |
Pulse | Keep the Mac awake by timer, schedule, open app, charging state or process lifetime. It uses real power assertions and hands sleep back when the session ends |
Pointer tools | Find the cursor, add configurable crosshairs or click rings, and jump the pointer to the next display |
Shortcut guide | Read every shortcut the front app actually exposes through its menus instead of relying on a stale cheat sheet |
Draw window places, save apps and documents as a desk, and return everything to the correct displays. Workspace details | |
Voice, CLI and agents | Run the same tools by name, from speech, the |
All but the shortcut guide can be switched off, from Tools in the menu bar dropdown or the Tools page. Off means gone: out of the sidebar, out of the menu, its shortcuts released, and its tools refused to agents until it is back on. The same switches cover zones, workspaces and voice, so desk arrangement can stand down while the rest of Plonk keeps running.
If you are coming from Rectangle, Magnet, Loop or Raycast, the familiar window
shortcuts can come with you. One button imports Rectangle bindings and existing
rectangle:// scripts need one substitution. Coming from Rectangle
has the details.
Longer versions: Zones · Workspaces · Hotkeys · Everything else · Coming from Rectangle
For agents
An agent gets the same toolbox as the menu bar: capture or read the screen, measure an interface, control an awake session, inspect the desk, arrange it and save the result.
keep the Mac awake until this build finishes
read the error out of that dialog and tell me what it says
how tall is that toolbar, in points and in pixels
capture this window and highlight the warning
put the browser on the left, then save this desk as "review"Twenty-two tools cover state, capture, OCR, measuring, keep-awake, layouts, workspaces and zones. Several agents can connect at once, each registering itself, with an optional mode that locks changes to the active one.
Setup, if you want the plonk CLI or an agent driving it (Node 18+):
claude mcp add plonk -- npx -y plonk-mcp # Claude Code
codex mcp add plonk -- npx -y plonk-mcp # Codex CLIIn Claude Code it can also be a plugin: same server, pinned to the release it shipped with rather than to whatever npm serves as latest.
/plugin marketplace add ostapondo/plonk
/plugin install plonk@plonkFor Claude Desktop there is nothing to type. Download plonk-<version>.mcpb
from the latest release and open it. The bundle carries the server and
its dependencies, so no config file is edited and nothing is fetched at launch.
Any MCP client works, over stdio or HTTP. One-pagers for Cursor, Zed and Cline.
The same package carries a plonk command, for the things that are neither an
agent nor a settings window:
plonk state # screens, zone sets, workspaces, windows
plonk launch review # a saved workspace
plonk awake while npm run build # awake for exactly as long as the build
plonk text | pbcopy # OCR a region into the clipboard
plonk measure 0.5 0.5 # size of what is mid-screen, in points and pixelsFor agents has every tool, the multi-agent rules, the HTTP transport and the rest of the CLI.
Privacy
No account, no cloud, no telemetry. The API binds to 127.0.0.1, refuses
anything carrying headers a browser cannot suppress, and is gated on a token
only you can read. The one outbound connection is the update check, which
carries no identifier and can be switched off.
None of that is a claim you have to take on trust. Releases are built and signed on GitHub's runners and ship with an attestation, so the binary on your Mac ties back to the commit it came from:
gh attestation verify Plonk-<version>.zip -R ostapondo/plonkCheck it yourself is every claim above with the command that tests it. SECURITY.md says where each promise stops.
Under the hood
The app is the single source of truth. The MCP server is a stateless bridge.
App/is the Swift menu bar app,mcp/the TypeScript MCP server.Config is plain JSON at
~/Library/Application Support/Plonk/config.json.
Build
Seven commands, and they are what CI runs on every pull request. Each line is a subshell, so paste the block from the repository root.
(cd App && swift build) # the app compiles
./scripts/test.sh # the unit suite
./scripts/lint.sh # style rules, no dependencies
(cd mcp && npm ci && npm test) # the MCP server
node scripts/check-zone-sets.mjs # the layouts in zone-sets/
node scripts/check-strings.mjs # every word the user reads
./scripts/check-security-claims.sh # what SECURITY.md promisesNone of that needs a signing certificate. Zone geometry, config decoding, HTTP routing, MCP tools, voice parsing, the CLI and every document here are reachable from that loop, and most changes need nothing more.
Producing a launchable Plonk.app does need one. Make your own once with
./scripts/make-signing-cert.sh, then run ./scripts/build.sh. macOS ties
Accessibility and Screen Recording to the code signature, and an ad-hoc one
changes every build, so a stable certificate is what stops rebuilds from
resetting permissions.
Contributing
Bug reports, zone sets, client one-pagers and code are all welcome. None of them need a signing certificate.
The smallest useful change is one JSON file.
zone-sets/is a gallery of layouts worth copying: an ultrawide split, a rotated monitor, the one built around a recurring meeting. Draw it in the app, read the numbers out ofplonk state --json, open a pull request. That folder has its own CI job and answers in about twenty seconds. No build, no signing, no Swift.good first issue issues are written to be picked up cold. Each says where the code is and how to tell it worked, and carries a prompt you can hand to an agent, since AGENTS.md already explains the repo to one.
needs-hardware is where a request for a desk nobody here has gets tagged, and answering one needs neither Swift nor a certificate. Desk tools meet hardware the author cannot see, so a report from three monitors or an ultrawide is worth more than a patch. An empty list is not a filled gap: open an issue with the arrangement you have and what happened.
CONTRIBUTING.md has the rest, including how long a review takes. Questions and half-formed ideas go to Discussions. A security problem goes through SECURITY.md, not a public issue. Everyone taking part follows the Code of Conduct.
License
MIT © ostapondo
Available Tools
22 toolsannotate_screenshotA
Draw on a screenshot you already took, then copy it to the clipboard and show it to the user. Call take_screenshot first and LOOK at the image: you cannot know where anything is until you have seen it. Points are fractions 0..1 of the image, origin TOP-LEFT, so a rectangle around a left sidebar that is a seventh of the width and starts under the title bar is [{x:0,y:0.05},{x:0.14,y:1}]. Rectangle and ellipse take two opposite corners, arrow takes start then tip, pen and highlight take a run of points. Returns the marked image so you can check what you drew.
Inputs: path (required), marks (required), output, clipboard.
apply_layoutA
Move and resize windows to build a layout. Each item places one window: 'app' is the app name (fuzzy matched), optional 'title' filters windows of that app by title substring, optional 'screen' is the monitor index from get_state (each monitor can get its own layout — just send items with different 'screen' values; defaults to the screen the window is currently on), 'frame' is {x,y,w,h} as fractions 0..1 of that screen's visible area with origin at TOP-LEFT (left half = {x:0,y:0,w:0.5,h:1}; bottom-right quarter = {x:0.5,y:0.5,w:0.5,h:0.5}; centered 60% = {x:0.2,y:0.15,w:0.6,h:0.7}). Windows are unminimized if needed. Returns per-item success/errors.
Inputs: items (required).
apply_saved_layoutA
Launch a saved workspace by name. Legacy name kept for older clients — new integrations should call launch_workspace, which adds a 'screen' option to pull the whole workspace onto one monitor. Opens every app that is not running, waits for its windows, and moves them into the saved positions; macOS cannot open an app straight into a position, so windows appear first and jump into place. Returns per-app success and reports apps that never opened a window. Takes up to a minute for a large workspace.
Inputs: name (required).
assign_zone_setA
Assign a zone set (built-in or saved) to one monitor, so dragging a window there snaps to that set's zones. Each monitor keeps its own assignment; assigning replaces whatever that monitor used before and takes effect on the next drag. Omit 'name' to restore the default set (Halves); pass 'edge' for plain edge snapping instead of zones. Available set names and current per-monitor assignments are in get_state.
Inputs: screen (required), name.
check_for_updateA
Ask Plonk whether a newer release exists, and report what is installed. Use this when the user asks what version they run, whether Plonk is up to date, or before calling install_update — which refuses unless a newer release is already on offer. The check is a network round trip to the GitHub releases API, so this returns immediately with the state as it stands and the result lands a moment later: read it back from get_state's 'update' key, or wait for an 'update' event on the change stream. If the user has turned update checks off, this fails with 409 rather than dialling out on their behalf — Plonk promises a process that only listens, and the user can still check by hand on its Updates page; report that back instead of retrying. Returns {installed, latest?, available, phase, status, automatic, notes?, page?}: 'available' is true only when 'latest' is newer than 'installed', 'phase' is idle|checking|available|downloading|verifying|installing|failed, and 'status' is a sentence fit to show the user.
Inputs: no named parameters.
clear_app_ruleA
Remove the rule set for an app with set_app_rule, so its new windows open wherever the app puts them again, or where its last window went when that habit is switched on in Plonk. 'app' is the pattern exactly as get_state.app_rules lists it, ignoring case. Fails when no rule matches; nothing else changes.
Inputs: app (required).
delete_layoutA
Delete the saved workspace with that name, whether it was saved with save_layout or save_workspace. Legacy name kept for older clients — new integrations should call delete_workspace, which does the same. Use it to clean up saved workspaces that are no longer wanted; existing names are listed in get_state.
Inputs: name (required).
delete_workspaceA
Delete a saved workspace by name. Use this to clean up workspaces you created that are no longer wanted.
Inputs: name (required).
delete_zone_setA
Delete a saved zone set by name. Any monitor currently using it falls back to the default set (Halves), so snapping keeps working. Only sets made with save_zone_set can go: the built-ins (Halves, Thirds, 60 / 40, Quarters, Priority) are refused. Deleting is immediate and cannot be undone — the zones would have to be described again. Saved sets and their per-monitor assignments are listed in get_state; use assign_zone_set instead when a monitor should merely stop using a set that others still need.
Inputs: name (required).
extract_textA
Read the words off the screen, or off a saved image, and return them as text. Recognition runs on the Mac itself and nothing is uploaded. Prefer this over take_screenshot whenever the answer is words rather than a picture — an error dialog, a log, a terminal, a table, text baked into an image or a paused video, a PDF page in a viewer that will not let text be selected. It costs a fraction of the tokens an image does and does not depend on reading pixels correctly. Use take_screenshot instead when layout, colour or 'what does this look like' is the question. mode 'screen' captures everything with no user interaction; 'region' and 'window' hand the user the native crosshair or window picker and wait for them, up to five minutes. Pass 'path' instead of a mode to read an image already on disk, including one take_screenshot just wrote. Returns 'text' (every line in reading order, top to bottom) and 'lines' — each with the recognized string, Vision's 0..1 'confidence', and 'box' {x,y,w,h} as fractions 0..1 of the image with origin at TOP-LEFT. Those boxes share the coordinate space annotate_screenshot draws in, so a line can be circled where it was found by passing the same path to that tool. The text is also copied to the clipboard unless 'clipboard' is false. An area with no readable text returns ok with an empty 'text' and a 'note' rather than an error.
Inputs: mode, path, clipboard, languages.
get_stateA
Get the current desktop state: all screens/monitors (index, frame, visible area — coordinates have origin at top-left of the primary screen, y grows down), all open windows (app name, title, which screen it is on, absolute frame, and 'fraction' — its position as fractions 0..1 of that screen's visible area), 'zone_sets' (every set by name, each zone as {x,y,w,h} with a 'name' where the set gives it one), 'screen_zone_sets' (which set each monitor wears), saved layout names, 'app_rules' (where each app's new windows open, see set_app_rule), 'place_new_windows' and 'auto_fill_zones', whether a keep-awake session is holding, and 'disabled_features': the modules the user switched off in Plonk (zones, workspaces, shot, ruler, awake and so on). A tool belonging to one of those fails with an error saying so until the user switches it back on. ALWAYS call this first before applying a layout, to see which apps are running and how many monitors there are.
Inputs: no named parameters.
install_updateA
Install the release that check_for_update found: Plonk downloads the build, checks it is signed with the same certificate as the running copy, swaps the bundle in, and relaunches itself. Prefer this over telling the user to download a build by hand — the signature check is also what preserves their Accessibility and Screen Recording grants, which a hand-installed copy can lose. Ask the user before calling it: it quits the app, so any window arrangement in flight stops and the local API is unreachable for a few seconds until the new copy is up. It fails without touching the installed copy when no newer release is on offer (call check_for_update first), when the user has update checks switched off (409 — installing downloads a build, so it is bound by the same promise as the check; they can install from Plonk's Updates page), when the download does not match the release or its signature, or when Plonk.app sits somewhere the user cannot write. Returns the same shape as check_for_update plus {installing: true} once the swap has started; poll get_state afterwards to confirm the new version came up.
Inputs: no named parameters.
launch_workspaceA
Launch a saved workspace: opens every app that is not running, waits for its windows, and moves them into the saved positions. Each window returns to the monitor it was captured on, so a workspace spanning several displays comes back spanning them. macOS cannot open an app straight into a position, so windows appear first and jump into place. Returns per-app success, and reports apps that never opened a window. Takes up to a minute for a large workspace.
Inputs: name (required), screen.
measure_screenA
Measure the screen in points and pixels, without taking a picture of it. Plonk photographs the screen once and walks out from the given point in all four directions until one pixel is unlike the one before it, which is where an edge is. What comes back is how far the point could travel each way: the run across and the run down. Prefer this over take_screenshot whenever the answer is a number: how wide that sidebar is, how tall that row is, how big the gap between two things is, is that tap target 44 points. An image costs far more tokens and still has to be eyeballed. Use extract_text when the answer is words, and take_screenshot when it is 'what does this look like'. Read the result honestly: these are two independent runs through one point, not the outline of an element. Inside a plain rectangle they are its width and height; inside a gap they are the gap; on a large flat background they run until something else is in the way, which may be most of the screen. When the exact bounds of a specific element matter, pass 'interactive' and let the user point at it. Three ways to ask. Pass 'point' for the runs through one place. Pass 'from' and 'to' for the straight-line distance between two places, which needs no capture at all. Pass 'interactive' to hand the user the ruler and wait up to five minutes while they measure it themselves. Points are fractions 0..1 of the screen's visible area with origin at TOP-LEFT, the same space apply_layout and save_zone_set use, so {x:0.5,y:0.5} is the middle of the screen. Returns 'points' {x,y,w,h} in screen points (absolute, origin top-left of the primary display) where w is the run across and h the run down, 'pixels' {w,h} in the display's own pixels — twice the points on a Retina screen, which is the difference that matters when checking an asset — 'fraction' {x,y,w,h} of that screen's visible area ready to hand to apply_layout, 'scale', and 'text', the same line Plonk shows the user. A distance also carries 'distance' and 'distance_pixels'. Needs macOS Screen Recording permission, the same as a screenshot; without it the call fails rather than guessing. What is measured is a still taken when the call started, so a screen that is animating measures as it was at that moment.
Inputs: to, from, point, screen, +2 more.
save_layoutA
Save the named window arrangement as a workspace. Legacy name kept for older clients — new integrations should call save_workspace, which can also record whether running apps get moved into place. Omit 'items' to snapshot the windows exactly as they are on screen right now; pass 'items' to describe the arrangement explicitly. Saving over an existing name replaces it. Saved workspaces are listed in get_state, with their full contents.
Inputs: name (required), items.
save_workspaceA
Save a workspace: the apps of a desktop setup, where each window goes, and what each app should open. Pass 'items' to describe the arrangement, or omit them to snapshot the windows exactly as they are on screen right now. Saving over an existing name replaces it. Saved workspaces are listed in get_state, with their full contents.
Inputs: name (required), items, move_existing.
save_zone_setA
Create or replace a named zone set used for drag snapping. Zones are rectangles {x,y,w,h} as fractions 0..1 of a screen's visible area, origin TOP-LEFT; each zone must stay inside the screen, but zones may overlap each other (the smallest one under the cursor wins). A zone may also carry a 'name' ('chat', 'editor'): it is drawn under the zone's number, the user can say it out loud, and snap_window takes it instead of the number; names must be unique within the set, ignoring case, and cannot be a bare number. Pass 'screen' to also assign the set to that monitor so it becomes active immediately. Pass 'gap' to give this set its own spacing around windows in points, or null to make it follow the default gap again; omitting it keeps whatever the set had. Built-in sets already exist: Halves, Thirds, 60 / 40, Quarters, Priority.
Inputs: name (required), zones (required), gap, screen.
select_agentA
Make an agent the user's active one in Plonk. Omit 'name' to select this client itself; pass "" to clear the choice so any agent may drive. The active agent shows in Plonk's menu bar and settings, and is where voice and other outgoing requests will go. With 'exclusive' true the app also rejects window and settings changes from every other agent (they can still read state and take screenshots). Connected agents are listed in get_state under 'agents'.
Inputs: name, exclusive.
set_app_ruleA
Make an app's windows open into a numbered zone from now on, so an arrangement holds without anyone dragging: 'Slack always in zone 1 on the second monitor'. 'app' is matched anywhere in the app's name or bundle id, case-insensitively, the way get_state.excluded_apps entries are; a bundle id such as 'com.tinyspeck.slackmacgap' is the safest form. 'zone' is the number Plonk draws on the zone (1-based) in the set assigned to that monitor. 'screen' is a monitor index from get_state and is stored as that display's identity, so it survives a reboot renumbering the screens; omit it and the window stays on whichever screen it opened on. One rule per app: setting it again replaces the old one; a rule that names the app exactly wins over a bare-word rule. Applies to ordinary windows that open after it is set, not to windows already open (use snap_window for those) and not to dialogs or panels. An app on get_state.excluded_apps is left alone even with a rule. A rule beats the habit Plonk keeps of where an app's last window went, and both beat filling an empty zone. Current rules are get_state.app_rules. Fails when 'screen' names a monitor that is not attached or a zone that monitor's set does not have.
Inputs: app (required), zone (required), screen.
set_awakeA
Keep the Mac from sleeping, and optionally keep the user shown as available in chat apps. One session with two levels, called Pulse in the app. 'available' picks the level. Left off, Plonk holds a power assertion: the Mac does not sleep, but Slack and Teams still slide the user to Away, because those read the system idle timer and an assertion does not touch it. Turned on, Plonk also resets that idle timer with a Shift keypress every two minutes, which is what keeps a chat status green — and which postpones sleep by itself, so being available always includes being awake. Use it whenever the point is how the user looks to other people rather than whether a job finishes. Three ways to end the session, in order of preference: 'pid' ends it the moment that process exits — best by far when something is running, because a build or a render knows when it is finished and nothing is left holding the machine awake afterwards; 'until' ends it at a wall-clock time ('17:00', or an ISO-8601 timestamp); 'minutes' ends it after a countdown. Give none of them and it runs until switched off, or until the user's configured default timeout expires. Switching it by hand overrides the schedule until the schedule itself next changes, so turning it off during scheduled hours lasts until those hours end rather than being undone on the next tick. Sessions Plonk starts on its own — a recurring schedule of hours and weekdays, a list of apps whose being open arms it, or the charger being plugged in — are settings on the Pulse page rather than parameters here; get_state reports all of them under 'awake_details'. Behavior also follows those settings: a session may pause on battery, so the returned 'status' is what actually happened, 'awake' is whether an assertion is held right now, and 'available' is whether a keypress is actually being posted. Those differ from what was asked for when Plonk has no Accessibility permission (nothing can be posted, though the Mac still stays awake) or when the user disallowed running on battery and the Mac is unplugged; neither is an error, since the request was understood. The menu bar cube glows while a session holds. A process-bound session is deliberately not restored if Plonk restarts, since the pid would mean nothing by then. Errors come back for a pid that is not running or a time that has already passed.
Inputs: on (required), pid, until, minutes, +1 more.
snap_windowA
Drop one window into a zone of the snap-zone set assigned to that monitor, by number or by name. The numbers are the ones Plonk draws on the zones while a window is dragged, so 'the middle zone' of a three-zone set is 2; a name is whatever the set calls a zone ('chat'), listed per zone in get_state.zone_sets, matched ignoring case. Fails with the zones that screen does have, names included, when neither matches. Zone sets and their per-monitor assignment are in get_state; use apply_layout instead when the user describes a size rather than a zone.
Inputs: app (required), zone (required), title, screen.
take_screenshotA
Capture the screen and return the image so it can be looked at. mode 'screen' captures everything (no user interaction); mode 'app' captures one named window and needs no user interaction either — pass 'app' and/or 'title_contains', and it works even when that window is behind others, minimized excepted, without raising it or taking focus; 'region' and 'window' hand the user the native crosshair/window picker and wait for them. Prefer 'app' whenever the user asks about a particular program ("what is playing in Spotify", "read the error in Xcode") — it is the only mode that can see a window the user cannot, and it does not disturb their desktop. Set annotate=true to open Plonk's drawing editor on the capture instead of returning it — use that when the user wants to mark the shot up themselves. Optional 'path' writes to an explicit file, otherwise the configured screenshot folder is used; 'clipboard' overrides the configured copy-to-clipboard behavior. The returned image is scaled down for legibility; the file at 'path' keeps full resolution. To draw on the result, pass that 'path' to annotate_screenshot.
Inputs: app, mode, path, annotate, +3 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
Most tools map cleanly to distinct actions (window layout vs zones vs screenshots vs awake vs updates), and descriptions carefully steer between look-alikes like extract_text, take_screenshot, and measure_screen. However, the three legacy aliases (save_layout/apply_saved_layout/delete_layout) are genuine duplicates of workspace tools, so an agent could pick the wrong one despite the 'legacy' warnings.
Every tool follows the same snake_case verb_noun convention (save_workspace, delete_zone_set, check_for_update), with the object as a noun and a clear action verb. Even legacy aliases comply with the pattern. No camelCase or inconsistent styles are present.
22 tools is on the heavy side, and three are pure legacy duplicates that add surface area without new capability. The breadth is defensible for a desktop-management utility covering windows, zones, screenshots, OCR, measure, keep-awake, updates, and agents, but it exceeds the ideal 3-15 range.
The tool surface covers the main lifecycles well: workspaces can be saved/launched/deleted, zone sets created/assigned/deleted, app rules set/cleared, and screenshots can be captured, annotated, OCR'd, and measured. Minor gaps remain—there is no explicit programmatic way to stop a keep-awake session, and update/agent tools are thin—but nothing likely to strand an agent.
Maintenance
Related MCP Connectors
Your AI apps as one team: hand work to Codex or Claude Code on your Mac, with a shared logbook.
Mac & Windows: let ChatGPT, Claude & Cursor use your email, calendar, iMessage, Teams, files.
Give your AI hands on every computer you own. Remote control of your Windows, Mac and Linux machines from one ChatGPT, Claude or Cursor chat — even from your phone. Ask it to find out why the office server is crawling, update every PC in the house, sort out your mom's laptop from another city, or ship the site from your work machine while you're on a train. It runs commands, reads files and takes a screenshot when it needs to see the screen. You decide what each computer allows, and you can cut any of them off in a second.
Use your Mac, Windows or Linux computer from ChatGPT, Claude or Codex: files, commands, documents.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to interact with macOS windows through yabai, providing window listing organized by Spaces and screenshot capture capabilities.2MIT
- AlicenseNot gradedqualityDmaintenanceProvides 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.4MIT
- AlicenseNot gradedqualityAmaintenanceA local autonomous AI agent that watches your screen, understands the visual layout, and executes native OS commands (clicking, typing) without cloud APIs.39MIT

ToolPiperofficial
AlicenseNot gradedqualityBmaintenance426+ MCP tools for macOS, all on-device — local AI inference (llama.cpp on Metal), voice, vision OCR, local RAG, browser automation, and ~140 system actions across 26 macOS domains. Nothing leaves your Mac.3MIT