{"slug": "show-hn-i-gave-claude-control-of-photoshop", "title": "Show HN: I gave Claude control of Photoshop", "summary": "Developer kathan3009 released photoshop-mcp, a free, MIT-licensed Model Context Protocol server that lets MCP clients including Claude Code, Claude Desktop, Cursor and Codex drive a Mac's Photoshop via AppleScript, with no Photoshop plugin required. The server wraps each edit in a single undo step that rolls back automatically on failure, requires `overwrite: true` to replace existing files, and returns sRGB renders plus histogram numbers before and after each change; it is macOS-only, requires Node.js 18+ and a licensed Photoshop, and was tested only against Photoshop 27.11.0. The existing 1.2.0 DMGs are unnotarized and can trigger macOS's \"damaged\" warning, so the author directs users to the source installation until a notarized build is advertised.", "body_md": "Let Claude (or Cursor, Codex, any MCP client) drive the **Photoshop running on your Mac**: open files, build layers, grade colour with adjustment layers, Select Subject, masks, text, smart objects, export. No Photoshop plugin to install.\n\n```\nClaude Code / Claude Desktop / Cursor / Codex ──MCP──▶ photoshop-mcp ──AppleScript──▶ Photoshop\nshell / scripts ──────────────────────────────psctl──▶ (same core)\n```\n\nEvery edit is **one undo step** that **rolls back automatically** if it fails. The agent gets a safety checkpoint before its first edit, requires `overwrite: true` to replace an existing file, and sees the picture (sRGB renders + histogram numbers) before and after each change.\n\n**macOS only.** You need an installed/licensed copy of Photoshop, Git, Node.js 18+ with npm (prefer a supported Node LTS), and an MCP client. Tested with **Photoshop 27.11.0**; other versions are unverified. The server is free and MIT-licensed; Photoshop and your model provider may charge separately.\n\n```\ngit clone https://github.com/kathan3009/photoshop-mcp.git\ncd photoshop-mcp\n./scripts/install.sh --client claude-code\n```\n\nUse `claude-desktop`, `cursor`, or `codex` instead of `claude-code` for your client. Omit `--client` to configure all detected clients. The installer installs locked dependencies, backs up existing client config files, adds the Photoshop MCP entry, and links the Claude skill and CLI tools. No sudo or desktop app is needed. Keep this clone in place: clients use its absolute path.\n\nRestart your client, open Photoshop, and ask it to **run `ps_status`**. Allow the macOS Automation request to control Photoshop. For diagnostics, run `node bin/photoshop-mcp.mjs doctor`. If `psctl` is not found, use `node cli.mjs` or add `~/.local/bin` to PATH.\n\nTry a disposable photo first: ask the agent to render it, add a named exposure adjustment layer, render again, then undo that step. The source server exposes the editing, history and checkpoint tools; the optional desktop app adds the menu-bar UI.\n\n[Installation, troubleshooting, privacy, and uninstall](https://github.com/kathan3009/photoshop-mcp/blob/main/docs/INSTALLATION.md) · [Report an issue](https://github.com/kathan3009/photoshop-mcp/issues)\n\nThe existing **1.2.0 DMGs are unnotarized** and can trigger macOS's “damaged” warning. Use the source installation above for now. The repo now separates notarized public builds from explicitly named `-unnotarized` local test builds; a new notarized download is not yet being advertised. See [release procedure](https://github.com/kathan3009/photoshop-mcp/blob/main/docs/RELEASING.md).\n\nThe app bundles its own runtime, shows activity/previews/checkpoints, and provides Pause, Undo and Revert controls. If using it, copy it to Applications and launch it there **before** connecting clients.\n\nAny other MCP client: `photoshop-mcp setup` prints the JSON snippet, which is just\n\n```\n{ \"mcpServers\": { \"photoshop\": { \"command\": \"/absolute/path/to/node\", \"args\": [\"/absolute/path/to/photoshop-mcp/server.mjs\"] } } }\n```\n\n| Area | Tools | \n|---|---|\n| **See** | `ps_status` (docs, history, EXIF) ·`ps_snapshot` (layer tree with stable ids, filterable) ·`ps_inspect` (exact adjustment settings, text styling, smart filters) ·`ps_render` (sRGB composite, crop at full res, or one layer solo → image) ·`ps_analyze` (histogram stats, clipping, colour cast, point samples) ·`ps_fonts` | \n| **Documents** | `ps_document` open / create / activate / duplicate / save (psd, tiff, png, jpg — exports converted to sRGB) / close / resizeImage / resizeCanvas / crop / trim / flatten | \n| **Layers** | `ps_create_layer` (pixel, group, solid & gradient fills, rectangle, ellipse) ·`ps_add_text` /`ps_edit_text` (point & paragraph text, fonts, tracking, leading — true pixels at any ppi) ·`ps_place_image` (smart object; contain / cover / stretch into a box) ·`ps_set_layers` (visibility, opacity, blend, move, resize, rotate, align; one layer or many) ·`ps_layer_op` (delete, duplicate, group, ungroup, merge, rasterize, smart object, clip, move) | \n| **Grade & retouch** | `ps_adjustment` — curves, levels, brightnessContrast, light, hueSaturation (with per-colour ranges), colorBalance, vibrance, photoFilter, exposure, blackAndWhite, gradientMap, invert, posterize, threshold (create, update, clip to a layer, mask from selection) ·`ps_filter` — gaussianBlur, unsharpMask, addNoise, highPass, motionBlur, median (smart filters by default) ·`ps_selection` (rect, ellipse,**AI Select Subject** with add/subtract/intersect, layer pixels, feather, expand, fill…) ·`ps_mask` (add / replace / delete / apply / invert / enable / disable) | \n| **Control** | `ps_history` (list, undo, redo, goto, snapshot, revert) ·`ps_batch` (many ops = one atomic undo step) ·`ps_actions` (play your recorded Photoshop Actions) ·`ps_exec` (ExtendScript escape hatch, opt-in) | \n\nThe server ships **instructions** that teach any connecting agent the house rules: look first (render + analyze), edit non-destructively, verify after each change, undo bad steps, never overwrite the user's file.\n\nNot covered: Camera Raw local masks / Texture / Clarity, generative fill, brushes, 3D. Raw develop settings can still be driven by writing an `.xmp` sidecar next to the raw before opening it.\n\n| Risk | Safeguard | \n|---|---|\n| Agent edits the wrong file when you switch documents | Each session is **pinned to one document** (first worked on, or last opened/created/activated). Your view is restored after every call.`docId` targets another one explicitly. | \n| Photoshop crashes mid-session and unsaved work is lost | **Checkpoint before the first edit** : a history snapshot (`ps_history revert` ) plus a layered copy of unsaved work in`~/Library/Application Support/photoshop-mcp/checkpoints` (newest 20 / 7 days kept). Re-taken before every`ps_exec` .`PS_MCP_CHECKPOINT=full\\|history\\|off` . | \n| Agent overwrites or discards your work | Saving in place needs `overwrite: true` ; closing a dirty document needs`discard: true` ; existing files are never replaced without`overwrite` — except files the same session exported itself. | \n| Arbitrary code | `ps_exec` is**off by default** (`PS_MCP_ALLOW_EXEC=1` , or`setup --exec` ). | \n| A dialog is open / Photoshop hangs | Per-tool timeouts; on timeout the bridge pings Photoshop and reports \"a dialog is probably open\" vs \"still working\". | \n| Opaque Photoshop errors | Locked/hidden layers, wrong layer type, wrong colour mode, targeted mask… are checked up front with actionable messages; raw \"not currently available\" errors get likely causes appended. | \n| Silent no-ops | Adjustment/filter params are strictly validated; tools return what Photoshop actually stored. | \n| Debugging | Every call is logged to `~/Library/Logs/photoshop-mcp/YYYY-MM-DD.jsonl` (14 days;`PS_MCP_LOG=off` ). | \n| Photoshop updates | `test/last-green.json` records the version the suite passed on;`ps_status` and`doctor` flag untested versions. Optional daily launchd watcher:`scripts/watcher.sh install` . | \n\n```\npsctl status\npsctl snapshot depth=2\npsctl render out=/tmp/look.jpg maxSize=1200\npsctl adjustment type=hueSaturation params='{\"reds\":{\"saturation\":25}}'\npsctl set_layers layer=12 opacity=60\npsctl batch @grade.json\npsctl run script.jsx docId=5        # ExtendScript file as one undo step\npsctl help ps_set_layers            # JSON schema for any tool\npsctl doctor\n```\n\nArgs are `key=value` (values parsed as JSON), one JSON object, `@file.json`, or `-` for stdin. The CLI is stateless: pass `docId` when more than one document is open.\n\nExecution is local, but requested previews and tool results are returned to your MCP client and may be sent to its model provider. Local execution is not a promise that images never leave your Mac. Logs and checkpoints can contain sensitive data. Overwrite/discard flags are programmatic guards, not independent human approval dialogs. See [details](https://github.com/kathan3009/photoshop-mcp/blob/main/docs/INSTALLATION.md#privacy-costs-and-permissions).\n\n```\nnpm run test:smoke   # MCP startup and tool discovery, without Photoshop\nnpm run test:release # release policy and isolated client-configuration regression checks\nnpm test          # end-to-end over real MCP against the running Photoshop\n```\n\nThe suite covers every tool, RGB/CMYK/Grayscale and 300 ppi documents, a 300-layer document, document pinning, checkpoints, the close guard, locked layers, busy detection, logging and exec gating. It works only in scratch `e2e-*` documents, verifies your own document is untouched, and **refuses to run while any document has unsaved changes** (a malformed Photoshop command can crash Photoshop and take every open document with it). On a full pass it writes `test/last-green.json`.\n\n`node test/agent-trial.mjs <dir with shikara.jpg & horses.jpg> [model]` runs real headless-Claude tasks and reports tool use, errors and cost.\n\n| Finding | Handling | \n|---|---|\n| New layers are created **inside** a selected group | lifted above the group unless `inside` is asked for (a note says so) | \n| Text `size` / paragraph box in \"px\" are read as points at document ppi (100 px → 417 px on a 300 ppi file) | scaled by 72/ppi; leading and position are true px | \n| Exports/renders carry the document profile (Display P3 from Camera Raw) | converted to sRGB unless `profile: \"document\"` | \n| ExtendScript mis-parses unparenthesized nested ternaries | none left in the codebase | \n| Deleting a mask drops the active selection; cover-fit placement adds a mask | selection kept; `replace: true` ; notes | \n| Select Subject ignores the selection mode | previous selection kept in a channel and combined | \n| Brightness/Contrast `brightness` is silently ignored (PS 27 \"Light\" adjustment) | `brightnessMode` set; params strict-validated | \n| `BlendMode.COLOR` doesn't exist (`COLORBLEND` ) | friendly names mapped | \n| AM history index / align / per-layer Select Subject / hidden-layer fill / Background id change / mask channel left targeted | each worked around, see `jsx/` comments | \n| Colour samplers and selections dirty the document | analysis runs on a temporary duplicate | \n\nThe raw probe scripts are in `probes/` (`node probes/run.mjs --scratch probes/p6.jsx` runs one in a throwaway document; they refuse to run next to unsaved work).\n\n```\napp/                    Electron desktop app (bundles the server; `npm run dist` builds the DMGs)\nmarketing/brag-output/   the launch film (/brag workflow: plan, brief, HyperFrames composition with music + Kokoro voice, share copy)\nmarketing/launch-video/ the earlier 56-second silent cut (HyperFrames composition)\nbin/photoshop-mcp.mjs   setup | doctor | uninstall | serve\nserver.mjs              MCP server (stdio) + agent instructions + per-connection session\ncli.mjs                 psctl CLI\nsrc/tools.mjs           tool registry: zod schemas, strict per-type params, batch validation, sessions, checkpoints\nsrc/bridge.mjs          osascript bridge: document targeting, serialized queue, timeouts, busy detection, error hints, logging\nsrc/setup.mjs           client registration (Claude Code, Claude Desktop, Cursor, Codex), skill + bin links\nsrc/doctor.mjs          environment and registration checks\njsx/prelude.jsx         ExtendScript helpers: JSON, layer lookup, AM reads, transactions, descriptor dump\njsx/ops.jsx             every operation (mutations are one-step transactions; batch chains them)\nskill/SKILL.md          agent skill\ntest/e2e.mjs            end-to-end suite · test/agent-trial.mjs headless-Claude trial\nscripts/                install.sh, watcher.sh + version-watch.mjs (optional launchd)\nprobes/                 live probes that produced the findings above\n```\n\n- macOS only (AppleScript transport). A UXP plugin + local daemon would enable Windows.\n- One call at a time (Photoshop runs scripts serially); a modal dialog blocks calls until dismissed (they time out after `PS_MCP_TIMEOUT_S` , default 120s).\n- No generative fill, painting/brush strokes, or 3D.\n\nMIT © Kathan", "url": "https://wpnews.pro/news/show-hn-i-gave-claude-control-of-photoshop", "canonical_source": "https://github.com/kathan3009/photoshop-mcp", "published_at": "2026-10-23 21:30:07+00:00", "updated_at": "2026-10-23 21:42:58.786291+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "developer-tools", "generative-ai"], "entities": ["photoshop-mcp", "kathan3009", "Claude Code", "Claude Desktop", "Cursor", "Codex", "Photoshop 27.11.0", "AppleScript"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-i-gave-claude-control-of-photoshop", "markdown": "https://wpnews.pro/news/show-hn-i-gave-claude-control-of-photoshop.md", "text": "https://wpnews.pro/news/show-hn-i-gave-claude-control-of-photoshop.txt", "jsonld": "https://wpnews.pro/news/show-hn-i-gave-claude-control-of-photoshop.jsonld"}}