Skip to content

For developers

Ron Yitzhaki edited this page Oct 21, 2026 · 4 revisions

For developers

How Surehand MCP is built, tested and released. For using it, start at Home.

How it fits together

AI app ──MCP (stdio or HTTP)──> server (Node.js, server/dist/surehand-ae.mjs)
                                   │  HTTP on 127.0.0.1, token
                                   ▼
                         bridge (CEP extension inside After Effects, com.aemcp.bridge)
                                   │  CEP evalScript
                                   ▼
                              ExtendScript (aemcp.jsxinc helpers)
  • The server is TypeScript bundled into one file. It defines the tools, transpiles modern JavaScript to ES3 with Babel, computes change reports, logs activity, runs Preflight's ffmpeg checks and aerender, and has the command-line commands (install, selftest, update…).
  • The bridge is an invisible CEP extension that starts with After Effects (StartOn: ApplicationInitialized). It runs a small HTTP server on 127.0.0.1 (port 8765, or the next free one of 10), runs one script at a time, holds approvals and the pause, and registers itself in ~/.surehand/ae/instances/.
  • The panel (com.aemcp.panel) and the approval pop-up (com.aemcp.approval) are CEP extensions in the same bundle. They talk to the bridge over the same HTTP API, and get the panel secret through CEP events (com.surehand.ae.hello → com.surehand.ae.welcome).
  • The extension is embedded in the server bundle at build time; the server installs or updates it when it starts.

Repository layout

server/                     MCP server (TypeScript) -> bundled into server/dist/surehand-ae.mjs (committed)
  src/aemcp.jsxinc          ExtendScript helper library, loaded into After Effects on demand
  src/tools/                tool definitions (each is a zod input schema + an ExtendScript function body)
  src/preflight/            Preflight: spec schema, project checks, ffmpeg checks, report
  src/cli/                  install, uninstall, selftest, chatgpt, setup, update, clients, checkpoints…
  src/paths.ts              every name and folder (surehand-ae, ~/.surehand/ae…)
  src/migrate.ts            the move from 1.3's ~/.aemcp
  src/permissions.ts        modes, shell check, approval texts
  test/                     unit tests (vitest)
  scripts/smoke.mjs         end-to-end test against a running After Effects
  scripts/package.mjs       builds the release folder, zip and .mcpb
  scripts/release.mjs       cuts a release (see below)
  scripts/test-mac.mjs      installs this build on this Mac and runs the full self-test on each open After Effects
  scripts/test-windows.mjs  the same on a Windows PC over SSH
  scripts/sign-extension.mjs   signs the After Effects extension
  scripts/package-mac-pkg.mjs  the signed, notarized Mac installer (.pkg)
  scripts/package-win-installer.mjs  the Windows installer (.exe, NSIS, built on the Mac)
  scripts/sign-win.mjs      signs Windows files (Certum certificate via SimplySign)
  scripts/ci-check.mjs      assertions for the CI install / uninstall runs
extension/                  After Effects extension (signed): bridge, panel, approval pop-up
  js/bridge-core.js         the bridge: HTTP API, queue, approvals, pause
  js/panel.js, panel-model.js   the panel (panel-model.js is pure and unit-tested)
  js/approval.js            the pop-up
  js/control.js             the panel secret handshake
  js/paths.js               the same folder rules as server/src/paths.ts
installer/                  run.sh / run.ps1 (find or download Node.js, run a command), START HERE.txt
  mac/, windows/            double-click launchers for a source checkout
  mac/pkg/                  .pkg resources and postinstall
  windows/nsis/             the NSIS script
brand/                      the logo: SVG masters, PNGs, Surehand.icns, Surehand.ico (see brand/README.md)
.github/workflows/ci.yml    CI

Build and check

cd server
npm install
npm run check      # what CI runs: type-check, all unit tests, build. Run before every push.
npm test           # unit tests only
npm run build      # -> dist/surehand-ae.mjs

server/dist/surehand-ae.mjs is committed. CI rebuilds it and fails if the committed file differs, so commit the rebuilt bundle with your change.

After changing the server or the panel: rebuild, re-run the installer (or node dist/surehand-ae.mjs install), restart After Effects. After Effects only loads extension changes when it starts.

Testing against After Effects

node scripts/smoke.mjs   # calls all major tools; needs After Effects running with the bridge
npm run test:mac         # installs this build here (AI app settings untouched) and runs the full self-test
                         # on each open After Effects; one run per Mac at a time (~/.surehand/test-mac.lock)
npm run test:windows     # the same on a Windows PC over SSH (host in the script, or AEMCP_WIN_HOST)

