AE Bridge MCP
# AE Bridge MCP
A [Model Context Protocol](https://modelcontextprotocol.io) 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](https://github.com/NeuralIO444/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](https://github.com/NeuralIO444/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](https://github.com/NeuralIO444/IPC_Client)** — the
general-purpose version of the socket transport both of the above are
built on.
## What it does
Four moving pieces:
1. **`ae_bridge_mcp/jsx/ae_bridge_listener.jsx`** — loaded into a running
After Effects session. Opens a TCP socket on `127.0.0.1:45445` and
evaluates whatever ExtendScript it receives, using a simple
length-prefixed JSON protocol.
2. **[IPC_Client](https://github.com/NeuralIO444/IPC_Client)** (a real
dependency, not vendored code) — the Python-side client for that same
protocol (`execute_job()`).
3. **`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](https://github.com/NeuralIO444/AE_Eval) for that.
4. **`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 |
|---|---|
| `ae_eval_script` | Evaluate arbitrary ExtendScript, return the result or error. |
| `ae_get_active_comp` | Name, id, width, height, fps, duration, layer count for the active comp. |
| `ae_get_selected_layers` | Index, name, label, comment, enabled, 3D, hasVideo for every selected layer. |
| `ae_get_layer_properties` | Transform (position/scale/rotation/opacity/anchor), `sourceRect`, and applied effects for a given 1-based layer index. |
## 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
```bash
pip install -e .
# or, for running tests too:
pip install -e ".[dev]"
```
One runtime dependency: [IPC_Client](https://github.com/NeuralIO444/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):
```json
{
"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](https://github.com/NeuralIO444/AE_Eval) instead (or
alongside — they share the same AE-side listener).
## Transport fallback
`evaluate()` tries, in order:
1. **TCP socket** (`127.0.0.1:45445`) — needs `ae_bridge_listener.jsx`
loaded 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 requested `timeout` — a legitimately slow script (many
layers/effects, a render trigger) isn't cut short at 2s.
2. **AppleScript** (`osascript`, macOS only) — works even without the
listener loaded, at the cost of a slower round-trip (writes a temp
`.jsx` file, has AE run it via `DoScriptFile`, 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
```bash
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_properties` reads `position`/`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 via
`ae_eval_script` instead (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.jsx` must be
loaded for anything to work at all.
- ExtendScript's `return` is illegal outside a function body — if you're
calling `ae_eval_script` with a multi-statement script that needs to
return a value, wrap it in an IIFE yourself: `(function(){ ...; return
x; })()`. `eval.py`'s own `wrap_iife()` helper does this for the
built-in structured tools.
## Changelog
- Hardened `handle_call_tool` so a malformed tool argument (e.g. a
non-numeric `layer_index`) returns an `isError` result 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 requested `timeout` — only the connect
phase is capped now (`connect_timeout`), the response wait honors the
real request.
- Switched to depending on the [IPC_Client](https://github.com/NeuralIO444/IPC_Client)
package instead of a vendored copy (also picks up its connect-timeout
and message-size-cap fixes).
- Fixed packaging: `ae_bridge_listener.jsx` moved inside the package
directory and is now actually included in a built wheel/sdist — a real
`pip install` previously 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-eval` console-script entry point (it
collided with the standalone `AE_Eval` package's own `ae-eval` command
if both were installed together) and the CLI code it pointed to, which
fully duplicated `AE_Eval`.
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: evaluation of scripts, retrieval of active comp metadata, retrieval of selected layers, and retrieval of specific layer properties. There is no semantic overlap between these operations, making selection unambiguous.
All tools use a consistent 'ae_' prefix followed by a verb-object pattern (e.g., 'eval_script', 'get_active_comp'), which is good. However, 'ae_get_selected_layers' and 'ae_get_layer_properties' could be more consistent in verb ordering, though the naming is still readable and predictable.
With 4 tools, the server is on the minimal side but still reasonable for a specialized After Effects bridge. It covers the essential read and evaluation operations without bloat, though a few more tools could enrich the surface.
The server provides read-only access (evaluation, comp metadata, layer selection, layer properties) but lacks any modification or creation tools (e.g., add layer, set property, render comp). This leaves a significant gap for an automation server, as agents cannot perform common After Effects scripting tasks beyond evaluation and inspection.