Skip to content
Moku Labs
@moku-labs/editor pages

Claude Code

moku-editor mcp: a stdio MCP server that lets Claude read the game, run its commands, take screenshots and edit files.

On this page

How it works

moku-editor mcp is a stdio MCP server. Claude Code starts it. It uses the running editor, or starts one. It gives Claude 22 generic moku_* tools, and on top of them one tool per command door of the connected game.

Register it

  1. Add the server from the game folder. A moku-game folder needs no HTML file.

    claude mcp add moku-editor -- bunx moku-editor mcp

    A game with its own page names the file and the port: claude mcp add moku-editor -- bunx moku-editor mcp web/index.html --port 3000.

  2. Or commit a project .mcp.json. bunx moku-editor mcp-config web/index.html --port 3000 prints it.

    .mcp.json
    {
      "mcpServers": {
        "moku-editor": {
          "type": "stdio",
          "command": "bunx",
          "args": ["moku-editor", "mcp", "web/index.html", "--port", "3000"]
        }
      }
    }
  3. Ignore the editor's files. Add .moku/ to .gitignore. The bin writes .moku/editor.json with its port and the per-start token. A bin the bridge starts logs to .moku/editor.log.

  4. Keep the game page visible. Open the game or the tools page in a browser, or in Claude's browser pane. A paused game or a hidden tab answers "game paused or hidden at frame N", not a timeout.

claude mcp list then shows moku-editor as connected.

Which editor the bridge uses

  • A running bin wins. The bridge finds it through .moku/editor.json under --root, by default the working directory.
  • No bin runs. The bridge starts moku-editor <html> --port <port> itself. In a moku-game folder it starts the engine page.
  • Claude Code closes. The bridge stops the bin it started. A bin you started yourself keeps running.

The tools

Call moku_status first. Every game tool takes an optional session.

ToolWhat it does
moku_statusIs the editor running, did the bridge start it, URLs, hot reload, sessions.
moku_sessionsThe connected games, each with heartbeat { frame, paused, silent }.
moku_manifestThe game's sources and commands: ids, inputs, effects.
moku_readReads a source, such as game.position.
moku_waitWaits until a source equals a value, leaves a value, or changes. At most 25 s.
moku_runRuns a command, such as game.pause. The answer starts with its effect.
moku_screenshotA JPEG of the game, at most 1080 px wide by default. key crops it to one ui element; format: "png" keeps it lossless.
moku_seriesA contact sheet: 2 to 12 frames, everyMs of game time apart, on one JPEG.
moku_referenceThe newest or a named reference card with its crop.
moku_selectionWhat is selected in the tools page: key, name, type, file:line, rect, card, crop, the reference line and the pending style edits.
moku_selectSelects an element by key, an area by rect, or a flow node by node. Needs the tools page open.
moku_deviceReads or sets the device of the tools page: preset, orientation, folded. Needs the tools page open.
moku_showSwitches the tools page to flow, game, render, state, files or console. Needs the tools page open.
moku_files_listLists project files.
moku_files_readReads a file and its version.
moku_files_writeWrites a file, with an optional version check.
moku_files_findWhere a key of the project index lives, such as node:board/merge or jsx:coins.
moku_projectThe project index: key counts per kind, the keys of a prefix, or what one file defines and uses.
moku_reloadReloads the game page and restores its state.
moku_hot_reloadSwitches Bun hot reload on or off. The server restarts; the call returns once it is back. Call moku_reload after it.
moku_startStarts the editor when it does not run.
moku_stopStops the editor, only when the bridge started it.

The file tools stay in the files sandbox. They never show or touch .moku/editor.json or .moku/editor.log. The bridge never prints the token.

Door tools

After the generic tools, the list holds one tool per command door of the selected session. Each has a typed input schema, so Claude does not guess the input of moku_run. Sources stay behind moku_read.

RuleWhat it means
NameThe door id with . as _: game.tap is game_tap. Cheat and raw doors carry the effect first: cheat_game_fill, raw_game_restore.
InputOne property per door field (string, number, boolean, json). Fields without ? are required. _session picks another session.
AnswerThe same as moku_run: effect: <effect>, then { value, frame, state }.
SessionThe one moku_run uses without session: the only one, else the embedded one. Several sessions and none embedded: no door tools.
ChangeThe bridge rebuilds the list, and sends notifications/tools/list_changed, only when the hash of the game's commands moves. A hot reload of the same game keeps the list.

Screenshots go only through moku_screenshot, so game.capture and the editor.* capture commands get no door tool. A name longer than 40 characters or one that starts with moku_ gets none either. moku_run still runs every door.

Deny the cheat and raw doors in the Claude Code settings:

{ "permissions": { "deny": ["mcp__moku-editor__cheat_*", "mcp__moku-editor__raw_*"] } }

Pick an element, fix it in code

The tools page publishes what is selected. Claude reads it with moku_selection.

  • pick in Game
  • reference line
  • moku_selection
  • edit the source
  • hot reload

A pick with the element picker copies one reference line for the chat and writes a card file next to the captures. Style changes you make in the Element tab stay pending: the editor does not write the file. They reach Claude in moku_selection as edits, each { path, from, to }. A text style edit has the path textStyle:<key>#<field>. An edit leaves by itself once the game shows the new value.

moku_select { key } or moku_select { rect } selects from the chat. The tools page picks it like a click, without the clipboard, and answers the selection.

  • Screenshots need the page on screen. A hidden tab answers with the frame it stopped at.
  • moku_select, moku_device and moku_show need the tools page open. The game page alone does not answer them.
  • A client that ignores list_changed keeps old door tools. A door that is gone answers isError. moku_run always works.
  • moku_hot_reload restarts the server. Every socket drops for about a second. Call moku_reload after it.