MCP tools¶
Start a local MCP server for one game:
redgem mcp /path/to/game --read-only
Configure your MCP client to launch the redgem executable with those arguments. It reads
newline-delimited JSON-RPC from standard input and writes only protocol responses to standard
output. Native library output goes to standard error. Closing input stops the server.
By default it opens no listening socket and works on files. Add --editor to connect to an
editor that explicitly enabled its local bridge (see below).
The server supports the MCP 2026-07-28 per-request metadata protocol and the 2025-11-25,
2025-06-18 and 2025-03-26 initialization handshakes (a client asking for one of them gets it). Modern clients can discover capabilities with server/discover and
must supply their protocol version and client capabilities in each request's params._meta.
Legacy clients send initialize, then notifications/initialized, before using tools.
Tools and permissions¶
| Tool | What it does |
|---|---|
scene_list |
Lists objects, taking optional scene, limit (default 50, 0 for all) and depth. |
scene_get |
Reads one object by persistent #id, unique name or hierarchy path. |
scene_edit |
Takes action (set, add, remove), object and optional assignments (key=value strings), from, parent, scene. Removal requires confirm: true. Saves the scene file. |
scene_diff |
Compares with an earlier scene file named by against, inside the game. |
game_run |
Plays headlessly with frames (default 60, maximum 600), seed, input, assert, until, print, every, scene. |
game_check |
Checks scripts, scenes and assets; does not run the game's test files. |
game_shot |
Returns a PNG picture of the saved game at at seconds (default 1), width pixels wide (default 512, 16:9), optionally through another camera. |
game_query |
Asks the playing game: inspect an object, raycast from one point to another, or overlap a sphere at a point with a radius. |
api |
Reads the API index, a class, or a search. |
events |
Reads this server's log history with since, limit (default 20, maximum 32), level. |
game_run expressions run in the game after each frame and can use anything scripts can, for
example print: "Scene.find('Player').transform.position" or
assert: "Physics.raycast(Vector3.new(0, 5, 0), Vector3.down)".
game_query answers in short lines with rounded numbers. inspect gives the object's world
position and rotation, its body, velocity and whether a character is grounded, and only those
props that scripts changed since the scene loaded. Points are x,y,z strings in meters. Without
an editor in Play, it plays the saved game for frames (default 60) first, as game_run does:
{"action": "inspect", "object": "Player"}
{"action": "raycast", "from": "0,5,0", "to": "0,-5,0"}
{"action": "overlap", "at": "4,1,-3", "radius": 1.5}
after 60 frames: #5 Player pos=0,0.8,0 body=character vel=0,-0.33,0 grounded
after 60 frames: 2 hits, nearest first:
#5 Player at 0,1.62,0 normal=0,1,0 dist=3.38
#0 Ground at 0,0,0 normal=0,1,0 dist=5
game_shot draws in a separate redgem shot process with a hidden window. A graphics driver
that crashes takes only that process down, and the call reports it. It needs a display. Without one (no DISPLAY on Linux)
it returns a tool error that says so, and the server keeps answering. Some MCP clients start
servers with only a few environment variables; pass DISPLAY in the server's configuration
if pictures are wanted. Prefer game_run to find out whether something works and use pictures
for how it looks.
Invalid arguments come back as tool errors (isError: true) naming the argument and what it
accepts, such as frames: must be at most 600, not 601, so the model can correct the call.
Unknown or disabled tools remain JSON-RPC errors.
--read-only exposes only scene_list, scene_get, scene_diff, api and events; the
game_* tools execute the game's scripts and are disabled. It also
rejects calls to disabled tools, even if the client remembers an earlier tool list. For a smaller
surface, add an exact comma-separated allow list:
redgem mcp /path/to/game --read-only --allow scene_list,scene_get,api,events
Without --read-only, the tools can save edits and execute the configured game's Ruby scripts.
Run only games you trust. A run expression can mutate state or use networking; read-only mode
disables execution entirely. The configured game directory stays fixed for the life of the
process. Input replay and comparison files must be regular files inside it, without symbolic
links. These restrictions protect tooling paths; they do not sandbox native game code.
Without --editor, edits go through the file-based scene commands. Close the editor while
editing its files this way. Keep an earlier save and inspect scene_diff after an edit.
from is available only in file mode; parent also works when adding a live empty object.
With --editor auto, check editor status to know whether calls reach an editor.
Live editor¶
Start the editor with its bridge explicitly enabled:
redgem /path/to/game --editor-bridge
The terminal prints the path of a private connection.json in the user's RedGem data folder.
Let the MCP server find it on every call, so one client configuration survives editor restarts:
redgem mcp /path/to/game --editor auto
With auto, each call uses the newest running editor of that game. When none answers, the
scene tools, game_query and events work on the saved files and this server instead, and
their answer starts with (no editor is running: answered from the saved files); the editor
tool returns an error saying how to start an editor with its bridge. A connection file whose
port refuses connections belongs to an editor that is gone and is deleted. A listener that does
not prove itself (below) is skipped for a minute, so it delays at most one call a minute by about
a second. To pin one editor instance, pass its file:
redgem mcp /path/to/game --editor /path/to/connection.json
If that editor exits, calls fail with an error that says how to restart it. A pinned editor
always answers scene edits live, so its scene_edit schema leaves out from.
With auto, an events cursor belongs to whichever process answered: the editor, or this server
when no editor is open. After an editor opens or closes, start again from since: 0.
Scene listing, object details and scene comparison now read the editor's unsaved document.
scene_edit supports set, add and remove. add creates an empty object, optionally under
parent, with initial assignments; attach scripts and properties as with the CLI. from
(asset-backed duplication) remains file-only. remove deletes a subtree and requires
confirm: true. Every call is one undo step: invalid assignments or exceptions roll it back
without losing earlier undo/redo. Changes update the viewport and remain unsaved until save.
The editor tool takes these actions:
| Action | Result |
|---|---|
status |
Scene, play state, dirty flag, selection, undo/redo and external-conflict state. |
select |
Select the requested object. |
frame |
Point the editor camera at object (selecting it), or at the selection. |
undo, redo, save |
Apply the corresponding editor command. Saving is explicit. |
play, pause, resume, stop |
Control the editor's game, using its normal input, audio and viewport. |
step |
Advance a paused game by exactly one fixed frame, keeping it paused. |
capture |
Return the last completed viewport as an MCP PNG image, without writing a file. |
capture accepts max_size (default 512, range 128–1024), the longest output side. Images are
reduced further when necessary for the reply, with the PNG as base64 text, to stay within the
bridge's 256 KiB limit (about 188 KiB of PNG). Only the scene
viewport is captured, never the desktop or other applications. object is accepted only by
select and frame; max_size only by capture. Read-only mode permits status and capture only.
Edits and document commands are refused while playing, during gestures/dialogs, or with an
external file conflict pending. Play starts a copy of the unsaved scene; Stop discards that
runtime and restores the edited document. Pause/resume/stop/step control that runtime, not the
file. Read operations still describe the edited document, not runtime-mutated objects. An
explicit scene must match the open scene; requests never switch or discard scenes.
events reads the editor's log history. game_query asks the game the editor is playing (the
runtime, not the edited document); it refuses frames there, so pause and step to advance.
While editing, it reports that the editor is not playing; it does not fall back to the saved
files, which may differ from the open document. api, game_run, game_check and
game_shot remain ordinary CLI tools: they read saved files rather than the unsaved editor scene.
Arbitrary live eval is not exposed.
The bridge binds only to IPv4 loopback and requires a random credential on every request, with the game directory checked at both ends. Its connection file stays outside the repository and is removed on clean shutdown. On POSIX it is owner-only inside an owner-only directory; on Windows it inherits the user's profile access controls. Treat the file as a credential granting control of that editor. MCP read-only and allow-list options restrict that client's calls.
At most four connections are handled at once, on the editor thread between frames. Requests
are limited to 64 KiB and replies to 256 KiB. Transport waits expire after five seconds. If an
edit times out, it may have completed: inspect status and the object before retrying. A pinned
connection (--editor connection.json) never falls back to the files.
Before it sends the credential or a request, the client sends a random nonce, and the editor must answer within one second with an HMAC-SHA256 of it keyed by the credential. A program that took a crashed editor's port therefore receives only that nonce. Until that proof, a failure means no editor took the call; after it, any failure (a timeout, or a reset after the request went out) fails the call. It is never retried elsewhere or applied to the files, because the edit may already have happened.
Bounded results¶
Each request is at most 64 KiB with at most 16 JSON nesting levels. Ordinary tool text is limited
to 8 KiB with a notice saying it was cut; narrow the scene list (depth, limit, then
scene_get) or the API query for more detail. Invalid arguments and a valid command that fails
both return isError: true, with what to change or the engine's diagnostic. Unknown or disabled
tools are JSON-RPC errors.
Event text starts with cursor=N, the value to pass as the next since (followed by more,
dropped or cursor_ahead when they apply), then one line per event, #id level text:
cursor=2
#2 error scripts/coin.rb:12: Coin#update on 'Coin1': undefined method 'positon=' for Transform (NoMethodError)
Each event is its message's first line, led by the place in the game's files its backtrace names,
with the game's folder left out of paths. Each event's text is capped at 256 UTF-8 bytes and ends
with ... when shortened. The cursor belongs to this server process; it is not a cursor into an
unrelated editor or game (with --editor, it belongs to that editor).
Events include engine log messages, not raw
standard output. Process history is bounded, so dropped identifies overwritten messages.
Requests run one at a time. A cancellation notification cannot interrupt a native command already running; frame limits and the game's script budget bound headless work. A slow native operation can still delay completion. Clients can terminate and restart a server that stops responding.
The protocol follows the official stdio binding and tool messages.