AE Bridge MCP
AE Bridge MCP
A Model Context Protocol server that lets an AI agent (Claude, or any MCP-compatible client) inspect and drive a live Adobe After Effects session — read the active composition, list selected layers, inspect a layer's transform/effects, or evaluate arbitrary ExtendScript.
Extracted from Dimension's
ae_bridge_mcp/ae_eval dev tooling so other NeuralIO 444 AE-adjacent
products can reuse it without depending on Dimension itself.
Part of a small set of related repos:
AE_Eval — the same evaluate-ExtendScript-in-AE capability as a plain terminal CLI, no MCP client needed. Independent of this repo — pick whichever fits, or use both (they share the same AE-side listener file).
IPC_Client — the general-purpose version of the socket transport both of the above are built on.
What it does
Four moving pieces:
ae_bridge_mcp/jsx/ae_bridge_listener.jsx— loaded into a running After Effects session. Opens a TCP socket on127.0.0.1:45445and evaluates whatever ExtendScript it receives, using a simple length-prefixed JSON protocol.IPC_Client (a real dependency, not vendored code) — the Python-side client for that same protocol (
execute_job()).ae_bridge_mcp/eval.py—evaluate()tries the socket transport first, falling back to macOS AppleScript (osascript) if the listener isn't loaded or AE isn't reachable. No CLI of its own — see AE_Eval for that.ae_bridge_mcp/server.py— the actual MCP server: a stdio JSON-RPC loop exposing 4 tools (see below) to any MCP client.
Tests in tests/.
Tools exposed over MCP
Tool | What it does |
| Evaluate arbitrary ExtendScript, return the result or error. |
| Name, id, width, height, fps, duration, layer count for the active comp. |
| Index, name, label, comment, enabled, 3D, hasVideo for every selected layer. |
| Transform (position/scale/rotation/opacity/anchor), |
Setup
1. Load the AE-side listener
In After Effects: File → Scripts → Run Script File... and pick
ae_bridge_mcp/jsx/ae_bridge_listener.jsx. It starts listening immediately
($.global.AEBridgeListener.start() runs at the bottom of the file) and
writes a confirmation line to the ExtendScript console.
To have it load automatically, drop it (or a script that $.evalFile()s
it) into AE's Scripts/Startup folder for your AE version, or wire it into
whatever your product's own panel/launcher already loads at boot.
To stop it: $.global.AEBridgeListener.stop(); from the ExtendScript
console, or just close AE.
2. Install the Python package
pip install -e .
# or, for running tests too:
pip install -e ".[dev]"One runtime dependency: IPC_Client
(itself pure stdlib, no further dependencies) — installed automatically
from its pinned commit via the git+https://... URL in pyproject.toml.
3. Point an MCP client at it
Example config (Claude Code, or any MCP client using the same
command/args shape):
{
"mcpServers": {
"ae-bridge": {
"command": "python3",
"args": ["-m", "ae_bridge_mcp"]
}
}
}Or run it directly to sanity-check it starts: python3 -m ae_bridge_mcp
(it will sit waiting for JSON-RPC on stdin — that's expected, it's not
meant to be run interactively).
This repo has no standalone CLI of its own by design — for a terminal command with the same evaluate/active-comp/selected-layers capability, install AE_Eval instead (or alongside — they share the same AE-side listener).
Transport fallback
evaluate() tries, in order:
TCP socket (
127.0.0.1:45445) — needsae_bridge_listener.jsxloaded in AE. Sub-5ms round-trip. The connect phase fails fast (connect_timeout, default 2s) so an unloaded listener falls through to AppleScript quickly; the response wait itself honors the caller's full requestedtimeout— a legitimately slow script (many layers/effects, a render trigger) isn't cut short at 2s.AppleScript (
osascript, macOS only) — works even without the listener loaded, at the cost of a slower round-trip (writes a temp.jsxfile, has AE run it viaDoScriptFile, reads a temp JSON output file back).
If neither works, evaluate() returns a structured error rather than
hanging — check that After Effects is running and the listener is loaded.
Security
ae_bridge_listener.jsx is an unauthenticated socket — any local
process that can reach 127.0.0.1:45445 and speaks the wire protocol can
execute arbitrary ExtendScript through it (including filesystem access
and system.callSystem()-style OS command execution) for as long as
After Effects is running with the listener loaded. This is inherent to
what the tool does — an AI agent is meant to evaluate arbitrary
ExtendScript through it — but it's worth knowing plainly before loading
it on a shared machine (a render farm node, CI runner, or multi-user
workstation), where any other local process/user could reach the same
port.
Testing
pip install -e ".[dev]"
pytest tests/Tests mock the AE-facing boundary (execute_job, eval_extendscript_socket,
eval_extendscript_applescript) — they don't require a real After Effects
instance to run.
Known limitations
ae_get_layer_propertiesreadsposition/scale/etc. via.value, which reads the property at the current playhead for keyframed properties, not a deterministic rest pose. If you need a deterministic read for an animated property, evaluate a custom script viaae_eval_scriptinstead (e.g.prop.valueAtTime(prop.keyTime(1), true)).The AppleScript fallback is macOS-only; on Windows/Linux, only the socket transport is available, so
ae_bridge_listener.jsxmust be loaded for anything to work at all.ExtendScript's
returnis illegal outside a function body — if you're callingae_eval_scriptwith a multi-statement script that needs to return a value, wrap it in an IIFE yourself:(function(){ ...; return x; })().eval.py's ownwrap_iife()helper does this for the built-in structured tools.
Changelog
Hardened
handle_call_toolso a malformed tool argument (e.g. a non-numericlayer_index) returns anisErrorresult instead of raising and killing the persistent stdio server process.Fixed
evaluate()silently capping the entire socket-transport wait at 2 seconds regardless of the requestedtimeout— only the connect phase is capped now (connect_timeout), the response wait honors the real request.Switched to depending on the IPC_Client package instead of a vendored copy (also picks up its connect-timeout and message-size-cap fixes).
Fixed packaging:
ae_bridge_listener.jsxmoved inside the package directory and is now actually included in a built wheel/sdist — a realpip installpreviously shipped a package with no way to reach After Effects on Windows/Linux (only worked by accident via editable installs from a live git checkout).Removed the redundant
ae-evalconsole-script entry point (it collided with the standaloneAE_Evalpackage's ownae-evalcommand if both were installed together) and the CLI code it pointed to, which fully duplicatedAE_Eval.
License
MIT — see LICENSE.