Skip to main content
Glama
README.md
# 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

A3.6/5.0

Scored across 4 tools

Disambiguation5/5

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.

Naming Consistency3/5

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.

Tool Count4/5

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.

Completeness2/5

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.

Maintenance

ActivityMaintained
ResponsivenessResponsive