Skip to content
Conduit

mcp server

construct3-mcp

MCP server that lets Claude, Cursor and other AI assistants read, analyze and safely edit Construct 3 projects — editor-faithful writes with backups and validation.

liauw-media/construct3-mcpcommunitylistedunreviewednot running here yet

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • Node.js 20 or newer
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "construct3-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "construct3-mcp-server"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 17, 2026

Construct3 MCP Server

An MCP server that lets Claude, Cursor and other AI assistants read, analyze and safely edit Construct 3 projects — editor-faithful writes with backups and validation. New here? Start with the User Guide.

v1.10.0 — The server follows the project on disk (no reconnect after an editor save or git restore), drives a running game over the Chrome DevTools Protocol with 12 new runtime tools, checks event sheet edits the way the editor does and keeps layers, instance variables and hierarchy links consistent. See What's new and the CHANGELOG.

License: MIT Node.js TypeScript

What's new in 1.10.0

  • Follows the project on disk — each tool call checks project.c3proj and the files the server has cached and reads again what changed, so there is no reconnect after a save in the Construct 3 editor or git restore. A write over an event sheet, layout, object type or family file changed since the call read it is refused, and the call's earlier writes are put back; each file the writer changes is backed up once per tool call (#51).
  • Drives a running game — serve_preview serves an exported game and launches a browser on it; connect_to_game, call_bridge, wait_for_condition, subscribe_events, simulate_input, screenshot_game and five more tools drive it over the Chrome DevTools Protocol (connecting needs Node.js 22) (#8–#13). inject_runtime_bridge now imports the bridge from the main script, so Construct loads it (not yet checked in the real editor).
  • Event sheet guards — delete_event_sheet refuses while other sheets use its functions or global variables, move_events_between_sheets keeps event variables in scope, and add_event_to_sheet checks function names as the editor does and returns the SIDs it creates (#58, #38).
  • Layers, instances and objects — layer renames update event sheets, instance variables follow the object and its families, hierarchy links go with their instances, family effects are checked, and delete_object keeps its images as .bak. validate_project adds duplicate-function-name, hierarchy-link and orphaned-image (#38).
  • Large and damaged projects — the UID/SID scan of files over the 10MB read limit streams them, so a 608MB layout no longer blocks new UIDs elsewhere, and the analysis tools list objects whose own file cannot be parsed in unanalysedObjects (#59, #60).
  • Stricter — some calls that worked in 1.9.2 are refused now: a sheet name in another letter case, a function name the editor refuses, a copied function block, a move that takes a variable out of its scope, an instance variable that neither the object nor its families declare, and deleting an event sheet, object or family that is still used in ways 1.9.2 did not check. See Changed and Fixed in the CHANGELOG.

What's new in 1.9.2

  • Safer deletes next to unparsed files — delete_object, delete_family, delete_layout, delete_event_sheet, delete_event_from_sheet, update_object_properties, update_family and rename_animation search registered files over the 10MB read limit or with invalid JSON as text and refuse without force on a possible use; results list the unchecked files in unscannedFiles.
  • Usage analysis says "possibly used" — find_orphaned_objects, get_object_dependencies and analyze_performance no longer report objects as unused that such files may use.

What's new in 1.9.1

  • Frame images stay with their frames — add_frame_to_animation with index and delete_frame_from_animation move the image files with the frames, keep replaced or deleted images as .bak, refuse an index past the end and roll back on failure; validate_project reports frames without an image file (frame-image).
  • No duplicate UIDs next to files over 10MB — such layouts and object types are scanned as text for their IDs; validate_project reports them as unscanned-file with complete: false, and valid is false whenever complete is.
  • Function options — add_event_to_sheet takes functionReturnType, functionIsAsync and functionCopyPicked.
  • Per-project index cache — tools no longer mix up two projects opened in one process.
  • User Guide — install, connect your AI tool, first session, safe editing workflow, runtime bridge.

Full list: CHANGELOG.

What's new in 1.9.0

  • Editor load-time checks — validate_project checks a set of rules the Construct 3 editor enforces when it opens a project, and event sheet writes that would add such an error are refused.
  • Behavior conditions and actions the editor reads — written with behaviorType; fix_legacy_behavior_keys repairs sheets from older versions (behavior-type is still accepted as a deprecated input alias).
  • Find events by editor number — locate_event and get_eventsheet_outline turn "sheet, event N, action M" into the event's JSON path.
  • Runtime traps — find_runtime_traps, the construct3://docs/pitfalls resource and the debug_stuck_game prompt for logic that loads but hangs or fails silently.
  • Editor-faithful event shapes — else-if and OR blocks, events without conditions, positional function calls, script lines and the Functions object as the editor saves them; fix_legacy_event_shapes converts older sheets.
  • Safer deletes and editor name rules — deletes and removals of used behaviors, instance variables and family members are refused, ambiguous SIDs are refused (pick one with eventPath), case-only name clashes are refused, and nested sub-layers are supported everywhere.
  • Accurate reports — validate_project, find_orphaned_objects, get_asset_usage and analyze_performance read editor-saved projects correctly (Transitions folder, animation folders, file assets, non-world instances, sub-layers) instead of reporting false problems.
  • Byte-faithful writes — line endings, trailing newline, BOM and file-name case are kept; lowercase image file names and per-instance behavior entries match the editor; every write result carries an editorNote.

Full list: CHANGELOG.

Quick Start

New to this server? Read the User Guide. It covers requirements, setup for each AI tool, a first session, the safe editing workflow (close the project in Construct 3 while the AI edits it) and runtime testing.

The server is not published on npm. Clone it and build it once (Node.js 18 or newer):

git clone https://github.com/liauw-media/construct3-mcp.git
cd construct3-mcp
npm ci    # installs dependencies and builds dist/ (prepare script)

Connect it to Claude Code with absolute paths to dist/index.js and to your project folder (the folder that contains project.c3proj; .c3p files cannot be opened):

claude mcp add construct3 --scope user -- node /absolute/path/to/construct3-mcp/dist/index.js "/absolute/path/to/My Game"
claude mcp list    # construct3: ... - ✔ Connected

Claude Desktop, Cursor, VS Code and others use a JSON config instead, see Usage.

Table of Contents

Why This Exists

The Problem: When you ask Claude Code to work on Construct 3 projects, it directly edits JSON files and often breaks:

  • Object references and unique IDs (SIDs/UIDs)
  • Event sheet dependencies and includes
  • Layout and instance relationships
  • Plugin and behavior configurations
  • The usedAddons registry

The Solution: This MCP server provides a structured, validated interface that:

  • Understands Construct 3's internal file format and ID system
  • Provides structured access to project data via resources and query tools
  • Enables deep analysis (dependency graphs, orphan detection, performance audits)
  • Safely creates, updates, and deletes project entities with automatic backup, ID generation, and validation
  • Links to the official Construct 3 manual and includes a curated list of Construct 3 pitfalls

Features

Resources (Read-Only Data Access)

ResourceDescription
construct3://project/infoProject metadata and basic info
construct3://project/structureComplete project structure overview
construct3://project/addonsAll plugins, behaviors, and effects
construct3://objects/{name}Specific object type details
construct3://eventsheets/{name}Specific event sheet details
construct3://layouts/{name}Specific layout details
construct3://docs/indexIndex of documentation categories and popular topics
construct3://docs/manual/{topic}Link to the construct.net manual page for a topic (the page text is not fetched)
construct3://docs/pitfallsCurated Construct 3 pitfalls (signals, scripts, picking, expressions), each tagged with its source

Query Tools (Read-Only)

ToolDescription
list_objectsList all object types with optional name filtering
list_eventsheetsList all event sheets
list_layoutsList all layouts
list_familiesList all object families
list_timelinesList all timelines (root and subfolders); transitions are listed separately
list_addonsList addons in usedAddons, optionally filtered by type
get_object_detailsGet detailed info about a specific object
get_eventsheet_detailsGet detailed info about an event sheet
get_layout_detailsGet detailed info about a layout
get_timeline_detailsGet a timeline's full JSON (tracks and settings)
search_objectsSearch objects by name pattern
get_project_summaryGet comprehensive project summary

Analysis Tools

ToolDescription
get_eventsheet_flowEvent sheet include hierarchy and layout bindings (Mermaid or JSON)
get_function_mapFunction definitions and call sites across event sheets (Call function actions, Functions.Name(...) expression calls, function map registrations)
get_object_dependenciesWhere objects are used (event sheets, layouts including sub-layers, families); objects that files it could not parse possibly use are marked (possiblyReferencedIn, unscannedFiles), and objects whose own object type file it could not parse are marked unanalysed (listed in unanalysedObjects when no use was found)
find_orphaned_objectsFind objects not used by any event (including object parameters, expressions, script actions and Create object (by name) with a literal name) or layout (including sub-layers, non-world instances and object properties of other instances); objects that files it could not parse possibly use are listed as possiblyUsed instead, and objects whose own file it could not parse as unanalysedObjects
get_asset_usageTrack sound, image, font, video and project file usage (used, unused or not analysed)
analyze_performanceHeuristic performance audit with categorized issues
validate_projectIntegrity checks: missing files, required fields, duplicate SIDs/UIDs, repeated layer names, function names shared by several function blocks, broken references and includes, behaviors and instance variables that events use but their object lacks, missing addons, legacy "behavior-type" keys, layout instances without behavior entries, hierarchy links to missing instances or one-sided ones, event shapes older versions wrote that the editor never writes, scripts in the one-string shape of older Construct 3 releases, orphaned and backup files, image files named after no object type, plus the rules the C3 editor enforces at load (trigger and else placement, expression syntax limited to unterminated string literals and backslashes outside them, empty expressions, duplicate names/SIDs, family plugins). Rules verified only in part are reported as warnings (details in API.md). valid means no errors and every registered file was checked; complete (and so valid) is false when a registered file exists but was not checked (over the 10MB read limit, invalid JSON, unreadable; listed in unscannedFiles)
get_group_settingsEvent group settings (isActiveOnStart, disabled) across sheets, filterable by sheet and active state
locate_eventMap an editor event number ("es_game, event 72, action 1") to its JSON path, sid, content and neighbouring events
get_eventsheet_outlineReadable, paged event sheet outline with editor event numbers (IF/DO/CALL/SCRIPT/GROUP/FUNCTION/VAR)
find_runtime_trapsRuntime traps: Wait for signal tags nothing signals, waits that start after a call already raised their tag, unused/dynamic signal tags, scripts using function parameters without localVars

Mutation Tools (Safe Write Operations)

Objects and families

ToolDescription
create_objectCreate a new object type (Sprite, Text, TiledBg, global plugins, etc.); refuses names that clash with an object type, a family, System or the Functions object, ignoring case
update_object_propertiesAdd/remove instance variables and behaviors, change global status; removing one that events still use is refused, listing the uses, unless forced
delete_objectDelete an object; refused while anything uses it (events, instances on any layer or sub-layer, families), listing where, unless forced; its image files are kept as .bak
create_familyCreate a family; refuses name clashes and members of mixed plugins (load-time checked)
update_familyAdd/remove members and shared instance variables; refuses member changes that mix plugins (load-time checked), and removing an instance variable or member through which events still use the family's instance variables, behaviors or effects, unless forced
delete_familyDelete a family; refused while events or object properties name it or events use its instance variables, behaviors or effects through a member, listing where, unless forced

Event sheets

ToolDescription
create_event_sheetCreate a new event sheet with optional includes; refuses names that differ from an existing sheet only in case
add_event_to_sheetAdd a group, function, variable, include, or comment (with colours) to a sheet (load-time checked); functions take the editor's return type, Asynchronous and Copy picked options; the names of a new (global) variable, of a function and of its parameters are checked like in the editor (no second function with the same name, ignoring case); returns the new SIDs (generatedSid, functionParameterSids), eventPath and backupFile
add_event_blockAdd a block event with conditions + actions (gameplay logic), written in the editor's own shapes: sub-events (also without conditions), else/else-if blocks, OR blocks, function calls, script actions, comment rows; refuses writes that break the checked editor load-time rules (unterminated string literals and backslashes outside them, empty expressions, trigger placement) and warns where Else cannot stand (after a triggered event)
update_event_blockUpdate an existing block: modify/add/remove actions and conditions, edit comment rows (text and colours) in place, make it an else or OR block (load-time checked)
update_event_block_actionReplace the parameters of one action in a block (by block SID and action index; function call arguments as an array; load-time checked)
update_event_variableRename a variable or change its type, initial value, static or constant flag; a new name is checked like in the editor
move_events_between_sheetsCopy or move top-level events between sheets by SID (optionally into a group); load-time checked, so copying an event that breaks a load-time rule is refused; a copy or move that would clash event variable names (e.g. a copied global variable) is refused, and so is (without force) a move that takes a used variable out of scope, e.g. a global variable moved into a group
delete_event_from_sheetDelete an event from a sheet by SID or include name (dry-run, force); refuses while functions or event variables it removes are still referenced elsewhere (by calls, function maps, System variable ACEs or by name in expressions); warns about an else block the delete leaves behind
remove_event_from_sheetRemove an include from a sheet by included sheet name
delete_event_sheetDelete an event sheet (with reference checking and optional force): includes, layout bindings, and uses in other sheets of the functions and global variables it defines
fix_legacy_behavior_keysRename legacy "behavior-type" keys (written by older versions) to "behaviorType" in all event sheets, checking each name against the object's behaviors (dry-run by default)
fix_legacy_event_shapesConvert event shapes written by older versions into the editor's own (block isElse to a System else condition, condition isOr to isOrBlock, old-shape function calls to positional arguments, one-string scripts, as older Construct 3 releases also saved them, to lines) where the result is unambiguous; reports the rest and which conversions can change how an event runs (dry-run by default)

Event SIDs are not always unique in editor-saved sheets. The tools that find an event by SID refuse a SID shared by several events in the sheet and list the candidates; pass eventPath (the JSON path that locate_event returns, e.g. events[3].children[1]) to pick one. move_events_between_sheets keeps SIDs and warns when a copy leaves such a shared SID in the target sheet. See API.md.

Layouts, layers and instances

ToolDescription
create_layoutCreate a new layout with configurable layers; refuses names that differ from an existing layout only in case
update_layoutUpdate layout event sheet binding and dimensions
delete_layoutDelete a layout (blocks startup layout, checks references)
add_layerAdd a layer (position, visibility, transparency, parallax, blend mode); refuses a name any layer or sub-layer of the layout uses, ignoring case
update_layerRename a layer or sub-layer or change visibility, interactivity, parallax, blend mode, scale rate, Z elevation; refuses a new name used by another layer or sub-layer, ignoring case; a rename points the event parameters that name the layer at the new name (unless another layout has a layer of that name) and lists other strings that may name it
delete_layerDelete a layer or sub-layer with its sub-layers (never the last top-level one; blocked while they hold instances unless forced); hierarchy links of other instances to the deleted ones are removed
add_instance_to_layoutPlace an object instance on a layout layer or sub-layer with full property control; writes a value for every instance variable of the object and its families and refuses names they do not have
update_instanceUpdate a placed instance by UID on any layer or sub-layer (position, size, angle, color, visibility, tags, instance variables of its object and families)
delete_instance_from_layoutRemove a placed instance by UID (layers, sub-layers and non-world instances); its hierarchy children stay without a parent, and its parent no longer lists it

Sprite animations

ToolDescription
add_animation_to_spriteAdd a new animation to a Sprite object
update_animation_propertiesUpdate animation speed, looping, ping-pong, repeat count
rename_animationRename an animation with its frame image files and the layout instances starting with it
delete_animationDelete an animation (never the last one)
add_frame_to_animationAdd a blank frame (placeholder PNG) at an index; later frame images move up with their frames
update_frameUpdate a frame's duration, size or origin
delete_frame_from_animationDelete a frame by index (never the last one); its image is kept as .bak, later frame images move down
replace_sprite_imageReplace a frame's image with base64 PNG data

Timelines

ToolDescription
create_timelineCreate a timeline (duration, loop, ping-pong, repeat count, start-on-layout); refuses a case variant of a timeline in the same folder
update_timelineUpdate timeline settings or enable/disable it
delete_timelineDelete a timeline (backs up exactly the file it deletes; errors and leaves project.c3proj unchanged when the file is missing)

Project and addons

ToolDescription
update_project_metadataUpdate project name, version, author, or description
register_addonAdd a plugin, behavior or effect to usedAddons
unregister_addonRemove an addon from usedAddons (built-ins need force)

Runtime Tools (Live Game Control)

ToolDescription
inject_runtime_bridgeAdd the bridge script that exposes the running game as globalThis.__c3bridge, imported by the main script (or as the main script of a project without one) so Construct loads it
remove_runtime_bridgeRemove the bridge script, its registration and every line that only imports it (the one it added, or one typed by hand); refuses, changing nothing, while a script uses the bridge otherwise
get_bridge_commandsList the commands call_bridge runs (callFunction, getGlobalVar, getObjectState, layerToCssPx, the subscription commands, ...)
connect_to_gameConnect to the game's browser tab over the Chrome DevTools Protocol; finds the tab whose bridge is ready, on the page or in the runtime's worker (pageUrl or urlContains name it; several ready tabs are refused, not guessed), and brings it to the front
disconnect_from_gameClose a connection
call_bridgeRun a bridge command and return its result; a command that times out is withdrawn
wait_for_conditionWait until a global variable, object property or layout matches (page expressions only with C3MCP_ALLOW_EVAL=1)
subscribe_events / read_events / unsubscribe_eventsBuffer global-variable changes, layout changes and custom events (__c3bridge.emit) between polls
simulate_inputClick, move, touch (tap, long press, swipe), press keys and type text, in viewport, canvas or layout coordinates
get_canvas_sizeThe game canvas's position and size, for input coordinates
screenshot_gameSave the page or the canvas as PNG or JPEG, to a new file outside the project (overwrite: true to replace one)
serve_previewServe an exported game folder on 127.0.0.1 and optionally launch Chrome or Edge on it with its own debugging port and profile
stop_previewStop preview servers and the browsers they launched
generate_bridge_eval_scriptReturn the browser-console lines that submit a bridge command and read its result, plus a Python snippet that only prints those lines (nothing connects to the browser)
export_for_previewPre-flight check: where the runtime runs (Use worker), bridge injection unless injectBridge: false, and the next steps
clone_projectDeep-copy the project with optional bridge injection
pack_projectPack the project folder into a .c3p file that Construct 3 can open (injects the bridge into the project first unless injectBridge: false, and says so with a warning)

The runtime tools test a running game from the AI tool: inject the bridge, preview the game in a browser started with --remote-debugging-port or export it and serve_preview the folder, then connect_to_game and drive it with call_bridge, wait_for_condition, simulate_input and the event tools. The connection tools need Node.js 22 or later. A game with Use worker on works too: the bridge is reached in the runtime's worker. See the User Guide.

inject_runtime_bridge                         (then reload the project in Construct 3, export it)
serve_preview   { folder, launchBrowser: true, headless: true }
connect_to_game { cdpEndpoint: <browser.pageEndpoint> }
call_bridge     { connectionId, command: "callFunction", args: { name: "StartGame" } }
wait_for_condition { connectionId, condition: { type: "globalVar", name: "GameState", operator: "eq", value: "READY" } }
simulate_input  { connectionId, action: { type: "click", x: 540, y: 720 }, coordinateSpace: "canvas" }
stop_preview    {}

The bridge can also be used from the browser console: globalThis.__c3bridge.submit("getGlobalVar", { name: "Score" }) returns an id, and getResult(id) returns { ok: true, value } once, after the next tick (null while pending).

Remove the bridge before exporting the game for players: while it runs, anyone with the browser console can change variables and call functions. remove_runtime_bridge deletes the script file, its entry in project.c3proj and the import line in your main script, the marked one inject_runtime_bridge added as well as one you typed by hand for v1.9.2. See the User Guide.

Prompts (Workflow Templates)

PromptPurpose
analyze_projectAnalyze project structure and organization
find_object_usageFind where a specific object is used
explain_eventsheetExplain how an event sheet works
review_game_logicReview overall game logic architecture
document_objectGenerate documentation for an object
optimize_projectGet optimization suggestions
debug_stuck_gameDiagnose soft-locks and silently dead features (runs find_runtime_traps, uses the pitfalls doc)

Safety Model

Mutation tools follow a strict safety protocol (exceptions below):

  1. Validation — Names checked for reserved words, path traversal, format. Plugin/behavior IDs validated against usedAddons.
  2. Backup — JSON files are backed up to <filename>.bak before modification, once per tool call: a call that writes a file twice (create_object registering a new plugin and then the object in project.c3proj) keeps the state from before the call.
  3. ID Generation — SIDs (15-digit random), UIDs (sequential), and imageSpriteIds (7-digit) are collision-checked against the entire project. Layouts and object types over the 10MB read limit or with invalid JSON are scanned as text for their UIDs and SIDs; when a registered layout or object type exists but cannot be read at all, a new UID is refused instead of guessed. The project is scanned on first use and again after a change on disk the server did not make; the server's own writes add their IDs without a new scan, and an ID once handed out is never handed out again.
  4. Write — JSON is pre-validated (round-trip test, size limit), then written to a temp file and renamed into place. Files keep their text style (see below).
  5. Verify — Files are read back, compared with what was written, and re-parsed to confirm integrity.
  6. Cache Invalidation — All reader caches and indexes are cleared so subsequent reads see fresh data.

Changes made outside the server are picked up. Every tool call, resource read and prompt starts from the project as it is on disk: the server checks project.c3proj, and the files its caches hold the first time the call uses them, by modification time, size and file id, and reads again what changed, for example after git restore or a save in the Construct 3 editor. There is no need to reconnect. A write never replaces a change it did not read: when an event sheet, layout, object type or family file changed on disk after the tool call read it, the write is refused with a message that names the file, and the file keeps that change; run the tool again. A call refused this way after it had written other files puts those back first, so it changes nothing. The server's own updates of project.c3proj (registering or removing an entity, addon auto-registration, project metadata) read the file right before they write it and keep a change made there; the timeline, addon and runtime-bridge tools refuse a project.c3proj that changed during the call, before they write anything (the runtime-bridge tools also right before they write project.c3proj, putting back what they wrote before). Of two parallel tool calls that change the same event sheet, layout, object type or family file, the second one is refused the same way; the animation tools and the project.c3proj updates above run one after the other. Changes that keep a file's size, modification time and file id (possible on file systems with coarse timestamps) are not seen. The check costs one file status per file the server has read when a call uses cached data: about 100 ms on a project of 3,000 files.

Steps 2, 4 and 5 apply in full to writes that go through the project writer: objects, families, event sheets, layouts, animations, project metadata and addon auto-registration. The other write paths do less:

  • register_addon and unregister_addon replace project.c3proj through a temp file, with no .bak backup and no read-back check.
  • The timeline tools back up the timeline file and project.c3proj and write through a temp file, but do not read the result back.
  • The runtime tools (inject_runtime_bridge, remove_runtime_bridge, and export_for_preview / pack_project when they inject the bridge) write project.c3proj (only when the registration changes), the bridge script and the one import line in the main script in place, with no backup or read-back check. remove_runtime_bridge also takes out bridge import lines typed by hand, and refuses before writing anything while a script uses the bridge in a way it cannot take out, or a script it has to check cannot be read. When one of their writes fails, both put back the files they wrote before.
  • PNG images are written without a backup, except that the frame tools keep a file they would replace or remove as .bak (see Frame images move with their frames below), and delete_object keeps the image files of the object it deletes as .bak.

The runtime connection tools write nothing to the project and keep to this machine:

  • serve_preview listens on 127.0.0.1 only, refuses a port another program answers on, answers only requests addressed to 127.0.0.1, localhost or [::1] (DNS rebinding), and serves only files whose real path lies in the exported folder. The interface, the browser (CHROME_PATH or the usual install locations) and its debugging port are not tool parameters. The browser gets a fresh profile and picks its own debugging port, and stopping ends only that browser and removes its profile.
  • connect_to_game reaches browsers on this machine only, including the page endpoints their debugging port lists, unless the server runs with C3MCP_ALLOW_REMOTE_CDP=1; wait_for_condition evaluates JavaScript in the page only with C3MCP_ALLOW_EVAL=1. Both are settings of whoever starts the server, not tool parameters, so a prompt that steers the tool calls cannot lift them. When several tabs have a ready bridge, connect_to_game refuses instead of picking one, so another site that defines a bridge of its own cannot take the connection (and with it the typed input). Everything else goes through the bridge's fixed commands, which still run game functions and set variables in the connected game.
  • screenshot_game writes one image file you name: an absolute path outside the project folder, with the extension of its format, and an existing file only with overwrite: true.

Close and reopen the project in Construct 3 before saving there. The editor keeps an open project in memory, so saving from a session that was opened before these edits can overwrite them. Its Project Bar reload (F9) re-reads script files only, not event sheets, layouts or project.c3proj. Every response that reports a completed write carries this reminder as editorNote. Error responses do not, even when a multi-step tool (e.g. create_object) failed after an earlier step had already written.

Additional safeguards:

  • Reference checking — delete_object, delete_family, delete_event_sheet, and delete_layout scan for references before deleting; delete_event_from_sheet and delete_event_sheet check for calls and uses of the functions and event variables they remove (for a deleted sheet: its functions and global variables, used from other sheets); update_object_properties and update_family check the events before removing an instance variable, a behavior or a family member. Registered files these checks cannot parse (over the 10MB read limit, not valid JSON) are searched as text for the names they look for: a match is a possible use (the name may be in another string), and it refuses without force just like a use, as does such a file that cannot be read at all. The response lists these files in unscannedFiles; without a match the tool goes ahead and warns that the file was only searched as text. delete_object, delete_family and delete_event_sheet also refuse when the object type's, family's or event sheet's own file could not be parsed, and rename_animation refuses when such a layout possibly has instances that start with the animation. delete_event_sheet, delete_event_from_sheet and move_events_between_sheets walk the events of all event sheets for the uses of the functions and event variables they delete or move and stop after 100,000 events (sub-events included); in a project that large the uses beyond are unknown, so they refuse without force then as well.
  • Addon auto-registration — When creating objects with new plugins or adding behaviors, known Scirra addons are automatically registered in usedAddons. Unknown/third-party addons are blocked with an error unless register_addon added them to usedAddons first (it does not check the ID or install anything). update_object_properties checks all behaviors it adds before it registers the first, so an unknown one leaves usedAddons unchanged.
  • Global plugin protection — Singleglobal-inst objects (Audio, AJAX, etc.) cannot be placed on layouts.
  • Plugin-specific defaults — Instances are created with correct default properties for each plugin type (Sprite, Text, TiledBg, NinePatch).
  • Image generation — Sprite and TiledBg creation automatically generates valid placeholder PNGs, named like the editor names them: images/<object>-<animation>-000.png, all lowercase. Batch writes roll back on failure.
  • Frame images move with their frames — The editor names a frame's image file after the frame's index, so add_frame_to_animation with an index and delete_frame_from_animation rename the image files of the later frames one index up or down (JPEG and other formats keep their extension). The deleted frame's image, and any unused file whose name a moved image or new placeholder needs, are kept as <file>.bak instead of being deleted or replaced. If a write fails, the placeholder is removed, the object file restored and the image files renamed back; when another write replaced the object file in the meantime, its content is kept and the image files are made to match it. The animation tools run one at a time, so parallel calls on one Sprite cannot move each other's images or write its object without each other's frames. validate_project reports frames without an image file (frame-image) and lists the .bak files in images/ (backup-file).
  • Layout instance sync — Like the editor, every layout instance carries an entry for each behavior of its object type and of the families it belongs to, with the built-in behaviors' default property values, and a value for each of their instance variables. add_instance_to_layout writes these entries and values; adding or removing a behavior or instance variable (update_object_properties, update_family) or changing family membership (update_family, delete_family) updates the existing instances, and a family's effect entries go with the family. add_instance_to_layout and update_instance refuse values for instance variables the object and its families do not have.
  • Hierarchy links follow deletions — Hierarchy (scene graph) links name UIDs, and a new instance can get the UID of a deleted one. delete_instance_from_layout and delete_layer therefore remove the links to the instances they delete (children stay in the layout without a parent), new UIDs are above every UID a link names, and validate_project reports links that point at no instance or only one way (hierarchy-link).
  • Names compared like the editor — Create and rename tools refuse a name that differs from an existing one only in case where Construct 3 compares names ignoring case: event sheets and layouts (project-wide), object types and families, the layers of one layout (sub-layers included; the editor cannot load a layout with two such layers), the animations of one sprite (in any animation folder), and sibling project-bar folders. Timeline names are compared exactly, as the editor does, but a case variant of a timeline in the same folder is refused because both would share one file on Windows and macOS. Tools that check an entity together with the rest of the project take its name only as registered, letter case included: update_object_properties, update_family, and the event sheet tools whose checks compare a sheet with the other sheets (add_event_to_sheet, delete_event_sheet, delete_event_from_sheet, update_event_variable, move_events_between_sheets).
  • Event variable names checked like the editor — add_event_to_sheet and update_event_variable refuse the event variable and function parameter names the editor's variable and parameter dialogs refuse: a name that matches, ignoring case, an event variable or function parameter in its scope (for a global variable, any in the project; for a local one or a parameter, the globals, the variables and parameters of its enclosing events and those below its parent event or function), the name of a System expression (e.g. time, random), and names with whitespace, punctuation such as - . :, a leading underscore or only digits. Names of object types and families are allowed, as in the editor. move_events_between_sheets refuses a copy or move that would create such a clash, e.g. a copy of a global variable (the editor renames a pasted variable instead). Function names must differ, ignoring case, from every other function in the project and from System expression names; a function with a return type also follows the character rules above. move_events_between_sheets refuses to copy a function block, and validate_project reports names that several function blocks share (duplicate-function-name).
  • No overwrite on create — Create tools refuse to write an entity JSON file (object type, family, event sheet, layout) or a timeline file where one already exists, also one whose name differs only in case (an unregistered file, or one registered under another spelling). Nothing is backed up or replaced. Placeholder PNGs are not covered: create_object and add_animation_to_sprite write them over an image file of the same name in images/, e.g. one left behind by a deleted animation (delete_object keeps the images of the object it deletes as .bak); add_frame_to_animation renames such a file to <file>.bak first.
  • File names kept — Rewriting an existing file keeps its name on disk exactly, including case (e.g. Layout1.json registered as layout1); the .bak backup takes the same name.
  • Text style preserved — JSON is written the way Construct 3 saves it (tab indent). A file that already exists keeps its own line endings (e.g. CRLF from a git core.autocrlf checkout), exact trailing whitespace and BOM. A new file follows project.c3proj, then the first JSON file with line breaks in its target folder, then Construct 3's own style (LF, no trailing newline, no BOM). For files in Construct 3's tab layout, diffs show only the lines that changed; files indented another way (e.g. with spaces) are re-indented with tabs in full.

Documentation

Detailed documentation is available in the /docs folder:

  • User Guide - Install, connect your AI tool, first session, safe editing workflow, runtime testing
  • Architecture - System design, components, and data flow
  • API Reference - Complete reference for all resources, tools, and prompts
  • Examples - Usage examples and workflows
  • Development Guide - Contributing, adding tools, C3 format notes
  • Troubleshooting - Common issues and solutions

Installation

Prerequisites

  • Node.js >= 18.0.0
  • npm or yarn
  • A Construct 3 project saved in folder format (.c3proj, not .c3p)

Install Dependencies

The package is not on npm. Clone the repository and install:

git clone https://github.com/liauw-media/construct3-mcp.git
cd construct3-mcp
npm ci

npm install works too. Do not install with --omit=dev or --production: the build step needs the TypeScript dev dependency and fails without it.

Build

npm ci and npm install already build the server (the prepare script runs npm run build), which compiles TypeScript to JavaScript in the dist/ folder. Run the build yourself only after changing the source:

npm run build

Usage

Step-by-step instructions for each client are in the User Guide.

The server finds its project in this order: the first argument after dist/index.js (a project folder or the .c3proj file), the C3_PROJECT_PATH environment variable, the working directory it is started in. A folder must contain the .c3proj file directly; subfolders and parent folders are not searched. Most clients do not start the server in your project folder, so pass the project path.

MCP config (Claude Desktop, Antigravity, Windsurf and a hand-written Claude Code .mcp.json; Cursor and VS Code need "type": "stdio", and VS Code uses a different top-level key, see below):

{
  "mcpServers": {
    "construct3": {
      "command": "node",
      "args": ["/absolute/path/to/construct3-mcp/dist/index.js", "/absolute/path/to/your-project"]
    }
  }
}

On Windows, write paths in JSON with forward slashes ("C:/Games/My Game") or doubled backslashes ("C:\\Games\\My Game"); single backslashes make the file invalid JSON. Each path is one array element, also when it contains spaces. Leave out the project path only if the client starts the server in the folder that contains the .c3proj.

With Claude Code

Add the server with the CLI:

claude mcp add construct3 --scope user -- node /absolute/path/to/construct3-mcp/dist/index.js "/absolute/path/to/your-project"
claude mcp list

claude mcp list should show construct3: ... - ✔ Connected. Inside a session, /mcp shows the status and can reconnect the server.

  • --scope user stores the server in ~/.claude.json for every folder. Without --scope (local scope) it only applies when claude is started in the folder where you ran the command; if that folder is in a git repository, it applies anywhere in that repository (a second claude mcp add construct3 for another game in the same repository fails with already exists in local config; see the User Guide for several games). --scope project writes a .mcp.json into the current folder.
  • A .mcp.json in the project folder (the JSON above) also works, but Claude Code does not start it until you approve it: claude mcp list shows ⏸ Pending approval until you start claude in that folder, trust the folder and approve the server.
  • Claude Code does not read ~/.claude/mcp.json.
  • Without a project path, the server only starts when Claude Code is started in the folder that directly contains the .c3proj. Started anywhere else, claude mcp list shows ✘ Failed to connect.

Windows shell pitfalls (PowerShell and --, Git Bash and backslashes) and tool permissions: User Guide.

With Claude Desktop

Add the config to your Claude Desktop settings file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Note: Claude Desktop doesn't change working directory per-project, so pass the project path explicitly in args. Quit Claude Desktop completely and start it again after editing the file; closing the window is not enough.

With Cursor

Add the config to .cursor/mcp.json in your project root (project-specific) or ~/.cursor/mcp.json (global). Cursor's documentation lists "type": "stdio" as a required field of each server entry. In .cursor/mcp.json, "${workspaceFolder}" can stand for the project path:

{
  "mcpServers": {
    "construct3": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/construct3-mcp/dist/index.js", "${workspaceFolder}"]
    }
  }
}
  1. Restart Cursor after adding or modifying the config
  2. The Construct 3 tools appear in Cursor's AI agent

With VS Code

VS Code (GitHub Copilot agent mode) reads .vscode/mcp.json, whose top-level key is "servers", not "mcpServers", and each entry needs "type": "stdio":

{
  "servers": {
    "construct3": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/construct3-mcp/dist/index.js", "${workspaceFolder}"]
    }
  }
}

With Antigravity

Add the config to Antigravity's MCP configuration:

  • Via UI: Click the ... menu in the Agent panel → MCP Servers → Manage MCP Servers → View raw config
  • Direct edit: the current Antigravity documentation names ~/.gemini/config/mcp_config.json (global) and .agents/mcp_config.json (workspace); the UI route above opens the right file

Note: Antigravity doesn't set a working directory per-project, so pass the project path explicitly in args.

With Windsurf

Windsurf (now Devin Desktop) uses the same mcpServers format. Its Cascade agent allows at most 100 tools in total, and this server alone brings 83.

Standalone Testing

# Auto-detect .c3proj in current directory
cd /path/to/project-folder
node /path/to/construct3-mcp/dist/index.js

# Or pass explicit path
node dist/index.js /path/to/project.c3proj
node dist/index.js /path/to/project-folder

On success the server prints Construct3 MCP Server ready (on stderr) and then waits silently for an MCP client on stdin; that is not a hang. Press Ctrl+C to stop it. On a wrong path it prints Failed to start server: ... and exits with code 1.

Example Queries

Once the MCP server is running, ask Claude:

Project Analysis:

  • "What objects are in my Construct 3 project?"
  • "Give me an overview of the project structure"
  • "What plugins and behaviors are being used?"
  • "Find orphaned objects that aren't used anywhere"
  • "Run a performance audit on my project"

Code Understanding:

  • "Explain how the MainSheet event sheet works"
  • "Show me the event sheet include hierarchy"
  • "Map all the functions in the project"
  • "What objects depend on the Player?"

Safe Modifications:

  • "Create a new Sprite object called Enemy"
  • "Add a health variable to the Player object"
  • "Create an event sheet for the menu logic"
  • "Add a new layout called LevelSelect with two layers"
  • "Place a Player instance at position 100, 200 on the Game layout"

Documentation:

  • "Which Construct 3 pitfalls should I know about?" (reads construct3://docs/pitfalls)
  • "Give me the manual link for the Sprite plugin" (the documentation resources return construct.net links, not the page text)

Development

Project Structure

construct3-mcp/
├── src/
│   ├── index.ts                    # Entry point: project path, server setup, registration, shutdown
│   ├── construct3/
│   │   ├── project-reader.ts       # Project file reads, caches, checks for changes on disk
│   │   ├── project-writer.ts       # Safe writes (backup/validate/write/verify), project.c3proj updates, undo of a tool call's writes
│   │   ├── id-generator.ts         # SID/UID/imageSpriteId generation with collision avoidance
│   │   ├── disk-state.ts           # File states on disk, tool call scope, stale-write error
│   │   ├── templates.ts            # Entity templates and known addon maps
│   │   ├── event-shapes.ts         # The event shapes the editor writes (else, OR, calls, scripts)
│   │   ├── instance-behaviors.ts   # Behavior entries on layout instances
│   │   ├── instance-variables.ts   # Instance variable values and effect entries on layout instances
│   │   ├── hierarchy.ts            # Hierarchy (scene graph) links between layout instances
│   │   ├── layer-references.ts     # Event sheet strings that name a layer (update_layer rename)
│   │   ├── object-images.ts        # An object type's image files in images/ (kept as .bak on delete)
│   │   ├── animation-rename.ts     # Frame image files and layout instances a rename_animation changes, frame image moves on frame insert/delete
│   │   ├── json-format.ts          # On-disk text style (line endings, trailing newline, BOM)
│   │   ├── layers.ts               # Layer trees: every layer and sub-layer, their instances, layer names
│   │   ├── atomic-write.ts         # Temp-file-and-rename writes that keep file names on disk
│   │   ├── names.ts                # Case-insensitive name and folder comparison
│   │   ├── event-variable-names.ts # Editor name rules for event variables, function parameters and function names
│   │   ├── path-utils.ts           # Path resolution inside the project folder
│   │   ├── png-generator.ts        # Zero-dep placeholder PNGs and image file names
│   │   ├── raw-text-search.ts      # Streamed whole-word text search and UID/SID scan in files the reader skips
│   │   ├── timeline-folders.ts     # The editor's Transitions folder in the timelines container
│   │   ├── types.ts                # TypeScript type definitions
│   │   └── analyzers/
│   │       ├── index-builder.ts    # Cross-reference index (cached per reader)
│   │       ├── event-flow.ts       # Event sheet include hierarchy and layout bindings, function map
│   │       ├── object-deps.ts      # Object dependencies, orphaned and unanalysed objects
│   │       ├── asset-usage.ts      # Asset usage tracking
│   │       ├── animations.ts       # Sprite animation trees (items + subfolders)
│   │       ├── event-outline.ts    # Editor event numbers, event sheet outline
│   │       ├── performance.ts      # Performance heuristics
│   │       ├── integrity.ts        # Project integrity checks (validate_project)
│   │       ├── load-rules.ts       # Editor load-time rules (validate_project, pre-write checks)
│   │       ├── legacy-behavior-keys.ts # Legacy "behavior-type" key scan and repair
│   │       ├── legacy-event-shapes.ts # Legacy isElse/isOr/function call/script shape scan and repair
│   │       ├── delete-references.ts # Function and variable names an event or sheet delete would leave dangling
│   │       ├── unscanned-uses.ts   # Possible uses in registered files the bulk reads skipped
│   │       ├── behavior-refs.ts    # Behavior name checks against objects and families
│   │       ├── effect-uses.ts      # Conditions and actions that name an effect of their object (family effect checks)
│   │       ├── group-settings.ts   # Event group settings (get_group_settings)
│   │       ├── runtime-traps.ts    # Signal pairing and order, script/parameter traps
│   │       └── script-scan.ts      # Lightweight JS/TS scanner for script actions
│   ├── resources/
│   │   ├── project.ts              # Project data resources (6)
│   │   ├── docs.ts                 # Documentation resources (3)
│   │   └── pitfalls.ts             # Curated pitfalls text (construct3://docs/pitfalls)
│   ├── runtime/
│   │   ├── bridge.ts               # Injectable runtime bridge script generator
│   │   ├── cdp-client.ts           # CDP client: game connections, bridge calls, input, screenshots
│   │   ├── preview-server.ts       # Loopback server for exported games, browser launch
│   │   └── zip-writer.ts           # Zero-dep ZIP writer for .c3p packing
│   ├── tools/
│   │   ├── query.ts                # Query tools (9)
│   │   ├── analysis.ts             # Analysis tools (11)
│   │   ├── mutations.ts            # Registers the domain tool modules below
│   │   ├── shared.ts               # Validation, result/error helpers, editor reload note
│   │   ├── project-sync.ts         # withProjectSync: each handler in a tool call scope, after a check of project.c3proj on disk
│   │   ├── object-tools.ts         # Object and family tools (6)
│   │   ├── event-tools.ts          # Event sheet tools (12)
│   │   ├── event-helpers.ts        # Event Zod schemas, builders, validators, load-time gate
│   │   ├── layout-tools.ts         # Layout, layer and instance tools (9)
│   │   ├── animation-tools.ts      # Sprite animation and frame tools (8)
│   │   ├── timeline-tools.ts       # Timeline tools (5)
│   │   ├── project-tools.ts        # Project metadata and addon tools (4)
│   │   └── runtime-tools.ts        # Runtime control tools (19)
│   └── prompts/
│       └── workflows.ts            # Workflow prompts (7)
├── test/                           # Vitest suites, mocks and fixtures
├── dist/                           # Compiled JavaScript (generated)
├── package.json
├── tsconfig.json
├── CHANGELOG.md
└── README.md

Development Commands

# Install dependencies
npm install

# Build (compile TypeScript)
npm run build

# Watch mode (auto-rebuild on changes)
npm run dev

# Run the test suite (vitest)
npm test

# Start the server
npm start

Building from Source

git clone https://github.com/liauw-media/construct3-mcp.git
cd construct3-mcp
npm install
npm run build

Contributing

We welcome contributions! Here's how to get started:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes
  4. Build and test: npm run build && npm start
  5. Commit your changes: git commit -m 'Add amazing feature'
  6. Push to your branch: git push origin feature/amazing-feature
  7. Open a Pull Request

Roadmap

Phase 1: Foundation ✅

  • Read-only project access
  • 7 resources, 9 query tools, 6 prompts
  • Project structure parsing
  • Official documentation access

Phase 2: Enhanced Analysis ✅

  • Event sheet flow visualization (Mermaid diagrams)
  • Object dependency graph
  • Performance analysis tools
  • Asset usage tracking
  • Orphaned object detection
  • Function mapping across event sheets

Phase 3: Safe Modifications ✅

  • Object creation with proper SID/UID management
  • Instance variable and behavior management
  • Event sheet creation and event insertion
  • Layout creation and instance placement
  • Project metadata updates
  • Automatic backup, validation, and verification
  • Reference checking before deletion
  • Addon auto-registration for known plugins

Phase 4: Event Blocks & Animation ✅

  • Event block creation (conditions + actions) with group path targeting
  • Script action support (inline JavaScript)
  • Animation management (add/update animations on Sprites)
  • Object class validation against project entities

Phase 5: Event & Layout Operations ✅

  • Delete events from sheets by SID or include name (dry-run, force, checks for references to the functions and event variables it removes)
  • Update existing event blocks (modify/add/remove conditions and actions)
  • Delete layouts (with reference checking, startup layout protection)
  • Update layout properties (event sheet binding, dimensions)
  • Full instance property overrides (angle, color, instanceVariables, behaviors, tags, etc.)
  • 278 tests, type-safe templates, domain-split tool modules

Phase 6: Runtime Control ✅

  • Injectable runtime bridge (runOnStartup, command queue, tick processing)
  • Bridge commands: callFunction, get/setGlobalVar, getObjectState, evaluateExpression, etc.
  • Project cloning with bridge injection
  • Export-for-preview pre-flight checks (worker mode, bridge registration)
  • Bridge eval script generation (browser-console lines, and a Python snippet that prints them)
  • Game connection over CDP (page or worker), bridge calls, conditions, event buffers, input, screenshots, preview server (issues #8 to #13)

M1 Primitive Surface ✅ (v1.8)

  • Layers, instance updates and instance removal
  • Families (create, update members and variables, delete)
  • Animation frames (add, update, delete, replace image) and animation rename/delete
  • Timelines (create, update, delete, list, details)
  • Addon registry tools (list_addons, register_addon, unregister_addon)
  • Project integrity validation and event group settings
  • .c3p packing (pack_project) and an end-to-end acceptance test

Editor Fidelity ✅ (v1.9)

  • Editor load-time checks in validate_project and before event sheet writes
  • Event shapes, names, image file names and instance behavior entries as the editor writes them, with repair tools for older sheets
  • Event locator and outline by editor event number, runtime trap analysis and curated pitfalls
  • Reference-checked deletes, ambiguous-SID protection and nested sub-layers in every layout tool
  • Byte-faithful writes (line endings, BOM, file-name case)

Phase 7: Advanced Features

  • Opening .c3p (zipped) projects directly
  • Rename with reference updates (dry-run preview)
  • Bulk operations
  • Plugin development assistance

Known Limitations

  • Folder Format Only: Works with .c3proj folder projects; pack_project can write a .c3p, but .c3p files cannot be opened
  • Editor Holds the Project in Memory: Close and reopen the project in Construct 3 after MCP edits and before saving there, or the editor can overwrite them
  • No Rename Refactoring: Objects, families, event sheets, layouts and timelines cannot be renamed; renaming an event variable (update_event_variable) does not update the events that use the old name, and renaming a layer (update_layer) updates only the "layer" parameters that are exactly the quoted old name (not while another layout has a layer of that name) and lists other strings that name it in a warning (issue #38). rename_animation renames the frame images and the layout instances' start animation, but not text in events. Renaming with reference updates is planned (Phase 7)
  • Runtime Testing Needs an Export or the Editor's Preview: Only the Construct editor exports or previews a project, so the runtime tools start from an exported folder (serve_preview) or a preview in a browser with a debugging port; they drive Chromium-based browsers (Chrome, Edge) only
  • No ACE Validation: Event block conditions/actions are not validated against plugin schemas (the AI caller is expected to know valid ACE IDs). Only the editor load-time rules listed under validate_project are checked; triggers are recognised by the on- id convention, which third-party addons do not always follow, so their trigger problems are warnings only. OR blocks are created with isOrBlock

License

MIT License - see LICENSE file for details

Authors

Contributors

  • Initial development and architecture

Acknowledgments

  • Anthropic - For creating the Model Context Protocol
  • Scirra - For Construct 3 game engine
  • The MCP Community - For inspiration and examples

Support


Made with care for the Construct 3 community

Back to top

↑↓ navigate openesc close