Skip to main content
Glama
AE Test Bridge MCP

AE Test Bridge MCP

by NeuralIO444

AE Test Bridge MCP

A fake Adobe After Effects bridge listener — same wire protocol as the real thing — for testing bridge client code without needing real After Effects running. Also exposed as an MCP server, so an AI agent can spin one up, script its responses, and inspect what a client under test sent it, entirely conversationally.

Part of a small set of related repos:

  • AE_Bridge_MCP — the real MCP server for live After Effects introspection/eval.

  • AE_Eval — the same capability as a plain terminal CLI.

  • IPC_Client — the general-purpose transport client this repo's MockAEBridge speaks the same protocol as (this repo re-implements the send/recv framing inline rather than depending on that package, to stay a standalone, zero-dependency install).

This repo exists because none of the three above had anything to test against other than mocking the transport itself — a real fake server closes that gap and lets a client under test exercise its actual socket code, not a stand-in for it.

What it does

  • ae_test_bridge_mcp/mock_server.py — MockAEBridge: a real TCP server. Register canned responses with .when(match, response) (matched by a substring in the request's script field, or an arbitrary predicate), a .default(response) fallback for anything unmatched, then point a real client at bridge.host/bridge.port. Every request it receives is recorded in bridge.received_requests for assertions.

  • ae_test_bridge_mcp/server.py — an MCP server wrapping one MockAEBridge instance behind 7 tools (below), for an AI agent to drive directly instead of writing Python.

It cannot actually evaluate the ExtendScript in a request's script field — there's no JS engine here, it's a test double. You tell it what to say back; it doesn't compute anything.

Related MCP server: SpyNet

Tools exposed over MCP

Tool

What it does

start_mock_bridge

Start listening (optional port, default: pick a free one). Returns the bound host/port.

stop_mock_bridge

Stop the running mock bridge.

queue_response

Register a canned response for requests whose script contains a given substring.

set_default_response

Set the fallback response for anything no queue_response rule matched.

get_received_requests

Return every request received so far, in order.

clear_received_requests

Clear the recorded history without stopping the bridge or its rules.

clear_rules

Remove all queue_response rules without stopping the bridge or clearing request history. The default response (if set) is unaffected.

Install

pip install -e .
# or, for running tests too:
pip install -e ".[dev]"

Zero runtime dependencies.

Usage as a Python library (pytest, or anywhere else)

from ae_test_bridge_mcp import MockAEBridge

with MockAEBridge() as bridge:
    bridge.when("app.project.activeItem.name", {"status": "OK", "result": "Hero Comp"})
    bridge.default({"status": "OK", "result": None})

    # Point whatever you're testing at 127.0.0.1:bridge.port instead of
    # the real After Effects listener's fixed port (45445) --
    # e.g. ipc_client.execute_job(job, port=bridge.port).

    assert bridge.received_requests[-1]["script"] == "..."

Responses can also be a one-argument callable (request_dict) -> dict for dynamic or stateful behavior (an incrementing counter, echoing part of the request back, simulating a value that changes across calls):

bridge.when("echo", lambda req: {"status": "OK", "result": req["script"]})

Rules are checked in registration order — the first match wins.

Usage as an MCP server

{
  "mcpServers": {
    "ae-test-bridge": {
      "command": "python3",
      "args": ["-m", "ae_test_bridge_mcp"]
    }
  }
}

A typical agent-driven session: call start_mock_bridge, note the returned port, tell the client under test to connect to that port instead of the real one, call queue_response for the scenarios you want to exercise, run the client, then call get_received_requests to confirm it sent what you expected.

Design notes

  • Hardened tool dispatch. Every MCP tool call is wrapped so a bad argument (wrong type, missing field) returns a normal isError: true result — it can never raise out of handle_call_tool and kill the whole persistent stdio server process. This was a real bug found in a sibling repo during a review pass; fixed here from the start.

  • One bridge at a time, by design. The MCP tool surface manages a single module-level MockAEBridge instance — start_mock_bridge fails loudly if one is already running rather than silently leaking the old one. For multiple concurrent fake bridges in the same process, use the MockAEBridge class directly instead of the MCP tools.

  • Each accepted connection runs on its own thread, so a slow or stuck client doesn't block other connections. Rule/state mutation is lock-protected so registering rules from the main thread while a request is mid-dispatch is safe.

  • A raising response callable never kills the connection silently. If a .when()/.default() callable raises, the client gets back a clean {"status": "ERROR", "error": "... raised ValueError: ..."} instead of the socket just closing with zero bytes.

  • A registered response that isn't JSON-serializable (a set, an arbitrary object) gets the same treatment — a clean error naming the problem, not a silent close.

  • Declared message length is capped (max_size, default 10 MiB, same class of protection as IPC_Client's own max_size — built independently here since this repo deliberately has zero dependencies) so a peer claiming a huge body can't make the server block trying to read it.

  • stop() force-closes in-flight connections rather than waiting out their own read timeout, so tearing down a bridge mid-test doesn't leave a stray thread appending to received_requests after the caller believes it's gone.

Testing

pip install -e ".[dev]"
pytest tests/

Tests use real sockets throughout — no mocking of the socket module — covering rule matching/ordering, dynamic responses, request recording, malformed input on both the raw-socket layer and the MCP tool-argument layer, and the full MCP JSON-RPC stdio loop.

Changelog

  • v1.1.0 — Hardening pass from a first-round review: a raising response/default callable now returns a clean ERROR result instead of silently dropping the connection; a non-JSON-serializable response gets the same treatment instead of closing with zero bytes; incoming messages are capped at max_size (default 10 MiB) so a peer can't make the server block trying to read an oversized declared length; stop() now force-closes any in-flight connections and joins their threads instead of waiting out their own read timeout. Also added the clear_rules tool/method for symmetry with clear_received_requests.

  • v1.0.0 — Initial extraction, as a mock-bridge test double inspired by (but not a direct port of) Dimension's own test_ae_bridge_mcp.py patterns.

License

MIT — see LICENSE.

Available Tools

7 tools
clear_received_requestsA

Clear the recorded request history without stopping the bridge or its response rules.

Inputs: no named parameters.

clear_rulesA

Remove all queue_response rules without stopping the bridge or clearing received-request history. The default response (if set) is unaffected.

Inputs: no named parameters.

get_received_requestsA

Return every request the mock bridge has received so far, in order -- for asserting what a client under test actually sent.

Inputs: no named parameters.

queue_responseA

Register a canned response: any incoming request whose script field contains match gets response back. Rules are checked in the order they were registered; the first match wins.

Inputs: match (required), response (required).

set_default_responseA

Set the response returned when no queue_response rule matches an incoming request.

Inputs: response (required).

start_mock_bridgeA

Start a fake After Effects bridge listener speaking the real wire protocol, for testing bridge client code without real After Effects. Fails if one is already running.

Inputs: port.

stop_mock_bridgeB

Stop the currently running mock bridge.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

    TDQS

    A4/5.0

    Scored across 7 tools

    Disambiguation5/5

    Each tool has a clearly distinct role: lifecycle management (start/stop), response configuration (queue vs default), and request inspection/reset. The relationship between queue_response and set_default_response is well-defined, so there is no real ambiguity.

    Naming Consistency5/5

    All tools follow a consistent snake_case verb_noun pattern: start_, stop_, queue_, set_, get_, clear_. The naming style is uniform and predictable across the entire set.

    Tool Count5/5

    Seven tools is well-scoped for a mock bridge server. Each tool covers a necessary capability—lifecycle, response control, request inspection, and state reset—without redundancy or bloat.

    Completeness4/5

    The surface covers the full testing workflow: start/stop the bridge, configure responses, inspect received requests, and reset state. Minor extras like checking bridge status or removing individual rules are absent but not critical to the core purpose.

    Maintenance

    ActivityNo data
    ResponsivenessSyncing

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      D
      maintenance
      An MCP server that enables AI assistants to control Adobe After Effects through a file-based communication bridge. It supports various operations including project and composition management, layer and keyframe manipulation, rendering, and batch processing.
      92
      23 npm
      21
      MIT
    • A
      license
      B
      quality
      D
      maintenance
      Enables AI assistants to configure and manage session-based REST and WebSocket mock servers for application development and testing. It allows for dynamic endpoint setup, request history inspection, and real-time WebSocket communication through natural language commands.
      8
      MIT