--allow-no-ae lets either test pass when no After Effects is open. An After Effects that was open before the install keeps running the previous extension; the test says so.

Self-test

surehand-ae selftest checks the installation and the connection. selftest --full runs every major tool (over 60 steps) against temporary comps, which are removed afterwards. If the open project has no unsaved changes, the Save As and checkpoint-restore checks briefly switch to a scratch project and reopen yours. It lists which scripting features your After Effects version has and saves a report to ~/.surehand/ae/compat/.

All steps pass on After Effects 2025 (25.6) and 2026 (26.5) on macOS, and on 2026 on Windows 11, also with 2025 and 2026 running side by side. Reports from other versions are welcome.

Adding a tool

Add an entry to one of the arrays in server/src/tools/*.ts: a zod input schema and an ExtendScript function body that receives args and can use the aemcp helpers. Mark tools that only read as readOnly (they skip approvals, the undo group and the change report, and work in Read-only mode). Add it to a category in server/src/tools/profile.ts so ae_tools lists it.

Signing the extension

npm run sign   # after changing anything in extension/ or the version

The version is stamped into the extension's files, so the signature must be redone after a version bump; npm test fails while it is out of date. Signing needs Adobe's ZXPSignCmd and the certificate outside the repo (see the header of scripts/sign-extension.mjs; override with AEMCP_SIGN_DIR / AEMCP_ZXPSIGNCMD). CI only verifies the digests.

Packaging

npm run package                 # -> ../dist-zip/Surehand-MCP/ (launchers, server, Surehand-MCP.mcpb) + zip
npm run package:mac-pkg         # -> ../dist-zip/Surehand-MCP-<version>.pkg, signed and notarized (--no-notarize: faster)
npm run package:win-installer   # -> ../dist-zip/Surehand-MCP-Setup-<version>.exe (--sign: Authenticode, needs SimplySign)
  • The .pkg bundles Node.js 24 LTS (universal arm64 + x86_64, re-signed with the Developer ID) and needs the Developer ID Application and Installer identities plus the notarization keychain profile aemcp-notary.
  • The Setup .exe bundles the official win-x64 node.exe, checked against a pinned SHA-256 and nodejs.org's SHASUMS256.txt, and is built with NSIS on the Mac.

Releasing

npm run release -- 1.4.0 --notes notes.md

scripts/release.mjs does everything in order and stops at the first failure:

  1. version bump;
  2. sign the After Effects extension;
  3. check (types, tests, build);
  4. Mac and Windows live tests (--skip-mac, --skip-windows to skip);
  5. package, then the signed installers (--skip-installers to skip);
  6. a release/X.Y.Z branch and its pull request;
  7. wait for the PR's CI;
  8. squash-merge, and check the merged tree equals the tested commit;
  9. the GitHub release on the merged commit, with the zip, the .mcpb, the installers and the server file (surehand-ae.mjs, also as ae-mcp.mjs for 1.3's updater, each with a .sha256).

main only accepts changes through a pull request with green CI, which is why the release goes through its own PR. Nothing is pushed until everything local has passed. If the PR's CI fails, main is untouched and nothing is published. Run it from a clean main that matches origin/main.

A pre-release version (1.4.0-beta.1) becomes a GitHub pre-release, which surehand-ae update offers only on the beta channel (--channel beta, or Beta updates in the panel). --draft creates a draft release.

CI

.github/workflows/ci.yml, on every push to main and every pull request:

Job What it does
test (Ubuntu) npm ci, type-check, unit tests, rebuild and fail if server/dist is stale, shellcheck run.sh, parse run.ps1
install (macOS and Windows) Builds the release folder, then on a fake, empty user profile runs the real launchers: 1. Install (with a forced Node.js download), 2. Test Installation, 4. Uninstall, and checks the result with ci-check.mjs

There is no After Effects on CI; the live tests are test:mac and test:windows.

Command line

The installed server is also the command-line tool. Run it with the Node.js the installer used:

node ~/.surehand/ae/server/surehand-ae.mjs <command>
Command What it does
(none) The MCP server over stdio. --tools core|full picks the tool profile.
--http [--port 8766] [--host 127.0.0.1] [--no-auth] The MCP server over Streamable HTTP at /mcp (token required).
install [--skip-clients] [--no-<app>] Install the server and extension; connect AI apps (--no-claude-desktop, --no-vscode…).
uninstall [--keep-config] [--all] Uninstall; keeps checkpoints and files that aren't Surehand's unless --all.
selftest [--full] Check the installation (and with --full, every major tool).
test --json The quick self-test as JSON (the panel's Connection test).
diagnostics A report for a bug report, with no secrets.
activity [n] The last n tool calls (default 30).
chatgpt [--local] HTTP server and public tunnel for ChatGPT on the web, in the terminal; Ctrl+C stops it.
chatgpt --background|--status|--stop [--json] The same in the background (the panel's ChatGPT screen).
update [--json], update --check [--json] Install the latest release; or only report. --channel beta|stable also sets the update channel.
clients [--json] The AI apps on this computer and whether they are connected.
connect <app> [--json], disconnect <app> [--json] claude-desktop, claude-code, codex, cursor, vscode, windsurf, lm-studio.
skills [--json], skills install|uninstall [--json] Where the Surehand skills are installed; write or remove them.
farm check <manifest.json> [--map <from>=<to>] [--ae <folder>] [--json] On a render node, without After Effects: does it have what the render needs? Exit code 0 ready, 1 missing or different, 3 not everything checked.
chat --agents --json, chat --agent <id> The panel's Chat in After Effects (see docs/chat-in-ae.md).
checkpoints [--json] [--project <path>] A project's checkpoints (default: the open one).
checkpoint-diff <id> [--json] [--project <path>] What changed since a checkpoint (needs After Effects).
checkpoint-save [--label <text>] [--project <path>] [--json] Save the open project and checkpoint it.
setup [--force] Only install or update the After Effects extension and config.
--version, --help

ae-mcp is an alias of surehand-ae. With --json, the panel's commands print one JSON object and never ask anything; an unknown command exits with an error instead of starting the server.

Environment variables are listed in Settings and files. For testing: SUREHAND_AE_FORCE_NODE_DOWNLOAD=1 makes the launchers ignore a system Node.js.

Names

The family is Surehand, so this app's names are surehand-ae: the command, the server file, the MCP server name and the key in AI app settings, and the folders ~/.surehand/ae (this app) and ~/.surehand (shared). All are in server/src/paths.ts. Unchanged so that scripts and the extension keep working: the tool names (ae_*), the extension id com.aemcp (and its bundle ids), the aemcp helper object and the aemcp:// resource addresses. The npm package name is surehand-mcp. Environment variables are SUREHAND_AE_*; the older AEMCP_* names still work (for example AEMCP_TOOLS), and the command ae-mcp stays as an alias.

Moving from 1.3

1.3 kept everything in ~/.aemcp, ran as after-effects in AI app settings, and its panel was named differently. The first time 1.4 runs (installer, update, or simply starting the server), server/src/migrate.ts moves it:

1.3 1.4
Folder ~/.aemcp ~/.surehand/ae (this app), ~/.surehand (shared)
Server file ~/.aemcp/server/ae-mcp.mjs ~/.surehand/ae/server/surehand-ae.mjs
Private Node.js / ffmpeg ~/.aemcp/node, ~/.aemcp/ffmpeg ~/.surehand/node, ~/.surehand/ffmpeg
Name in AI app settings after-effects surehand-ae
Command ae-mcp surehand-ae (ae-mcp still works)
Environment variables AEMCP_* SUREHAND_AE_* (AEMCP_* still work)
Claude Desktop extension after-effects surehand-ae
  • Settings, checkpoints, project history and other files go to ~/.surehand/ae; Node.js and ffmpeg to ~/.surehand. Files are renamed, not copied. Where a file of the same name already exists, the old one arrives as <name>.from-aemcp.
  • AI apps that ran the old server are pointed at the new one: after-effects becomes surehand-ae. An after-effects entry that runs some other server is left alone. A 1.3 backup <file>.aemcp.bak counts as the first original.
  • If anything can't be moved, the move is undone and retried later (~/.surehand/migration.log); Copy diagnostics shows "(1.3 location, not migrated yet)" meanwhile.
  • Until After Effects restarts, the 1.3 extension still reads ~/.aemcp, which keeps a copy of config.json, an empty instances folder, a small server/ae-mcp.mjs that starts the new server, a node link to the moved Node.js and MOVED.txt. The 1.4 server accepts the old extension's token meanwhile, and settings the 1.3 panel saves there are picked up later. Uninstalling 1.4 removes these compatibility files; files of yours that stayed in ~/.aemcp are kept.
  • The 1.4 release also carries the server as ae-mcp.mjs, the file 1.3's updater downloads.

Contributing

  • Open an issue first for anything larger than a fix.
  • Run npm run check before pushing, and commit the rebuilt server/dist.
  • If you change extension/, it must be re-signed (npm run sign); a maintainer does this if you can't.
  • Keep the user-facing language plain and precise; error messages say what happened and what to do.
  • Changes reach main through a pull request with green CI.

Licence: MIT. Created and maintained by Ron Yitzhaki.

Clone this wiki locally