romdev-mcp 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +12 -1
- package/package.json +1 -1
- package/src/mcp/tools/playtest.js +20 -32
package/AGENTS.md
CHANGED
|
@@ -8,6 +8,17 @@ Drives the full homebrew ROM dev loop for retro game platforms (NES, Game Boy, S
|
|
|
8
8
|
|
|
9
9
|
You drive the work. The human is a director — they may want a game, a ROM disassembly, a tool-assisted reverse-engineering session, or anything else this server can do.
|
|
10
10
|
|
|
11
|
+
## The compilers and emulators are bundled — don't install or download those
|
|
12
|
+
|
|
13
|
+
Every **compiler, assembler, linker, and emulator core** you need to build and run ROMs is **already bundled** as WebAssembly inside romdev and runs through these MCP tools. So do **not**:
|
|
14
|
+
- `apt`/`brew`/`npm install` a compiler (cc65, sdcc, gcc, tcc, wla, rgbds, vasm, m68k-gcc, …) — they're built in.
|
|
15
|
+
- fetch a devkit/SDK (devkitPro, SGDK, PVSnesLib, libtonc/libgba, cc65 libs, …) — their source is bundled and the toolchains link against it for you.
|
|
16
|
+
- look for a system emulator — the libretro cores (fceumm, snes9x, gpgx, gambatte, mGBA, …) run in-process.
|
|
17
|
+
|
|
18
|
+
**Build/run/inspect happens through the romdev tools** (`buildSource`, `runSource`, `buildProject`, `loadMedia`, `screenshot`, `inspect*`, …) — not by shelling out to a system `gcc` or a downloaded toolchain. If you catch yourself reaching for a compiler/emulator install, stop and call the equivalent romdev tool. `listPlatforms` / `listToolchains` show what's available.
|
|
19
|
+
|
|
20
|
+
This is scoped to the build/run toolchain — romdev is **not** an everything box. A user may well want **external art and music tools** (Aseprite/LibreSprite for sprites, a tracker like FamiStudio/Deflemask for music, ImageMagick for conversions). Those are legitimately the user's to install, and several romdev asset-loader tools (`loadAsepriteSheet`, `loadSpriteSheet`, `loadGifAnimation`, …) are designed to consume their output. Don't refuse or reinvent those — if the user wants to paint or compose in a real editor, that's expected; romdev imports the result.
|
|
21
|
+
|
|
11
22
|
## If a human is watching, open playtest early
|
|
12
23
|
|
|
13
24
|
If a human is sitting next to you during this session — and that's most sessions in practice — open the playtest window as soon as your first build succeeds. `playtest()` opens a native SDL window that runs your ROM live and accepts USB gamepads (hot-plugged controllers are picked up automatically). It returns **immediately** — the render loop runs in the background, so you keep calling other tools while the human plays. Every other MCP tool keeps working against that same running ROM, and **`runSource`/`loadMedia` rebuilds update the window in place** — the window follows your latest build, no relaunch and no crash on rebuild. A human sitting next to you should be **playing the game** while you iterate, not watching screenshots scroll past.
|
|
@@ -21,7 +32,7 @@ After that, keep iterating with `runSource` / `buildSource` / readMemory / scree
|
|
|
21
32
|
|
|
22
33
|
**No gamepad?** `playtest()`'s response includes a `keyboardControls` map and a `tellUser` note when no controller is detected — relay the keys to the human (arrows = D-pad, Z = main action, Enter = START, ESC closes) so they know how to play.
|
|
23
34
|
|
|
24
|
-
Skip playtest only when there's clearly no human in the loop: CI runs, automated test suites, batch reverse-engineering, or when the user has explicitly said "headless." `playtest()` needs a desktop
|
|
35
|
+
Skip playtest only when there's clearly no human in the loop: CI runs, automated test suites, batch reverse-engineering, or when the user has explicitly said "headless." `playtest()` needs a desktop session to draw into; if it can't open a window it returns `{opened:false, reason:"sdl-error", message}` explaining how to get one (usually: run the server yourself in a terminal inside your desktop session via `npx romdev-mcp`, then connect your agent) — every other tool (build, run, screenshot, inspect) is fully headless and unaffected. When in doubt, ask once, then default to opening it.
|
|
25
36
|
|
|
26
37
|
## Tool surface: everything is loaded — just call the tool
|
|
27
38
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "romdev-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "MCP server giving coding agents full control of homebrew ROM development across retro platforms (NES, SNES, GB, Genesis, Atari, C64, ...) via WASM libretro cores.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/mcp/server.js",
|
|
@@ -75,31 +75,12 @@ export function registerPlaytestTools(server, z, sessionKey) {
|
|
|
75
75
|
aspect: z.enum(["fb", "tv", "core"]).default("fb").describe("Initial window shape. 'fb' (default) opens at raw framebuffer * scale — square pixels, exact dev-time geometry. 'tv' = 'how a player saw the hardware': 4:3 for consoles (NES/SNES/Genesis/SMS/Atari/C64); native LCD aspect for handhelds (GB/GBC 10:9 = 160×144 NOT stretched; GG ~6:5; Lynx 4:3; GBA 3:2). 'core' honors the core's reported display_aspect_ratio (the framebuffer geometric ratio, often non-4:3 — Genesis H40 reports ~10:7). The user can resize; letterbox preserves the chosen aspect. NOTE: 'tv' looks up the platform from the running host, so always pass the correct `platform` arg to loadMedia (`platform:\"gbc\"` not `\"gb\"` for a CGB game) or 'tv' falls back to the framebuffer aspect."),
|
|
76
76
|
},
|
|
77
77
|
safeTool(async ({ scale, title, aspect }) => {
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// it
|
|
83
|
-
//
|
|
84
|
-
// getHost ran first). The display is what's missing, not the ROM.
|
|
85
|
-
if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
|
|
86
|
-
return jsonContent({
|
|
87
|
-
opened: false,
|
|
88
|
-
reason: "no-display",
|
|
89
|
-
message:
|
|
90
|
-
"Can't open a playtest window: the server process has no DISPLAY or " +
|
|
91
|
-
"WAYLAND_DISPLAY set, so there's no desktop to draw on. This usually " +
|
|
92
|
-
"means the server was started from a shell/tmux/ssh session that " +
|
|
93
|
-
"predates the desktop login. Restart the server with the display env, " +
|
|
94
|
-
"e.g.: DISPLAY=:0 WAYLAND_DISPLAY=wayland-0 " +
|
|
95
|
-
"XAUTHORITY=$XDG_RUNTIME_DIR/.mutter-Xwaylandauth.* node src/mcp/server.js " +
|
|
96
|
-
"(discover live values via: tr '\\0' '\\n' < /proc/<desktop-pid>/environ | " +
|
|
97
|
-
"grep -E '^(DISPLAY|WAYLAND_DISPLAY|XAUTHORITY)='). Every headless tool " +
|
|
98
|
-
"(screenshot, runSource, readMemory, ...) still works — only the live " +
|
|
99
|
-
"window needs a display.",
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
78
|
+
// No preflight display checks. We just attempt to open the SDL window and
|
|
79
|
+
// report whatever SDL says — env-var guessing (DISPLAY/WAYLAND_DISPLAY)
|
|
80
|
+
// is Linux-only and wrong on macOS/Windows, where those vars are never
|
|
81
|
+
// set even with a full GUI session. SDL's createWindow already knows
|
|
82
|
+
// whether it can draw on any platform; the try/catch below surfaces the
|
|
83
|
+
// real error.
|
|
103
84
|
const host = getHost(sessionKey);
|
|
104
85
|
const loadedMediaPath = host.status?.mediaPath ?? null;
|
|
105
86
|
if (reconcileSession()) {
|
|
@@ -130,17 +111,24 @@ export function registerPlaytestTools(server, z, sessionKey) {
|
|
|
130
111
|
aspect,
|
|
131
112
|
});
|
|
132
113
|
} catch (e) {
|
|
133
|
-
//
|
|
134
|
-
// the
|
|
114
|
+
// SDL couldn't open a window — report what it said. We don't guess at
|
|
115
|
+
// the cause (no display / no GUI session / driver issue differ per OS
|
|
116
|
+
// and SDL already knows); we just relay the error plus the one fix that
|
|
117
|
+
// works everywhere, and remind the agent the headless tools are fine.
|
|
135
118
|
return jsonContent({
|
|
136
119
|
opened: false,
|
|
137
120
|
reason: "sdl-error",
|
|
121
|
+
platform: process.platform,
|
|
138
122
|
message:
|
|
139
|
-
"
|
|
140
|
-
".
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
123
|
+
"Couldn't open the SDL playtest window: " + (e?.message ?? String(e)) +
|
|
124
|
+
". This typically happens when the server runs as an MCP subprocess " +
|
|
125
|
+
"(spawned by your agent host) with no access to the logged-in desktop " +
|
|
126
|
+
"session. The reliable fix on any OS: run the server yourself in a " +
|
|
127
|
+
"terminal inside your desktop session (`npx romdev-mcp`), then connect " +
|
|
128
|
+
"your agent to it. Every headless tool (screenshot / runSource / " +
|
|
129
|
+
"readMemory / stepFrames / pressButton) still works against the live " +
|
|
130
|
+
"ROM — only the interactive window needs a desktop. You can also open " +
|
|
131
|
+
"the built ROM in any standalone emulator.",
|
|
144
132
|
loadedMediaPath,
|
|
145
133
|
});
|
|
146
134
|
}
|