hx-multianim-mcp
Drive and inspect a running hx-multianim game (HashLink or browser via DevBridge) from an MCP client — read, poke, script, and diagnose it.
Connect & health —
connectto a HashLink port/host or a known instance;ping,performance(FPS, draw calls, triangles, objects); picktargetwhen several games are attached.Scene inspection —
list_screens,list_builders,scene_graph,inspect_element,inspect_programmable,get_screen_state,get_tween_state,list_slots,list_interactives,list_active_programmables,list_resources,list_fonts,list_atlases.Layout/hit-testing —
find_element_at,coordinate_transform,check_overlapsto spot broken click regions.Screenshots —
screenshotto PNG, optionally scaled.State manipulation —
set_parameter,get_parameters,set_visibility,reload(.manim/.anim hot reload),eval_manimto validate snippets.Game control —
pause,step(frame-by-frame, max 100),wait_for_idle,quit.Input injection —
send_event(mouse/key/text/wheel),send_events(multi-step drags/scrubs with frame steps),click_buttonby interactive ID.Diagnostics —
get_traces,get_errors,get_debugger_hits(cursor-based breakpoint hits fromDevBridge.debugger), plus a unifiedeventsbuffer.Game ops & data —
list_game_ops/game_op/get_game_eventsfor game-registered queries, commands and events;list_data/get_data/roll_pickfor.manimtables, picks and trees.
Connects to a running hx-multianim application (a Haxe/HashLink game framework) through its DevBridge, exposing tools to inspect and manipulate the live game: scene graph and element inspection, screenshots, hit-testing and overlap detection, runtime parameter/visibility changes, hot-reloading of .manim files, input injection (mouse, keyboard, gestures), pause/step control of the game loop, and reading game data tables, picks, traces, errors and custom game ops/events.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@hx-multianim-mcp connect to localhost:9001 and then list the screens"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
hx-multianim-mcp
An MCP server that connects Claude (or any MCP client) to a running hx-multianim application via its DevBridge.
It reaches a game on HashLink over the DevBridge's HTTP server, and a game in a browser page over a WebSocket relay the page dials into (--listen). Same tools, same answers.
Which game. With exactly one game connected, every tool goes to it;
connectis not needed. With none, the first call looks for a HashLink game on the default port (the port inHX_DEV_READY_FILE, elseHX_DEV_PORT, else 9001). With several, passtarget(an id fromlist_instances, e.g."http:9001"or"web-1", or a bare port) or choose one withconnect. A HashLink game prints its port on startup:[DevBridge] Listening on port N.
Tools
Connection & health
Tool | Description |
| Choose the game calls go to: |
| Browser games on the relay (app, title, URL, session), HashLink games connected, and DevBridges answering on the ten default ports |
| Lightweight health check — uptime and port |
| FPS, draw calls, triangle count, object count, scene dimensions |
Scene inspection
Tool | Description |
| Registered screens with active/failed status |
| Loaded |
| Recursive scene tree dump ( |
| Position, size, visibility, text of a named element |
| Deep inspection of a live programmable (params, slots, refs, elements) |
| Hit-test scene coords, front-to-back list of objects |
| Screen manager state: mode, transitions, pause, counts |
| All active tweens with target, duration, progress |
| Interactive hit-test regions with IDs and bounds |
| Swappable container slots of a programmable |
| Live incremental-mode programmables with current state |
| Loaded sprites, fonts, |
| Registered font names |
| Loaded sprite atlases and tile/sprite names |
| Cursor names an interactive can use: OS ones and tile cursors (size, hot point) |
| Convert between scene and element-local coordinates |
| Detect overlapping interactives/visuals to find layout bugs |
Screenshots
Tool | Description |
| Capture current frame as PNG (optional |
State manipulation
Tool | Description |
| Modify a programmable parameter at runtime (incremental mode) |
| Current parameter values and definitions for a programmable |
| Toggle element visibility |
| Hot-reload |
| Parse and validate |
Game control
Tool | Description |
| Pause/resume the game loop |
| Advance N frames while paused (max 100) |
| Check if no tweens/transitions are active |
| Cleanly shut down the game |
Input injection
Tool | Description |
| Inject a single mouse/keyboard/wheel event |
| Sequence of events with frame steps (drag, scrub, multi-step gestures) |
| Click an interactive by ID, bypassing hit testing |
Diagnostics
Tool | Description |
| Recent |
| Accumulated runtime errors/exceptions |
| Poll |
| Everything the connected games pushed (traces, errors, reloads, screen changes, breakpoints, game events), from a buffer this server keeps (last 1000), with a cursor ( |
Game ops
Tool | Description |
| The queries, commands and events the game registered, and the library's own queries ( |
| Call one of them by name |
| Poll the events the game emitted (cursor-based) |
Game data
Tables, picks and trees: what the game loaded from .manim data blocks, and the tables its own code registers with DataRegistry.registerTable (hx-multianim's bh.multianim.data).
Tool | Description |
| Every table, pick and tree: its kind, rows, and where it is ( |
| One in full: a table's columns and rows (a tree's edges too), a pick's exact odds |
| Draw from a pick with the game's own picker and a seed: the rows the game draws from that seed |
A game built on an hx-multianim without data tables answers these with not_supported.
Sound
The game's sound (hx-multianim's ScreenManager.audio, the sounds, mix and music blocks of its .manim files): what it has, what it did, and a hand on it. Every line of its history also streams as the audio event.
Tool | Description |
| The banks and sounds, the mix as it stands (each bus, the VCAs, the snapshots on), the parameters, the tracks and the one playing, the plays alive, the backend |
| The last 200 lines of what the sound did: plays and drops with their reason, stops, snapshots, VCAs, parameters, music; |
| A VCA to 0..1, a snapshot on or off, a parameter to a label, a sound played or stopped, music crossfaded to or stopped; several at once |
A game built on an hx-multianim without sound answers these with not_supported.
Every tool that talks to a game takes an optional target.
Related MCP server: mcp-defold
Breakpoints from game code
Call DevBridge.debugger(data, pause) anywhere in your game code to capture a data snapshot (with auto-captured file/line/method):
screenManager.devBridge.debugger({hp: unit.hp, target: unit.target?.name}); // pauses by default
screenManager.devBridge.debugger({fps: hxd.Timer.fps()}, false); // push-only, no pauseHits are delivered three ways:
events— kinddebugger, with every other event, from this server's buffer (the reliable one: clients do not always put log notifications in the model's context).Poll —
get_debugger_hitstool withsince_idcursor.Push — warning-level MCP log notifications.
If pause=true, resume with pause({paused:false}).
Usage
Claude Code
// .mcp.json
{
"mcpServers": {
"hx-multianim": {
"command": "npx",
"args": ["-y", "@bh213/hx-multianim-mcp"]
}
}
}A game in a browser: relay mode
A page cannot listen on a port, so the roles swap: this server listens and the game dials in.
{
"mcpServers": {
"hx-multianim": {
"command": "node",
"args": ["../hx-multianim-mcp/dist/index.js", "--listen"]
}
}
}--listen (or --listen=9011, or HX_DEV_WS_PORT) listens on ws://127.0.0.1:9010, trying the next nine ports when busy. Open the game built with -D MULTIANIM_DEV with ?devbridge=ws://127.0.0.1:9010 (add &token=... when a token is set). It appears in list_instances as web-1 (the id stays while the page keeps its session, so a page that dials back after a restart is the same game). HashLink games work alongside it. In a browser game reload needs the file's text (content, or source_path for this server to read) and quit answers not_supported; its tab must be visible (a hidden tab stops its frames).
Wire protocol (one JSON object per message): the game sends {"kind":"hello","protocol":1,"session",...}, this server answers {"kind":"welcome","instance":"web-1"}, then sends {"kind":"call","id","method","params"} and gets {"kind":"result","id","ok",...}; the game pushes {"kind":"event","seq","event","data"}. See hx-multianim's docs/devbridge.md.
Environment variables
Variable | Default | Description |
|
| HashLink DevBridge port tried when nothing is connected |
|
| HashLink DevBridge host |
| — | A game writes |
| — | Sent to every game ( |
|
| A call with no answer by then fails with code |
| — | Relay mode on this port (same as |
|
| Relay bind address |
Error codes
Tool errors come back as {error, code} with isError: not_connected, ambiguous_target, unknown_target, connection_failed, timeout, bad_reply, unauthorized, and the DevBridge's own (not_found, invalid_params, invalid_state, not_supported, unknown_method, internal).
Security
What this server is: a bridge from an MCP client to a game the developer is running, built with -D MULTIANIM_DEV. It does what the game's DevBridge does and nothing else.
Transport. The MCP side is stdio only: no HTTP or WebSocket endpoint for clients. The game side is the DevBridge of a HashLink game (
localhostunlessHX_DEV_HOSTnames another host) or, in relay mode, a WebSocket server bound to127.0.0.1(HX_DEV_WS_HOST) that a game page dials into. Nothing is sent anywhere else.Authentication. Set
HX_DEV_TOKENin both the game and this server: it is sent with every call to a HashLink game and required in every browser game'shello. Without it, anything that can reach the DevBridge port can call it, which is the DevBridge's own default (HX_DEV_BINDon the game side chooses its bind address).No code execution. No tool runs a shell command or evaluates code.
eval_manimparses and builds a.manimsnippet with the game's own parser to report its errors; nothing it builds is shown, kept or run.game_opcalls a handler the game registered in its own code, and does whatever that handler does.Files. This server reads two kinds of file:
HX_DEV_READY_FILE, for the port a game wrote there, andreload'ssource_path, only ever a.manimor.animfile, sent to the game as the new text of a resource. It writes no files.What the game gives back is the game's own state: scene graph, parameters, traces, errors, screenshots, the data tables it registered, and the events its code emits with
DevBridge.emitEvent. All of it comes from the developer's own build, not from players or the network. Treat it as you would the game's logs.Annotations. Every tool declares MCP tool annotations: the readers (
scene_graph,screenshot,eval_manim,list_*,get_data, ...) arereadOnlyHint; the setters and input tools (set_parameter,reload,send_event,pause, ...) write but are not destructive;get_traces,get_errors,get_debugger_hitsandget_game_eventscan clear the buffer they read;quitandgame_op(a command is whatever the game made it) aredestructiveHint. Nothing isopenWorldHint.
Development
npm run build
npm test # node:test against fake games: HTTP + SSE and browser games over the relay
node scripts/smoke.mjs # end to end: runs dist/ over stdio in relay mode and drives whatever
# games it finds (open a browser game with ?devbridge=ws://127.0.0.1:9010)License
BSD-3-Clause
Available Tools
38 toolscheck_overlapsA
Detect overlapping elements to find layout bugs and broken click targets.
Interactive overlaps (severity: high): two clickable regions overlap, causing unreliable clicks
Visual overlaps (severity: low): sibling elements with overlapping bounds (parent-child overlap is normal and ignored) Returns overlap pairs with their bounds, overlap rectangle, and overlap area in pixels.
Inputs: mode, screen, include_hidden, min_overlap_area.
click_buttonA
Directly click an interactive button by its ID, bypassing coordinate-based hit testing. Works even if the button is scrolled off-screen or obscured by other elements. Use list_interactives to discover available button IDs.
Inputs: id (required), screen.
connectA
Connect to a game instance on a specific port (and optionally host). Sets up both DevBridge and SSE connections. Must be called before using any other tools. Default port is 9001, but the actual port should be obtained from the game's stdout output, e.g.: [DevBridge] Listening on port 9002
Inputs: port (required), host.
coordinate_transformA
Transform coordinates between local and global space relative to a named element. Use to_local to convert scene coords to element-local, to_global to convert element-local to scene coords.
Inputs: x (required), y (required), element (required), direction (required), +1 more.
eval_manimA
Parse and validate a .manim source snippet. Returns parsed node names and per-node buildErrors[]. Each build error has 'node' (programmable name or '') and 'error' (message); runtime builder failures additionally include 'file', 'line', 'col', and optional 'code' ('not_a_number', 'missing_ref', etc.) for clickable diagnostics.
Inputs: source (required).
find_element_atA
Hit-test a screen position to find all scene graph objects at the given coordinates, sorted front-to-back by depth
Inputs: x (required), y (required), relative_to.
game_opA
Invoke a game-specific custom query or command registered by the running game. Use list_game_ops to discover available ops and their param shapes. Returns {kind: "query"|"command", op, result}. Errors with code "not_found" for unknown ops, "internal" if the handler throws.
Inputs: op (required), params.
get_debugger_hitsA
Poll recent hits from DevBridge.debugger(data, pause?) calls placed in the game (JS-debugger-style breakpoint). Each hit has {id, data, paused, file, line, method, timestamp}. Use since_id from a previous call as a cursor to get only new hits. Hits also arrive in real time as SSE 'debugger' events (shown as warning-level log notifications). If paused=true, the game is paused at the hit — resume with pause({paused:false}).
Inputs: clear, limit, since_id.
get_errorsA
Get accumulated runtime errors/exceptions since last query
Inputs: clear.
get_game_eventsA
Poll custom game events emitted via DevBridge.emitEvent(name, data) on the Haxe side. Events also arrive in real time as SSE "game_event" notifications. Mirrors the get_debugger_hits cursor pattern: use since_id from the previous response to fetch only new events.
Inputs: clear, limit, types, since_id.
get_parametersC
Get current parameter values and definitions for a live programmable instance
Inputs: programmable (required).
get_screen_stateA
Get detailed screen manager state: mode, active screens, transition status, pause state, element/interactive counts
Inputs: no named parameters.
get_tracesA
Get recent trace() output from the running application (ring buffer of last 200 lines)
Inputs: clear, limit.
get_tween_stateA
Get all active tweens/animations with their targets, duration, elapsed time, and progress
Inputs: no named parameters.
inspect_elementA
Get detailed info about a named element on a screen (position, size, visibility, text content)
Inputs: screen (required), element (required).
inspect_programmableA
Deep inspection of a live programmable: current parameter values, slots, dynamic refs, named elements, interactives, and settings
Inputs: programmable (required).
list_active_programmablesA
List all live incremental-mode programmables currently in the scene. Returns current parameter values, parameter definitions (types), named elements, slots, interactive counts, position, and visibility for each. Only programmables built with incremental:true are tracked.
Inputs: depth, sceneGraph, programmable.
list_atlasesA
List all loaded sprite atlases with their tile/sprite names
Inputs: no named parameters.
list_buildersA
List all loaded .manim builders with their programmable names and parameter definitions
Inputs: no named parameters.
list_fontsA
List all registered font names available for use in .manim files
Inputs: no named parameters.
list_game_opsA
List game-specific custom operations registered by the running game. Returns {queries, commands, events}, each entry has {op|name, description, params|payload} where params/payload is a schema-lite hint (e.g. {lane: "int", count: "int?"}). Call this first to discover what the current game exposes, then use game_op to invoke a query/command, or get_game_events to poll events.
Inputs: no named parameters.
list_interactivesB
List all registered interactive hit-test regions on a screen with their IDs, positions, and metadata
Inputs: screen.
list_resourcesA
List all loaded resources: sprite sheets, fonts, .manim files, .anim files
Inputs: no named parameters.
list_screensB
List all registered screens with their active/failed status
Inputs: no named parameters.
list_slotsB
List all slots (swappable containers) on a programmable with their occupied/empty status
Inputs: programmable (required).
pauseA
Pause or resume the game loop. When paused, all game logic, animations, and rendering stop but the DevBridge remains responsive for inspection. Use step() to advance frame-by-frame while paused.
Inputs: paused.
performanceB
Get FPS, draw calls, triangle count, object count, and scene dimensions
Inputs: no named parameters.
pingA
Health check - returns uptime and port. Lightweight alternative to performance for connection testing.
Inputs: no named parameters.
quitB
Cleanly shut down the running game application
Inputs: no named parameters.
reloadB
Hot-reload a .manim file (or all files if no file specified). On failure, errors[] entries include 'message', 'file', 'line', 'col', 'errorType' ('parse' | 'build' | 'signatureIncompatible'), and 'context' — builder errors now carry accurate file/line/col instead of 0 placeholders.
Inputs: file.
scene_graphB
Dump the scene graph tree showing object types, positions, visibility, and names
Inputs: depth.
screenshotB
Capture the current frame as a PNG image. Provide width and/or height to scale down (aspect ratio is preserved when only one is given; error if both are given with wrong aspect ratio).
Inputs: width, height.
send_eventB
Inject an input event into the running application. Event types:
click: mouse click (push + release) at x,y with button (0=left, 1=middle, 2=right)
mouse_down / mouse_up: separate push/release at x,y
move: mouse move to x,y
key_down / key_up: keyboard key press/release with keyCode (hxd.Key constants)
key_press: key_down + key_up combined
text: text input with charCode
wheel: mouse wheel with delta at x,y
Common key codes: SPACE=32, ENTER=13, ESCAPE=27, TAB=9, A=65, 0=48, UP=38, DOWN=40, LEFT=37, RIGHT=39, F1=112
Inputs: type (required), x, y, delta, +3 more.
send_eventsA
Send a sequence of input events with game frame steps between them. Enables multi-step interactions (drag-and-drop, slider scrub, card hand drag) in a single call.
Each entry in the events array is either:
An event: {type, x, y, button, ...} (same params as send_event)
A frame step: {step: N} — advance N game frames (processes animations, state machines, zone detection)
The game must be paused for frame steps to work. Use auto_pause:true to auto-pause before and resume after.
Example drag: [ {type:"mouse_down", x:100, y:200}, {step:2}, {type:"move", x:200, y:150}, {step:1}, {type:"move", x:300, y:100}, {step:1}, {type:"mouse_up", x:300, y:100} ]
Inputs: events (required), auto_pause.
set_parameterC
Set a parameter on a live programmable BuilderResult (uses incremental mode)
Inputs: param (required), value (required), programmable (required).
set_visibilityC
Toggle visibility of a named element on a screen
Inputs: screen (required), element (required), visible (required).
stepA
Advance the game by N frames while paused, then re-pause. Game must be paused first.
Inputs: frames.
wait_for_idleA
Check if the system is idle (no active tweens, no screen transitions). Returns current state without blocking.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 38 tools
Most tools target a clearly distinct subsystem (scene graph, interactives, traces, events, tweens), but a few clusters overlap: get_parameters vs inspect_programmable vs list_active_programmables all surface parameter values, and list_resources vs list_fonts vs list_atlases are superset/subset. send_event vs send_events vs click_button also need the descriptions to disambiguate. Boundaries are mostly clear thanks to detailed descriptions.
Names overwhelmingly follow snake_case verb_noun or verb patterns (list_*, get_*, set_*, inspect_*, send_event(s)), with no camelCase mixing. Minor deviations like single-word verbs (connect, ping, quit, pause, step, reload, screenshot) and coordinate_transform are still readable and consistent in spirit.
38 tools is heavy, well past the 3-15 sweet spot. The domain is genuinely broad (input, inspection, resources, screens, programmables, diagnostics, events), so most tools map to a distinct subsystem rather than being redundant, but the surface is still on the bulky side.
Covers the full debug-bridge lifecycle: connect/quit, pause/step, input injection, hot-reload, eval, screenshot, scene/element/programmable inspection, resource and screen listing, plus diagnostics (traces, errors, debugger hits, performance) and game-specific ops/events. No obvious dead ends for the stated purpose.
Maintenance
Related MCP Connectors
MCP server to assist with JxBrowser development.
Official remote MCP server bridge for Muumuu Domain.
Make games with AI: one MCP server that connects your agent to Unity, Unreal and Godot editors.
One connector for 15,000+ MCP servers plus your team's private MCPs, from any AI client.
Related MCP Servers
- AlicenseAqualityBmaintenanceConnects MCP clients directly to a live Godot editor, enabling AI assistants to build scenes, edit nodes, and control the editor through over 120 operations.4730,790 PyPI3,482MIT
- AlicenseAqualityBmaintenanceThis MCP server drives Defold games through an in-game debug bridge, allowing MCP clients to build, launch, playtest, and interact with real game builds via tools for input, screen reading, state, and logs.91MIT
- AlicenseNot gradedqualityCmaintenanceEnables an MCP client to connect to a running Blockland client to execute TorqueScript, inspect player state, control character movement and actions, and capture screenshots.MIT
- AlicenseAqualityCmaintenanceConnects an MCP client to a running Serious Editor 2017 / Serious Sam Fusion instance through a local Lua bridge, enabling package and entry inspection, log reading, editor diagnostics, and previewed/undoable entity property edits on a captured selection.14MIT