fceux-mcp has 22 well-named tools with consistent verb_noun patterns (emu_*, memory_*, joypad_*, etc.). All tools have descriptions (10 - 200 chars, within baseline 34 - 392 range). Input schemas are present and typed for all tools. However, parameter descriptions are sparse or missing for many tools, e.g., memory_readword has 'address' and 'address_high' but no explanation of what 'address_high' does or when to use it. Output schemas are not documented; LLMs cannot predict return types. Error handling is minimal, BridgeError exists but tool-level recovery guidance is absent. No tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite clear risk classifications in the metadata. Composition is sound: tools are single-purpose and chainable (e.g., emu_loadrom → emu_framecount). Security: no secrets in params (good), but no audit logging or permission gates.
Read CPU register state (PC, SP, A, X, Y, P, cycles).
Current NES frame counter.
Load a different ROM. Path is resolved relative to bridge.lua or as absolute. Note: if the path can't be loaded, FCEUX silently falls back to the most-recent ROM; the returned `filename` is what's actually loaded so the caller can detect that case.
Display a message in FCEUX's HUD overlay.
Set the bridge-level pause flag — frames stop advancing until emu_unpause.
Return the current bridge-level pause state.
Power-cycle the NES (hard reset). Frame counter resets to 0.
Output schemas not documented. LLMs cannot predict return types (e.g., does cpu_getstate return a dict with PC, SP, A, X, Y, P, cycles fields?). Forces agents to guess or make exploratory calls.
Parameter descriptions incomplete or missing context. memory_readword has 'address_high' but no explanation of when/why to use it vs address alone. joypad_set describes buttons as dict but doesn't list valid button names in the description (only in the docstring).
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Metadata shows risk classifications (READ_ONLY, WRITE, REVERSIBLE) but these are not exposed to the LLM via MCP tool annotations. Agents cannot infer safety properties.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 57 | 2026-07-28+ | v2 |
Soft-reset the NES (equivalent to the console's reset button).
Advance the emulator by N frames synchronously. Returns the new framecount.
Clear the bridge-level pause flag — frames advance at NTSC ~60 Hz.
Capture the current emulated screen as PNG. Note: advances the timeline by one frame (FCEUX defers screenshot writes until the next frame render).
Read controller state for the given player (1–4).
Force controller buttons. `buttons` is a dict mapping NES button names ("A", "B", "up", "down", "left", "right", "start", "select") to True/False.
Read one unsigned byte from CPU RAM at the given address (0x0000–0xFFFF).
Read `length` unsigned bytes from CPU RAM starting at `address`.
Read a 16-bit word from CPU RAM.
Write one byte to CPU RAM at the given address.
Round-trip ping to the FCEUX bridge.
Return the path of the currently loaded ROM.
Return the SHA1 hash of the currently loaded ROM.
Restore a previously saved state. With slot=1-10 loads from that session slot (reusable). Without slot loads from the single-use anonymous state. Errors if the slot was never saved or the anonymous state was already consumed.
Save current emulator state. With slot=1-10 saves into a session slot that's reusable across many loads (in-memory; cleared when FCEUX exits). Without slot saves into a single-use anonymous in-memory state — note these are single-use: FCEUX deletes the state on load, so you must save again before each anonymous load. For repeated rewinds to the same point, use a slot.
Error handling lacks recovery guidance. BridgeError exists but tools do not return actionable error messages. E.g., if emu_loadrom fails, the error should suggest 'ROM not found. Check the path or call rom_getfilename() to see the currently loaded ROM.'
No audit logging or permission gates. Tools modify emulator state (emu_step, memory_writebyte, emu_poweron) without logging who called them or when. No permission checks to prevent unauthorized state changes.