GameMaker MCP Server
Provides a real connection to GameMaker, enabling project editing, resource creation, GML function checking, compilation, running the game, and live interaction through an in-game bridge for variable access, function calls, and screenshots.
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., "@GameMaker MCP ServerCreate a script that logs hello, check it for errors, and compile the project."
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.
GameMaker MCP Server
Give an AI agent a real connection to GameMaker — faithful project editing, a compile loop, and a live link into the running game.
Agents writing GameMaker code today work blind. They invent GML functions that
do not exist, cannot tell whether the code compiles, cannot see what the game
looks like, and cannot safely touch the .yy files that define rooms, objects
and sprites. This fixes all four.
|
|
Both of those are games the server built — sprites, objects, events, rooms and all — then compiled, launched, played and photographed. No imported assets, no hand-edited files.
Contents
Related MCP server: Forge Engine MCP Server
Quick start
1. What you need
Node 20 or newer
GameMaker, with at least one runtime installed. The server reads the GML function database from your runtime and drives GameMaker's own compiler, so it matches the version you actually build with.
A GameMaker project to work on. Any project with a
.yypwill do.An MCP-capable LLM client — Claude Code, Claude Desktop, or anything else that speaks MCP over stdio.
2. Install the server
git clone https://github.com/jhalek90/Gamemaker-MCP-Server
cd Gamemaker-MCP-Server
npm install
npm run build3. Check it can see your project
Before wiring it into anything, make sure it opens your project:
node dist/mcp/cli.js --project "C:/path/to/YourGame"It should print something like this to stderr and then sit waiting for MCP traffic — that is correct, it is a stdio server:
gml-mcp: YourGame (63 resources)Press Ctrl+C to stop. If it cannot find the project it will say so and exit.
The project path can also come from the GML_MCP_PROJECT environment variable,
or it defaults to the current directory.
4. Connect it to your LLM
Claude Code — one command:
claude mcp add gml -- node "C:/path/to/Gamemaker-MCP-Server/dist/mcp/cli.js" --project "C:/path/to/YourGame"Claude Desktop — add this to claude_desktop_config.json
(%APPDATA%\Claude\ on Windows, ~/Library/Application Support/Claude/ on
macOS):
{
"mcpServers": {
"gml": {
"command": "node",
"args": [
"C:/path/to/Gamemaker-MCP-Server/dist/mcp/cli.js",
"--project",
"C:/path/to/YourGame"
]
}
}
}Any other MCP client — it is a standard stdio MCP server. Command is
node, arguments are the path to dist/mcp/cli.js plus --project <your project>. Restart the client after editing its config.
5. Try it
Open a chat with your model and ask for something. A first prompt that exercises most of the server:
Use the GameMaker tools. Tell me what project I have open and how many resources it has. Then search GML for functions about collisions, create a script called
scr_hellothat logs a message, check it for invented functions, and compile the project.
You should see it call gml_project_info, gml_search, gml_create_script,
gml_check and gml_compile in turn. If the compile succeeds, everything is
wired up.
To undo whatever it just did:
Undo the last change.
Every change the server makes is a transaction with a named restore point, so
gml_undo reverses a whole operation rather than a single file write.
6. Add the live bridge
This is the part that lets the model see your game rather than just write to it. The bridge is a small set of GML resources — one object and two scripts — that the server installs into your project.
Ask your model:
Inject the GameMaker bridge, place it in my starting room, then run the game and take a screenshot.
Which does this:
gml_bridge { action: "inject" } adds obj_gmlmcp_bridge + 2 scripts
gml_add_room_instance { room: "rm_main", object: "obj_gmlmcp_bridge" } so it starts with the game
gml_run compiles, launches, connects
gml_screenshot an image the model can actually look atThe bridge must be in whichever room your game starts in, or the server has nothing to connect to. Once it is running, the model can read and write variables, call functions, count instances, adjust tunables live and take more screenshots.
When you are done:
Eject the GameMaker bridge.
Ejecting restores your project to exactly the bytes it had before.
Working with the IDE open
The intended workflow is to keep GameMaker open while the agent works. GameMaker notices changes on disk and offers to reload.
Reload before you save. If the IDE has been open while the agent added resources, saving from a stale in-memory project will drop them. Accept the reload prompt first.
What it does
Knows the real GML API. 2,354 functions read from your installed runtime's
GmlSpec.xml— search, signatures, and a checker that flags calls to functions that exist nowhere.Edits projects without corrupting them. A byte-splicing
.yydocument model that preserves formatting exactly, validated against 35,917 real files.Every change is a transaction with a named restore point and unlimited undo, backed by a shadow git repo that never touches yours.
Creates resources — objects, scripts, sprites drawn from primitives, folders, rooms with cameras, and instances placed in them.
Compiles with GameMaker's own toolchain and reports errors at file and line.
Talks to the running game — read and write variables, call functions, count instances, adjust tunables live, and take screenshots.
Runs deterministic behaviour tests against that live game.
See it work
Two complete games, built end to end through the server.
A platformer
80×12 tiles, 13 sprites, 15 objects, 211 instances. Coins, patrolling enemies you stomp, pits, bonus blocks, a mushroom that makes you big, bricks only big Mario can smash, and a flagpole. Coyote time and a jump buffer, because a platformer that ignores your input feels broken.
An autopilot plays it start to finish over the bridge — 11 coins, 4600 points, reaches the flag — and eight declarative specs cover the mechanics.
npx tsx tools/platformer/build.mts --compile # build it
npx tsx tools/platformer/specs.mts # 8 behaviour specs
npx tsx tools/platformer/playtest.mts # autopilot, start to flagA raycaster
32×24 cells, 11 sprites, 15 objects, 292 instances. Textured DDA raycasting at 683 rays a frame and 120 fps, with distance shading, sliding doors, billboarded enemies clipped per column against the wall depths, a hitscan shotgun, a status bar and an automap.
Walls are real instances, and the collision grid is rebuilt from whatever instances exist at room start — so rearranging the level in GameMaker's room editor genuinely changes the level. A bot walks the 41-cell route from spawn to exit.
npx tsx tools/doom/build.mts --compile
npx tsx tools/doom/playtest.mtsEvery sprite is drawn from primitives
No asset pipeline, no image files in. Rectangles, ellipses, polygons and lines into a hand-written PNG encoder.
|
|
Levels are ASCII maps, checked before they are built. The raycaster map is flood-filled from the player start — 504 of 504 floor cells and all 17 entities reachable — and the platformer asserts that the run-up before every pit is clear at head height. A level that cannot be finished is worse than one that looks wrong, and neither check needs the game to run.
Tools
Tool | |
| Find GML functions by name or description |
| Full signature, parameter types and docs for one identifier |
| Report calls to functions that exist nowhere |
| Compile with GameMaker; errors located to file and line |
| Read the project |
| Add resources |
| Create a room, with a scrolling view and a place in the room order |
| Build a sprite from shapes, no image file needed |
| Write gameplay logic |
| Edit and remove |
| Install the in-game bridge and place it |
| Launch the game and connect to it |
| Capture what the game is showing, as an image to read |
| Inspect and steer the running game |
| Run deterministic tests against the running game |
| Step back through changes |
The live bridge
This is what turns a code generator into something that can see what it made.
agent ──MCP──▶ server ──TCP 5959──▶ obj_gmlmcp_bridge (inside your game)
│
ping · get_var · set_var · instances · call
create · destroy · goto_room · speed · seed
input · wait · tunables · screenshotThe bridge is a real GameMaker project at bridge/mcp_bridge — openable,
runnable and debuggable in the IDE, with Feather and breakpoints. Authoring it
as escaped strings inside TypeScript would have made it miserable to iterate
on, and this way the server's own gml_compile verifies the one piece of GML
it ships.
Injection copies the GML, not the .yy files. Resources are re-created
through the normal project API so they adopt your project's own conventions —
copying .yy files verbatim would push this project's GameMaker version into
yours. Ejecting restores the project byte for byte.
The protocol is newline-delimited JSON over raw TCP, game as server on port
5959. json_stringify escapes newlines inside strings, so a bare newline is an
unambiguous frame terminator with no length prefix to keep in sync across
partial reads. The greeting carries a protocol version, so a stale injected
copy is caught on connect rather than mis-parsed.
GML has no eval. The bridge cannot run arbitrary code sent over the wire,
so every capability is an explicit verb over named assets. Anything genuinely
new needs an edit and a recompile — about three seconds.
Live tunables are what make tuning practical. Game code registers values it wants adjustable, and changing them takes effect immediately. Adjust, screenshot, look, adjust again, with no recompile in the loop:
// in your game, in the Step event where the value is used
speed_max = gmlmcp_tunable("speed_max", 8);gml_tunables { set: { speed_max: 12 } }gmlmcp_tunable is the one bridge function meant to be called from your own
code. Read it where the value is used rather than once in a Create event —
returning the current value every step is what lets it change while the game
runs. The first call registers your default, which puts the name in the
registry, so an agent can ask what is adjustable instead of being told.
Screenshots are deferred to Post Draw, because screen_save captures the back
buffer and an async event would catch a partly drawn frame.
The document model
.yy and .yyp files are JSON with a dialect of their own: trailing commas,
no space after :, marker keys like $GMObject and %Name, and no trailing
newline. Parse-and-re-serialize loses all of that.
So nothing is ever re-serialized. Edits splice bytes into the original
text, leaving every byte outside the edit untouched. Measured across a corpus
of 35,917 real .yy files from 131 projects:
Byte-splice model | 0 parse failures, 0 splice failures |
Parse-and-re-serialize | 81.7% of files come back different |
Key ordering matters too. GameMaker sorts keys case-insensitively, but _
sorts above every letter — implemented as a _→| substitution before
comparison. That detail is the difference between 453 ordering violations and
11,570 across 620,224 objects.
Transactions and undo
Every write goes through a transaction. It collects the whole change set, validates it, snapshots the current state, then flushes in one burst:
const workspace = Workspace.open(projectRoot);
const tx = workspace.begin('add obj_bridge');
const car = tx.readDoc('objects/objCar/objCar.yy')!; // reads through pending writes
car.set(['persistent'], true);
tx.writeDoc('objects/objCar/objCar.yy', car);
tx.write('objects/obj_bridge/Create_0.gml', 'server = network_create_server_raw(...);');
tx.commit(); // validate -> snapshot -> apply, or roll back
workspace.undo(); // step back, repeatableBatching is correctness, not tidiness. Creating one object touches the
.yy, the .yyp resource list, .resource_order and each event .gml.
Written one at a time with the IDE open, GameMaker prompts to reload after each
and can reload a half-created resource.
Validation runs before anything lands. A .yy that does not re-parse is
rejected along with the whole change set, because a corrupt .yy stops
GameMaker loading the project at all.
Undo uses a shadow git repo at .gml-mcp/shadow.git, with the project as
its work tree. Deliberately not your repository — running git commit inside
someone's working tree while they have their own staged changes is a bad
surprise. A separate GIT_DIR costs nothing, gives unlimited undo depth, and
behaves identically whether or not the project is versioned. core.autocrlf is
forced off: 39% of corpus files are CRLF and git would otherwise rewrite them
on restore.
Rooms
createRoom(project, tx, 'rm_level1', {
width: 5120, height: 768,
background: '#5c94fc',
view: { width: 1366, height: 768 },
orderIndex: 0, // 0 makes it the room the game starts in
});
addRoomInstance(project, tx, 'rm_level1', 'obj_player', { x: 160, y: 640 });A new room gets the two layers GameMaker makes for a blank one: an instance layer at depth 0 and a background layer at depth 100.
Two prefix conventions live inside a single view object, and they do not mean the same thing:
| width — paired with |
| horizontal — paired with |
So hview is 768 on a 1366×768 view, while hborder is the slack on the
left and right. test/rooms.test.ts asserts all four with distinct values.
Room instance coordinates are floats. GameMaker writes "x":384.0 and
"imageSpeed":1.0; emitting 384 and 1 would make the IDE rewrite every
instance in the room on its next save.
Testing game behaviour
Compiling proves the code parses. The checker proves the functions exist.
Neither says whether the game works. gml_test runs deterministic tests
against the running game:
{
"name": "holding right moves the player",
"seed": 424242,
"speed": 2000,
"steps": [
{ "set": { "scope": "objPlayer", "name": "x", "value": 100 } },
{ "press": "vk_right" },
{ "wait": 60 },
{ "release": "vk_right" },
{ "expect": { "scope": "objPlayer", "name": "x", "op": ">", "value": 100 } }
]
}Tests are declarative rather than code, because an agent talks to this server
over MCP and cannot ship a function across that boundary. Steps cover seed,
speed, set, press/release, wait, call, goto, screenshot and
expect; expectations read a variable, call a function, or count instances,
and compare with ==, !=, >, >=, <, <=, contains or exists, with
an optional tolerance because physics is rarely exact.
Determinism comes from a fixed random seed, input delivered on exact
frames, and waits measured in frames rather than wall clock. Speed is the
other half: raising game_set_speed runs the simulation faster than real time,
so 120 frames that would take two seconds of play complete in about 130ms.
PASS big Mario smashes a brick with his head (48 frames, 830ms)
PASS small Mario only nudges the same brick (56 frames, 950ms)
PASS landing on a goomba flattens it (97 frames, 1634ms)
PASS a pit is fatal (64 frames, 1084ms)
PASS touching the flag wins the level (31 frames, 557ms)
8 passed, 0 failed in 9.5sAn input seam is worth building into a game you want an agent to drive. Both demos read every control as the real keyboard OR a global:
var _right = keyboard_check(vk_right) || keyboard_check(ord("D")) || global.bot_right;A driver then says exactly what is held, for exactly how long, without racing
whatever else has the keyboard. Jumps go through a one-shot request the game
consumes — bot_jump_once = 26 means jump, and hold the button 26 frames —
so a single round trip describes a whole jump. A human playing notices nothing.
The limit worth knowing
The tuning loop is fast because live tunables need no recompile. Anything structural — new logic, a changed algorithm — still costs an edit, a three-second rebuild and a relaunch, because GML cannot evaluate new code in a running game. No amount of bridge work changes that.
Compiling and checking
gml_compile drives Igor, GameMaker's own build tool, into a dedicated cache
directory so agent builds never contend with IDE builds. Compiler error line
numbers are zero-based; the diagnostics layer adds one so reported lines match
what you see in an editor.
The compiler and the checker catch different things, and you want both.
Invented function calls — plausible names, correct-looking arguments — compile
cleanly and exit 0. GameMaker resolves unknown identifiers at runtime, so a
typo becomes a crash when that line is finally reached, not a build error.
gml_check reads the same GmlSpec.xml Feather uses and flags them before the
build.
It is tuned against the corpus to keep false positives down: 3,126 → 309 across
15,952 .gml files, with 49 of 66 projects completely clean.
Grounding, not preloading
GmlSpec.xml is about a megabyte. Loading all 2,354 functions into context
would cost tokens on every request to answer a question the agent has not asked
yet. Instead the server indexes it and answers gml_search and gml_lookup on
demand, with ranked search and Levenshtein suggestions for near misses. The
spec is read from your runtime, so signatures match the version you build
with. It is never redistributed.
What was measured
Nothing here was assumed where it could be counted.
| 35,917 files, 131 projects |
Byte-splice fidelity | 0 failures, versus 81.7% drift for re-serialization |
Key sort rule | 453 violations versus 11,570, across 620,224 objects |
Event type table | derived from 4,975 objects, zero ambiguity |
Checker false positives | 3,126 → 309 across 15,952 |
GML functions indexed | 2,354 |
Test suite | 191 passing |
The event-type table came from pairing eventList entries against sibling
.gml files across every object in the corpus, which produced exactly one
prefix per type with no disagreements:
0 Create | 1 Destroy | 2 Alarm | 3 Step |
4 Collision | 5 Keyboard | 6 Mouse | 7 Other |
8 Draw | 9 KeyPress | 10 KeyRelease | 12 CleanUp |
Types 11 (Trigger, deprecated), 13 (Gesture) and 14 (PreCreate) never occurred
and are marked unverified in the source. Collision events are named after the
other object — Collision_objWall.gml — not a number.
Development
npm install
npm test
# Two opt-in suites run against real data:
GML_MCP_CORPUS=/path/to/projects npm test # format fidelity
GML_MCP_SAMPLE_PROJECT=/path/to/a/project npm test # resource ops on a real projecttest/corpus.test.ts asserts that every file parses, reproduces byte for byte,
survives a scalar replacement without disturbing another byte, and returns to
its original bytes after an insert/remove round trip.
test/integration.test.ts copies a real project and runs the resource
operations over it, checking that a create touches only the expected files and
that undo restores every byte.
The demos double as end-to-end exercises of the whole server:
npx tsx tools/platformer/build.mts --compile
npx tsx tools/platformer/specs.mts
npx tsx tools/platformer/playtest.mts
npx tsx tools/doom/build.mts --compile
npx tsx tools/doom/playtest.mtstools/*/preview.mts renders every sprite to a single sheet, so the art can be
judged without launching anything.
Layout
src/yy/ byte-splicing .yy document model
src/tx/ transactions, shadow-git undo
src/project/ .yyp graph, resources, events, sprites, rooms, symbol index
src/spec/ GmlSpec.xml knowledge base, runtime discovery
src/build/ Igor driver and diagnostics
src/bridge/ injection and the TCP client
src/testing/ deterministic test runner
src/image/ PNG encoder and rasteriser
src/mcp/ the MCP server itself
bridge/ the injectable GameMaker project
tools/ demos, corpus measurement scriptsPrior art and attribution
Nothing here vendors or depends on third-party source. The runtime dependencies
are the MCP SDK and Zod; everything else — the .yy parser, the PNG encoder,
the rasteriser, the raycaster — is written for this project.
Stitch by Butterscotch Shenanigans
(MIT, © 2023) — @bscotch/yy for project files, plus a GML parser and VS Code
extension. Read as a reference, deliberately not a dependency: its model
re-serializes .yy files where this one splices bytes. Its version-gate table
is a useful record of GameMaker's schema drift. No Stitch code is reproduced
here.
Raycasting technique — the demo in tools/doom/ uses the grid traversal
from Amanatides & Woo, A Fast Voxel Traversal Algorithm for Ray Tracing
(1987), applied to a first-person view the way Wolfenstein 3D did and as set
out in Lode Vandevenne's raycasting
tutorial. That tutorial's source is
"Copyright (c) 2004-2020 by Lode Vandevenne. All rights reserved." with no
licence grant, so none of it is copied: the GML is written from the published
technique. The credit is owed regardless.
GameMaker's GmlSpec.xml is read from your own runtime install at runtime.
It is not redistributed and no copy is checked in.
The demo games are original code and original art — every sprite is drawn
from primitives by tools/*/art.ts, with no imported asset. They are
pastiches: a platformer in the style of Super Mario Bros. and a shooter in
the style of Doom. Nintendo and id Software own those properties; this project
is not affiliated with or endorsed by either.
GameMaker is a trademark of YoYo Games. This is an independent project.
Licence
MIT — use it, fork it, ship it.
This server cannot be deployed
Maintenance
Related MCP Connectors
Build browser games on gamedev.pl from your coding agent.
Playgama MCP is a remote MCP server for game developers. Point Codex, Claude Code, Cursor or VS Code at it and your agent publishes an HTML5 game to Playgama without leaving the editor: it creates the game and fills in the form, uploads the build and covers, edits the in-app catalog and leaderboards, gets a QA Tool link, and publishes a sandbox — a public link anyone can play. 20 tools, 10 of them read-only; every write is annotated as destructive so your client asks first. Submitting to moderation, deleting and payouts are deliberately left out: the agent prepares everything, a human presses the button. Product page: https://playgama.com/mcp/ Docs: https://wiki.playgama.com/playgama/mcp Tools reference: https://wiki.playgama.com/playgama/mcp/tools Token: https://developer.playgama.com/mcp
Create, test and play AI-native games through server-authoritative contracts.
Drive real devices from your AI Coding tool. Embed a client SDK (Unity, Godot, Flutter, iOS/macOS, Android, React Native, Web) in your app, then capture screenshots, traverse the UI tree, inject taps and key events, and run automated test tasks on the physical device over a secure relay.
Related MCP Servers
- FlicenseNot gradedqualityFmaintenanceAn MCP server that parses GameMaker Studio 2 projects, providing developers and AI agents with quick access to project structure, GML code, and asset metadata.19-
- AlicenseAqualityNot gradedmaintenanceEnables AI agents to fully control the Forge Engine (Godot fork) by manipulating scene trees, modifying node properties, and managing game scripts. It allows agents to perform development tasks such as running projects, capturing screenshots, and executing GDScript through a WebSocket connection.21-
- FlicenseAqualityFmaintenanceEnables AI-assisted GameMaker project development in Cursor IDE, allowing users to create projects, add scripts, objects, and sprites using natural language.510-
- AlicenseCqualityAmaintenanceEnables AI agents to interact with the Godot game engine, including project inspection, scene/script parsing, headless exports, runtime control with live scene-tree inspection and evaluation, and API documentation search.100379 npmMIT

