Skip to main content
Glama

mcp-server-misterfpga

An MCP server for MiSTer FPGA devices: find and launch games, see what's being played and what's been played most, take screenshots, make NFC cards, change MiSTer.ini settings (globally or per core), back up saves, and start Update All, from Claude Code, Claude Desktop or, through a Cloudflare Tunnel, the Claude apps anywhere. It talks to each MiSTer through Zaparoo Core's API and, for what Zaparoo doesn't cover, SSH. One server drives several MiSTers, and a companion service publishes what each one plays to Home Assistant over MQTT (Play tracking).

A sibling of mcp-server-harmony, mcp-server-sofabaton, mcp-server-webos and mcp-server-batocera, with the same conventions, and sharing code with them through mcp-server-shared. docs/DESIGN.md is the design, the research behind it, and the decisions.

Status: verified against the simulator only. HARDWARE_VALIDATION.md is the checklist for a first session with real MiSTers.

Set up your MiSTer

1. Zaparoo Core

Zaparoo isn't on a MiSTer by default. From Zaparoo's MiSTer page:

  1. Run Update All. During its countdown, press UP to open its settings, turn on Zaparoo under Tools & Scripts, choose SAVE, and let it run.

  2. On the MiSTer, open Scripts → zaparoo and accept the prompt to start it as a service at boot.

  3. From another machine, open http://<mister-ip>:7497/app/: that's Zaparoo's web UI. Let it build its media database (that's what search_games searches).

  4. For accurate tracking of games launched from the MiSTer's own menu, Zaparoo asks for recents=1 and log_file_entry=1 in [MiSTer] of MiSTer.ini. Once SSH works (below), set_ini can do it; doctor tells you when they're off.

This server talks to Zaparoo without a key or pairing: on MiSTer, Zaparoo admits unauthenticated clients from the LAN (Z-LEGACY below). Don't configure a Zaparoo API key for it: once any key is configured, Zaparoo refuses every client without one, the Zaparoo phone app included.

2. SSH (for MiSTer.ini, save backups, attract-mode detection and the running core)

MiSTer's root password is 1. Give it a key once, from the machine this server runs on:

ssh-keygen -t ed25519 -N "" -f ~/.ssh/mister_ed25519
ssh-copy-id -i ~/.ssh/mister_ed25519.pub root@<mister-ip>

SSH is optional per MiSTer; without it, get_status lists what's unavailable.

3. Tell the server about it

mcp-server-misterfpga check --host <mister-ip> --name "Family Room" --model "SuperStation One" \
    --ssh --ssh-key ~/.ssh/mister_ed25519

check confirms Zaparoo answers, shows the MiSTer's SSH host key fingerprint and pins it once you say yes, tests the login, and saves the MiSTer to config.json. Name MiSTers by where they are; --model says what each one is (a MiSTer Pi, a SuperStation One, a DE10-Nano), because nothing on the network says so reliably. Then:

mcp-server-misterfpga doctor

Related MCP server: hass-mcp

Install

git clone https://github.com/SDNick484/mcp-server-misterfpga.git && cd mcp-server-misterfpga
python -m venv .venv && . .venv/bin/activate && pip install -e .

Python 3.11 or newer. Every compiled dependency ships a musl wheel, so Alpine needs no compiler.

Use it from Claude

claude mcp add misterfpga -- /path/to/.venv/bin/mcp-server-misterfpga     # Claude Code, stdio

For Claude Desktop, add it to claude_desktop_config.json under mcpServers with the same command. To use it from claude.ai or the mobile app too, run it as a service and add it as a connector (see Remote access).

Tools

All tools are prefixed with mister_ to eliminate namespace collisions when multiple MCP servers run concurrently in an agent's context. CLI commands support shorthand (e.g., mcp-server-misterfpga call search_games query=gunstar or call status).

Tool

Does

Annotations

mister_get_status

each MiSTer: model, what's playing, running core, attract mode, library size, free space, capabilities and limitations

read-only

mister_list_systems

systems each MiSTer can launch, with game counts and the core that runs each

read-only

mister_search_games

find games by name, optionally in one system; each result has a ref

read-only

mister_play_history

recently played, or most played by total time, from Zaparoo's own records

read-only

mister_get_ini

MiSTer.ini (or an alternate) parsed into sections, as MiSTer reads it

read-only

mister_list_nfc

NFC readers, card mappings, the last card scanned

read-only

mister_launch

a game by ref, or a system's core with no game; confirmed running; refuses while anything runs

idempotent

mister_return_to_menu

end what's running (unsaved progress is lost)

destructive

mister_take_screenshot

what the MiSTer shows, as an image

—

mister_set_ini

one MiSTer.ini key, global or per core, with a backup first; dry_run shows the diff

idempotent

mister_backup_saves

saves and save states into a dated snapshot in your backup folder (rate-limited)

idempotent

mister_run_update

start Update All on the MiSTer's screen (it may reboot); needs host and confirm

destructive

mister_write_nfc_card

write a game onto the card on the reader (overwrites the card)

destructive

mister_map_nfc_card

make a card's UID launch a game, without writing to it

idempotent

A ref is <MiSTer>/<id>, e.g. Family Room/4812: the same game on two MiSTers has two refs, and the ref decides which TV it launches on. Actions return {"host", "outcome": "done" | "unchanged" | "dry_run", "detail", "warnings"}; failures are tool errors that say what to do ("Family Room is running Super Metroid. Call return_to_menu ...", "Which MiSTer should open the core? Pass host: Office, Family Room.").

Resource misterfpga://ini-keys: every key set_ini accepts, with type and range, and the refused ones with the reason. Prompt make_nfc_cards: one card at a time, for a list of games.

What the model can't do

  • No paths, no ZapScript, no shell. Games launch by ref; the server asks Zaparoo for that game's own launch script. A system launch is built from a validated system id. SSH runs five fixed commands, nothing else.

  • SFTP stays in its lane: reads of MiSTer*.ini and saves/, savestates/; writes of MiSTer*.ini (with a backup); deletes of this server's own old INI backups only.

  • No Zaparoo settings, profiles, playtime limits, device power or Zaparoo updates: the methods it may send are an allow-list, pinned by a test.

  • set_ini refuses main (another Main binary), bootcore, shared_folder and debug, plus array keys (player_1_controller, ...); values are checked against MiSTer's own table.

  • Never guess a MiSTer. An action that could mean either MiSTer is refused, naming both.

  • Never replace a running game. launch refuses; ending a game is its own call.

  • Rate limits per MiSTer: launches and returns to the menu (4, then 1 per 15 s), screenshots, INI writes, NFC, and Update All (once an hour).

Dry run

serve --dry-run (or MISTERFPGA_DRY_RUN=1) reads the MiSTers but changes nothing: every action returns outcome: "dry_run" with what it would do. set_ini and backup_saves also take a per-call dry_run.

config.json

~/.config/mcp-server-misterfpga/config.json (or $MISTERFPGA_CONFIG_DIR); check writes it.

{
  "hosts": [
    {"host": "192.168.1.70", "name": "Office", "model": "MiSTer Pi",
     "ssh": {"key": "~/.ssh/mister_ed25519", "host_key": "ssh-ed25519 AAAA..."}},
    {"host": "192.168.1.71", "name": "Family Room", "model": "SuperStation One",
     "manufacturer": "Retro Remake"}
  ],
  "backup_dir": "/mnt/backups/mister-saves",
  "backup_keep": 30
}
  • ssh: key (no passwords are stored), host_key (pinned by check --ssh; a host without one is never connected to), optional user (root) and port (22).

  • zaparoo_port: 7497 unless you changed Zaparoo's api_port.

  • backup_dir (or $MISTERFPGA_BACKUP_DIR) has no default: any folder this server can write to (a local disk, NFS, SMB, an OpenMediaVault share mounted into the LXC). Until it's set, backup_saves says how to set it.

  • MISTERFPGA_HOSTS (comma-separated addresses) replaces the list, keeping names from the file.

Backups

backup_saves reads /media/fat/saves and /media/fat/savestates over SFTP into <backup_dir>/<host>/<date_time>/. Files unchanged since the last snapshot (same size and modification time) are hard-linked to it, as rsync --link-dest does: every snapshot is complete on its own, and only changes cost space. A snapshot is written as .partial and renamed when complete. The newest backup_keep (30) are kept. The MiSTer is only read.

Play tracking in Home Assistant

mcp-server-misterfpga track publishes what each MiSTer plays to Home Assistant over MQTT, with the same entities and payloads as mcp-server-batocera's playtracker, so one automation or dashboard card covers the arcade and both MiSTers. It runs as a second service next to the MCP server, not on the MiSTer: Zaparoo already pushes media.started / media.stopped over the network, so nothing goes on the SD card and a reflash can't lose it. It covers games launched any way: from the MiSTer's menu, an NFC card, the Zaparoo app, or launch.

What gets logged

  • A session is a game from start to quit. Sessions under MISTERFPGA_MIN_SESSION_SECONDS (default 60) aren't logged.

  • Missed sessions are caught up. If the tracker or a MiSTer's connection was down, it reads Zaparoo's own play history on reconnect and publishes what it missed, back to the last session it published (kept in /var/lib/mcp-server-misterfpga/track-state.json). The very first run starts from now instead of replaying all of Zaparoo's history.

  • A session's started comes from the MiSTer, and its length from Zaparoo's own count, so they agree with play_history.

  • Not yet: attract mode. MiSTer Super Attract Mode's demo games are real launches, so until attract detection lands they're logged like any other session over the minimum length.

Install

  1. In Home Assistant, create a user for it (Settings > People > Users, e.g. misterfpga). The Mosquitto add-on accepts Home Assistant users.

  2. In /etc/mcp-server-misterfpga/env (see Service and Cloudflare), set MISTERFPGA_MQTT_HOST, _USER and _PASSWORD. The file is readable by the service user only.

  3. Start it: rc-update add mcp-server-misterfpga-track default && rc-service mcp-server-misterfpga-track start. It logs to /var/log/mcp-server-misterfpga/track.log.

A device per MiSTer appears in Home Assistant under MQTT, named from its name and model: Office MiSTer Pi, Family Room SuperStation One.

Entities

For a MiSTer named Office with model MiSTer Pi (check the device page for the exact IDs):

Entity

State

Attributes

sensor.office_mister_pi_now_playing

game name, or Idle

system, rom, started, source, host, device

binary_sensor.office_mister_pi_playing

on while a game runs

sensor.office_mister_pi_now_playing_system

system id (SNES)

sensor.office_mister_pi_last_played

last game logged

system, rom, started, ended, duration_s, host, device

sensor.office_mister_pi_last_session_length

minutes

event.office_mister_pi_game_session

session_start, session_end

game, system, rom, source, started (+ ended, duration_s at the end), host, device

sensor.office_mister_pi_activity

menu, playing, off

availability

misterfpga/<mister>/status and misterfpga/_tracker/status

For automations (lights when a game starts), use Activity. It follows only the tracker, so when a MiSTer is switched off it reads off rather than unavailable. menu means no game is running; a core opened without a game (say, ao486) also reads menu. The other entities go unavailable when either the MiSTer or the tracker is gone, so a stale "playing" never shows.

Topics are misterfpga/<mister>/now_playing (retained), last_session (retained), session (events), activity (retained) and status, where <mister> is the name in lower case (family_room). Unlike Batocera's script, the tracker keeps its broker connection open, so its MQTT last will marks everything unavailable if it dies. There's no equivalent of Batocera's Cabinet event: Zaparoo has no power events, and a MiSTer rebooting looks the same as one switched off.

If the broker is unreachable (Home Assistant restarting, say), the latest state of each entity is sent when it's back, along with logged sessions; a session_start that would arrive late is dropped, so its automation doesn't fire at the wrong time (Batocera's rule).

Daily play time on one MiSTer:

sensor:
  - platform: history_stats
    name: Office MiSTer play time today
    entity_id: binary_sensor.office_mister_pi_playing
    state: "on"
    type: time
    start: "{{ today_at() }}"
    end: "{{ now() }}"

Remote access: stdio, or HTTP behind Cloudflare Access

There are two ways to run it:

  • stdio: Claude Code or Claude Desktop launches it on a machine on your LAN.

  • serve --http: Streamable HTTP on 127.0.0.1:8716/misterfpga/mcp, as a long-lived service (see Service and Cloudflare). Published through a Cloudflare Tunnel with Cloudflare Access in front, it's one URL you add as a connector in Claude Code, Claude Desktop and the mobile app, the same way as its siblings (Onkyo, Shield TV, Harmony, Sofabaton, webOS, Batocera).

The HTTP transport is remote.py, shared byte for byte with those siblings through mcp-server-shared. What sits behind it is more powerful than a TV remote: this server can start an update that reboots a MiSTer, and its SSH key is root on them. Zaparoo, on MiSTer, has no authentication either. So the server is the gate, and it doesn't rely on Cloudflare's edge alone:

  • With Access configured, every request needs a valid Access assertion, checked here: signature (Access's published keys), the application's audience tag, issuer, expiry, and MCP_ALLOWED_EMAILS if set. Anything else gets a plain 403, so a request that reaches the port from the LAN, around the tunnel, is refused too. Set MCP_ALLOWED_EMAILS to yourself: it keeps that control yours even if the Access policy is later widened for another app.

  • Without Access, it serves only the machine it runs on. It won't listen on anything but loopback, won't start with a --public-host, and answers 403 to anything a tunnel or proxy forwarded. --insecure-no-auth turns that off for a short trusted-LAN test; never run the service with it: it would give anything that can reach the port reboot-capable, root-over-SSH control of your MiSTers. Its Host and Origin checks block DNS-rebinding from a browser, not a direct request.

  • Never expose Zaparoo (7497) or SSH (22) themselves. Only this server's port goes through the tunnel.

  • What a caller can do is the same over HTTP as over stdio: the limits below are in the tools, not the transport. run_update still needs host and confirm, and is rate-limited to once an hour per MiSTer.

Service and Cloudflare

deploy/alpine/ installs it as an OpenRC service on an always-on Alpine machine on the MiSTers' LAN (a Proxmox LXC, for example), in the siblings' deploy/alpine/ layout so they can share a container: owned by its own user mcp-mister, with the SSH key and config.json in /var/lib/mcp-server-misterfpga (0700), and the HTTP and Access settings in /etc/mcp-server-misterfpga/env:

sh deploy/alpine/install.sh        # prints the next steps: key, ssh-copy-id, check, doctor, start

The service runs /opt/mcp-server-misterfpga/run.sh (serve --http) under supervise-daemon (restarted if it exits) as mcp-mister, logging to /var/log/mcp-server-misterfpga/server.log. Its settings are the MCP_HTTP_*, MCP_PUBLIC_HOSTS and CF_ACCESS_* lines in the env file:

Flag

Variable

Default

--bind

MCP_HTTP_BIND

127.0.0.1

--port

MCP_HTTP_PORT

8716 (the siblings use 8711 to 8715, Batocera 8717, Pixelcade 8718)

--path

MCP_HTTP_PATH

/misterfpga/mcp

--public-host

MCP_PUBLIC_HOSTS

none: only loopback Host headers pass

CF_ACCESS_TEAM_DOMAIN, CF_ACCESS_AUD

none: serves only this machine

MCP_ALLOWED_EMAILS

anyone Access lets in (set it)

To use it as a connector, with cloudflared running on the same machine (keep the 127.0.0.1 bind):

  1. Tunnel route: a public hostname, say mcp.example.com, path misterfpga/*, to http://localhost:8716. Several servers can share the hostname, one path each.

  2. Access application (self-hosted) covering that hostname and path, with a policy for who may use it and Managed OAuth turned on, so Claude's connector can sign in through Access. Note its AUD tag.

  3. The env file: MCP_PUBLIC_HOSTS=mcp.example.com, CF_ACCESS_TEAM_DOMAIN=<team>.cloudflareaccess.com, CF_ACCESS_AUD=<the AUD tag>, MCP_ALLOWED_EMAILS=<you>, then rc-service mcp-server-misterfpga restart.

  4. The connector: https://mcp.example.com/misterfpga/mcp.

GET /healthz answers without auth, for monitoring. Details are in src/misterfpga_mcp/remote.py. CI runs deploy/alpine/smoke-test.sh in a stock python:3.12-alpine container: install, the test suite on musl, the OpenRC service as its user, an MCP call over HTTP against the simulator, and the Access settings from the env file turning the Access check on.

Logs

stderr only (stdout is the MCP stdio transport). Addresses and MACs are redacted unless --no-redact (MISTERFPGA_LOG_UNREDACTED=1); keys and tokens always are. -v adds protocol traffic, including SSH.

Testing without a MiSTer

pip install -e ".[dev]" && pytest && ruff check . && ruff format --check . && mypy
mcp-server-misterfpga simulate                # two simulated MiSTers; prints what to run next, e.g.:
MISTERFPGA_CONFIG_DIR=~/.config/mcp-server-misterfpga-sim/config mcp-server-misterfpga call launch game="Office/101"

The simulator (src/misterfpga_mcp/sim/) runs, for each MiSTer, a fake Zaparoo Core speaking JSON-RPC over a real WebSocket and a fake MiSTer SSH server (real SSH and SFTP, chrooted to a folder standing in for the SD card), over one shared device state, so launching through Zaparoo changes what /tmp/CORENAME says over SSH. The server's real clients connect to both. Two MiSTers on 127.0.0.1 and 127.0.0.2: Office (a MiSTer Pi) and Family Room (a SuperStation One), with Super Metroid on both on purpose.

tests/test_track.py runs the play tracker against the same simulator and a small fake MQTT broker: menu launches, outages, catching up after a restart, a broker restart.

doctor --dump DIR records a real MiSTer's read-only Zaparoo answers (redacted); put them in tests/fixtures/recorded/ and test_recorded.py replays them through the tools.

Shared code

src/misterfpga_mcp/remote.py, logsafe.py, tasks.py, ratelimit.py, their tests and scripts/sync_shared.py come from mcp-server-shared, pinned by shared.lock. Don't edit them here: change them there, then python scripts/sync_shared.py --update. CI's shared-drift job fails on a copy that differs.

What has been confirmed on hardware

Every protocol detail this server relies on is in src/misterfpga_mcp/assumptions.py, with its source and how confident that source is. doctor prints the unconfirmed ones; HARDWARE_VALIDATION.md says which step confirms each.

Assumption

Confidence

Status

Claim

Z-ENDPOINT

high

simulator-only

Zaparoo's API: JSON-RPC 2.0 over ws://<host>:7497/api/v0.1; ping → pong

Z-LEGACY

high

simulator-only

MiSTer admits unauthenticated LAN clients (no encryption by default)

Z-LEGACY-CAPS

high

simulator-only

such a client may launch, search, screenshot, use NFC; not device or update methods

Z-RUN-WAITS

high

simulator-only

run returns after the launch finishes, with categorized errors

Z-STOP-MENU

medium

simulator-only

stop returns an FPGA core to the menu

Z-MEDIAID

medium

simulator-only

media.meta by mediaId gives the launch script; ids last until a reindex

Z-TRACKS-MENU

medium

simulator-only

menu launches are tracked too (with recents=1, log_file_entry=1)

Z-NOTIFY

high

simulator-only

media.started / media.stopped payloads

Z-HISTORY

medium

simulator-only

play history includes menu launches

Z-SESSION-TIMES

medium

simulator-only

a session's live start and its history entry agree (the tracker's catch-up)

Z-SCREENSHOT

high

simulator-only

screenshot returns a base64 PNG and where it was saved

Z-NFC

medium

simulator-only

readers.write waits for a card; UIDs are normalized; scans show in tokens

Z-SCRIPT

low

simulator-only

**mister.script:update_all.sh runs Update All on screen

M-INI-SECTIONS

high

simulator-only

how MiSTer matches [sections]

M-INI-KEYS

high

simulator-only

the key table, from Main_MiSTer cfg.cpp

M-INI-APPLY

medium

simulator-only

INI changes apply at the next core load

M-INI-RENAME

low

simulator-only

write-then-rename works on the SD card over SFTP

M-CMD

high

simulator-only

load_core on /dev/MiSTer_cmd

M-CORENAME

high

simulator-only

/tmp/CORENAME, MENU in the menu

M-PATHS

medium

simulator-only

saves and save states locations

M-SSH

medium

simulator-only

root SSH with a key, and SFTP

S-ACTIVE

medium

simulator-only

attract mode ⇔ SAM's loop_core process

S-HANDOFF

medium

simulator-only

picking up a controller in attract mode reloads the game

U-REBOOT

high

simulator-only

Update All may reboot when it finishes

H-SS1

low

simulator-only

the SuperStation One behaves like any MiSTer here

License

MIT. Zaparoo Core, Main_MiSTer and mrext are GPL-3.0; this server uses their documented protocols and cites facts read from their source (such as the INI key table), and copies none of their code.

Available Tools

14 tools
backup_savesBack up savesA
Idempotent

Copy a MiSTer's saves and save states into a new dated snapshot in the configured backup folder.

Files unchanged since the last snapshot are hard-linked rather than downloaded, so each snapshot is complete but only changes cost space. The MiSTer is only read. Old snapshots beyond the configured number are removed. Needs SSH and a backup folder (backup_dir).

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).
dry_runNoCount what would be copied, write nothing.

Output Schema

NameRequiredDescription
hostYes
filesYes
copiedYes
linkedYes
prunedYes
outcomeYes
snapshotYes
bytes_copiedYes

TDQS

A3.7/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses rich behavior: hard-linking unchanged files, read-only MiSTer access, retention-based removal of old snapshots, and SSH/backup_dir prerequisites. However, “Old snapshots beyond the configured number are removed” conflicts with destructiveHint=false, which says the tool is not destructive. Because the description explicitly describes deletion while the annotation marks it non-destructive, this is an annotation contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the main action. Each sentence adds useful context: hard-link efficiency, read-only source access, retention behavior, and prerequisites.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that an output schema exists and the input schema is fully described, the description provides enough operational context for an agent to understand the tool. It covers side effects, retention, and prerequisites; the remaining issue is the annotation conflict rather than missing description content.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents host and dry_run thoroughly. The description mentions backup_dir, but that is a configuration prerequisite rather than a parameter, and it adds no host or dry_run semantics beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: copying saves and save states into a dated snapshot in the configured backup folder. This is clearly distinct from siblings such as launch, get_status, and run_update.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the operation and prerequisites (SSH and backup_dir), giving clear context for when the tool applies. It does not explicitly state when not to use it or compare against alternatives, but no sibling tool appears to offer equivalent backup functionality.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_iniRead MiSTer.iniA
Read-only

Read a MiSTer's MiSTer.ini (or an alternate ini), parsed into sections the way MiSTer reads it. [MiSTer] is global; [SNES] and other core-named sections override it for that core. Needs SSH.

NameRequiredDescriptionDefault
fileNoMiSTer.ini, or an alternate such as MiSTer_crt.ini (get_ini lists them).MiSTer.ini
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).
sectionNoOnly this section, e.g. MiSTer or SNES. Omit for all.

Output Schema

NameRequiredDescription
fileYes
hostYes
filesYes
sectionsYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered; the description adds a real operational constraint by stating SSH is required. It also discloses that output is section-structured and that core-named sections override [MiSTer], which is behavior beyond the annotations. No failure behavior (e.g. unreachable host) is described.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with what is read and how it is parsed, then the section override rule, then the SSH prerequisite. No filler and nothing repeated from the schema verbatim.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only tool with an output schema and fully documented parameters, the description covers purpose, structure, and the SSH requirement. The remaining gap is minor: it does not say what happens when a requested section or file is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description earns a bump by explaining the meaning of the section parameter — [MiSTer] is global and core sections override it — which the schema only hints at with an example. It also clarifies that alternate ini files exist, complementing the file parameter's own description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (read) and resource (MiSTer.ini, or an alternate ini), plus the shape of the result: parsed into sections the way MiSTer reads it. That parsing detail distinguishes it from a generic file-read tool and implicitly from the write-side sibling set_ini.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a prerequisite ("Needs SSH") and explains the section hierarchy so the agent knows how to scope the read, but it never names the alternative tools (set_ini for writes, get_status for hosts) or states when not to use it. Usage is inferable rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_statusGet MiSTer statusA
Read-only

Start here. Every MiSTer (or just host): its model, what it's playing, and what works on it right now.

capabilities names what works (search, launch, screenshot, ini_write, backup_saves, ...); limitations says what doesn't and how to fix it, e.g. Zaparoo Core not installed, or SSH not set up. core is the running MiSTer core (MENU in the menu) and attract_mode is true while MiSTer Super Attract Mode cycles games; both need SSH. dry_run true means this server reports writes instead of doing them.

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).

Output Schema

NameRequiredDescription
hostsYes
dry_runYes

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint/openWorldHint; the description goes well beyond them by disclosing prerequisites (core and attract_mode require SSH), what capabilities/limitations mean, and the server-level dry_run flag that changes whether writes are executed. That is exactly the kind of context annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with 'Start here.' and then dense, field-by-field explanation. A little run-on in the capabilities/limitations sentence, but nearly every clause conveys new semantics an agent must interpret output correctly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't enumerate return values, and instead explains the meaning of the harder fields (capabilities, limitations, core, attract_mode, dry_run). Complete for a zero-required-param read tool; only the sibling routing is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the host parameter is documented in the schema itself, so the description adds no further syntax or format detail. Baseline 3 applies when the schema does the work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the resource (MiSTer status) and enumerates exactly what the report contains: model, current game, capabilities, limitations, core, attract_mode. That is far more specific than the name alone, though it stops short of explicitly contrasting with siblings like list_systems or get_ini.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Start here' is an explicit first-call directive, and the host note ('Omit when the call already says which (a ref does)') tells the agent precisely when the single parameter is unnecessary. No exclusion against sibling tools is stated, which keeps it below a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

launchLaunch a game or coreA
Idempotent

Launch a game (by ref) or open a system's core (by id) and wait until the MiSTer reports it running.

Refuses while anything is running on that MiSTer: ending a game loses unsaved progress, so that's return_to_menu's job, and you should ask first. Rapid repeated launches are rate-limited.

NameRequiredDescriptionDefault
gameNoA game ref from search_games or play_history, e.g. 'Family Room/4812'.
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).
systemNoInstead of a game: a system id from list_systems, to open its core with no game.

Output Schema

NameRequiredDescription
hostYes
detailYes
outcomeYes
warningsYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (which state non-read-only, non-destructive, idempotent), the description discloses that the call blocks until the target reports running, that it refuses while busy, and that rapid repeats are rate-limited. Those are the operationally important traits an agent cannot infer from the hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The action and completion condition come first, then the refusal rule and its alternative, then the rate-limit note. Every sentence carries distinct operational weight with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With full schema coverage, an output schema handling return values, and annotations covering safety, the description fills the remaining gaps: blocking behavior, busy-state refusal, rate limits, and destructive side effects of the alternative tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds genuine meaning the schema does not express: 'game (by ref)' vs 'system (by id)' as mutually exclusive modes, and implicitly that host is derivable from a ref. The 'or' framing clarifies an exclusion the anyOf schema leaves open.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and two distinct modes (launch a game by ref, open a system core by id), plus the completion semantics ('wait until the MiSTer reports it running'). This distinguishes it cleanly from siblings like return_to_menu and get_status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the preclusion condition (refuses while anything is running), routes the conflicting case to return_to_menu, and even advises asking the user first because unsaved progress is at stake. This is exactly the when/when-not/alternative structure.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_nfcList NFC readers and cardsC
Read-only

A MiSTer's NFC readers, the card-to-game mappings Zaparoo has, and the last card scanned (its UID is what map_nfc_card uses by default).

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).

Output Schema

NameRequiredDescription
hostYes
readersYes
mappingsYes
last_cardYes
last_card_atYes

TDQS

C2.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds which data is exposed (readers, mappings, last-scan UID) but does not disclose whether results are cached, how many MiSTers are covered, or what happens if no card has been scanned — modest added value.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single compact sentence with no filler, and the most useful cross-reference (map_nfc_card UID default) is placed at the end where it is memorable. The grammatical fragment structure is slightly awkward but costs little space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so explaining return values is redundant — yet that is essentially all the description does, leaving the action verb and usage context unaddressed. For a simple read tool that is borderline adequate, but the definition doesn't fully pull its weight alongside the structured fields.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With one parameter and 100% schema description coverage, the schema already explains 'host' (MiSTer name from get_status, omit when a ref supplies it). The description contributes nothing further about the host parameter, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The sentence is a noun-phrase fragment that enumerates what the tool surfaces (NFC readers, card-to-game mappings, last scanned card) but never states the verb — the listing action is only inferable from the title. It does distinguish content from siblings implicitly and name-checks map_nfc_card, but an agent reading the description alone has to guess that this is a read/list operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use or when-not-to-use guidance. The parenthetical about map_nfc_card defaulting to the last scanned card's UID hints at a sequencing relationship, but it is oblique and doesn't tell the agent when to call list_nfc versus write_nfc_card or map_nfc_card.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_systemsList systemsA
Read-only

The systems each MiSTer can launch (consoles, computers, arcade), with how many games each has indexed and the MiSTer core that runs it. Use a system's id with search_games, or launch(system=...) to open the core with no game (e.g. a computer core).

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).

Output Schema

NameRequiredDescription
systemsYes
unavailableYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint true and openWorldHint false, so the safety profile is covered. The description still adds useful substance: the listing is scoped per MiSTer (matching the host parameter), and each entry includes indexed game counts and the running core.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with what is listed and what each entry contains, then the follow-up usage. There is slight density in the comma-and-parenthesis parentheticals, but no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, side-effect-free single-parameter listing tool with an output schema, the description covers what is listed, per what host, and how the ids are used downstream. Return shape is handled by the output schema, so nothing important is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter has 100% schema description coverage and already explains the host/ref convention, so the schema does the work. The description does not mention the host/ref selection behavior itself, so it adds nothing to what the schema provides. That meets the baseline for fully covered schemas.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb and resource ('The systems each MiSTer can launch') and enumerates the categories (consoles, computers, arcade) plus what each row carries (game count, core). This makes the tool's distinct role clear relative to siblings like get_status, which reports hardware status, or search_games, which consumes the ids it produces.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It tells the agent such ids are the input to search_games, and that the id can also be passed to launch(system=...) for a core with no game (e.g. a computer core). That is a concrete use case and a pointer to two specific alternatives, though it does not say when *not* to call this listing (e.g. if the system id is already known from a ref).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

map_nfc_cardMap an NFC card to a gameA
Idempotent

Make a card launch a game without writing to it: Zaparoo remembers 'this card's UID means this game'. Works with read-only cards and toys. Ask the user to scan the card on the game's MiSTer first, then call this without uid. Refuses a card that's already mapped.

NameRequiredDescriptionDefault
uidNoThe card's UID. Omit to use the card scanned on that MiSTer in the last 2 minutes.
gameYesA game ref from search_games or play_history, e.g. 'Family Room/4812'.

Output Schema

NameRequiredDescription
hostYes
detailYes
outcomeYes
warningsYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare idempotentHint=true and destructiveHint=false, and the description reinforces this by explaining the card is never written to and read-only cards/toys work. It also discloses a refusal path (already-mapped cards) and a prerequisite human step, which annotations cannot express. Minor gap: what happens on re-mapping to a different game or an invalid game ref is unstated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each carrying distinct information: what happens, who it works with, and how to invoke it. The key constraint (no card writing) is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values needn't be described, and the description covers prerequisites, card compatibility, and refusal behavior. It stops short of covering edge cases like an unknown game ref or an already-mapped card being reassigned.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the schema already explains that omitting uid uses the card scanned in the last 2 minutes. The description restates the omit-uid workflow but adds no syntax or format detail beyond the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb and resource (map a card to a game) plus the key mechanism: the mapping lives in Zaparoo, not on the card. That mechanism implicitly separates it from the sibling write_nfc_card, so an agent can tell the two apart without reading schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a usable procedure ('Ask the user to scan the card on the game's MiSTer first, then call this without uid') and a condition for failure ('Refuses a card that's already mapped'). It does not explicitly name write_nfc_card as the alternative when the user does want to write to the card, leaving that inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

play_historyPlay historyA
Read-only

What's been played on each MiSTer, from Zaparoo's own records: games launched from the MiSTer's menu count too. Rows have a ref, so 'play the last thing again' is one launch call.

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).
modeNorecent: last played first, one row per game. top: most played by total time.recent
limitNo
systemNoA system id from list_systems, e.g. 'SNES' or 'Genesis'.

Output Schema

NameRequiredDescription
entriesYes
unavailableYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds genuinely useful context: the records are Zaparoo's own and include games launched from the MiSTer menu (a non-obvious inclusion rule), plus that rows carry a ref. It says nothing about ordering guarantees across modes or result limits, so it adds value but stays thin.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the scope, and every clause carries information (data source, menu-launch inclusion, ref for replay). The conversational phrasing ('play the last thing again') is slightly informal but not wasteful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and annotations covering read-only semantics, the description only needs to frame the data and its quirks; it does that by explaining provenance and the ref field. Minor gaps remain around mode-specific behavior and limits, but nothing blocks correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 75% and the schema already explains host, mode ('recent' vs 'top'), and system with examples. The description only contributes the ref/host interaction obliquely; it adds no syntax or semantics for limit or mode beyond what is structured.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening clause names the resource and scope concretely ('what's been played on each MiSTer') and adds the data source ('Zaparoo's own records'). It is clearly a read/history query, distinct from launch or get_status, though it never names a sibling to contrast with.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: 'play the last thing again' hints at the replay workflow via launch, and the host parameter note says to omit it when a ref already identifies the host. There is no explicit statement of when to choose recent vs top, or when to prefer this over search_games.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

return_to_menuReturn to the MiSTer menuA
DestructiveIdempotent

End whatever is running and go back to the MiSTer menu. Unsaved progress in the game is lost, so confirm with the user first. Without host, acts on the one MiSTer that's running something.

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).

Output Schema

NameRequiredDescription
resultsYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true; the description goes further by naming exactly what is destroyed (unsaved in-game progress) and by mandating user confirmation before the call. It also adds host-resolution behavior not covered by annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the action, followed by the risk warning and the default-targeting rule. Nothing is padded or repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-optional-param destructive action with an output schema and full annotations, the description covers the action, its consequence, the required user confirmation, and host defaults. Nothing an agent needs in order to call it safely is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so baseline is 3; the description adds genuine meaning by explaining the fallback semantics of omitting host ('acts on the one MiSTer that's running something'), which the schema itself only hints at.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('end whatever is running') and destination ('back to the MiSTer menu'), which cleanly distinguishes it from write-oriented siblings like launch or run_update. The scope ('whatever is running') is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear precondition for calling: confirm with the user first because progress is lost. It also explains default targeting when host is omitted. It does not name an alternative tool, but there is no obvious competing sibling for this action.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

run_updateUpdate the MiSTerA
Destructive

Start Update All on a MiSTer's own screen (cores, Main, Linux, and whatever else it's set to update).

It runs for several minutes and the MiSTer may reboot at the end. Refuses while a game is running. Limited to once an hour per MiSTer.

NameRequiredDescriptionDefault
hostYesMiSTer name from get_status. Required: there's no default for this.
confirmYesMust be true: you've confirmed with the user that it may reboot.

Output Schema

NameRequiredDescription
hostYes
detailYes
outcomeYes
warningsYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructiveHint=true annotation, the description discloses duration (several minutes), a possible reboot at the end, the game-running refusal, and the hourly rate limit. These are exactly the behavioral traits an agent needs before invoking a destructive action.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the action, then duration/reboot, then refusal and rate-limit. Every sentence contributes distinct, useful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists and annotations cover the safety profile, the description still adds the operational context (duration, reboot, refusal, rate limit) that neither structured field provides. Nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters are fully documented in the input schema, including the 'Must be true' constraint on confirm and the no-default note on host. The description adds no parameter semantics beyond what the schema already provides, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific operation ('Start Update All') on a specific resource (a MiSTer's own screen) and enumerates what gets updated (cores, Main, Linux). This clearly distinguishes it from siblings like set_ini or launch.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear context for when this runs (an Update All) and explicit preconditions for refusal (a game running) and a rate limit (once an hour per MiSTer). It doesn't name an alternative tool, but the sibling set makes the choice obvious.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

screenshotTake a screenshotA

Capture what a MiSTer is showing, as an image. MiSTer also saves it under /media/fat/screenshots. Without host, uses the one MiSTer that's running something.

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false but give no reason; the description supplies that reason by disclosing the side effect — a file is written to /media/fat/screenshots. That is genuine added context beyond the structured fields. It stops short of saying what the call returns (image payload vs. path) or how the write failure is surfaced.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, action first, side effect second, default behavior third. Nothing is padded and nothing needs re-reading.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-required-parameter tool with full schema coverage and a declared side effect, this is nearly sufficient. The one gap is the return shape — whether the agent gets image content back or just a file path — which is not covered by an output schema and is only implied by 'as an image'.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter is well documented as 'MiSTer name from get_status', so baseline would be 3. The description goes slightly beyond by explaining the runtime fallback behavior when host is omitted, which the schema only gestures at with 'omit when the call already says which'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Capture what a MiSTer is showing, as an image.' No sibling tool (get_status, launch, backup_saves, etc.) overlaps with this action, and the description makes the distinction obvious without needing the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Without host, uses the one MiSTer that's running something' gives a clear condition for the host-omitted call path. There are no true alternatives among siblings, so no exclusions are needed, though it never explicitly says when to prefer this over, say, inspecting state via get_status.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_gamesSearch for gamesA
Read-only

Find games in each MiSTer's Zaparoo library by name (every word must match), optionally in one system.

Each result has a ref for launch. The same game on two MiSTers comes back twice, with two refs: the ref says which TV it launches on. variant tells apart files with the same name (region, revision, hack).

NameRequiredDescriptionDefault
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).
limitNoMost results per MiSTer.
queryYesWords in the game's name, e.g. 'metroid'.
systemNoA system id from list_systems, e.g. 'SNES' or 'Genesis'.

Output Schema

NameRequiredDescription
resultsYes
truncatedYes
unavailableYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint=false, so safety is covered; the description adds genuinely new behavior — that the search fans out across every MiSTer, that duplicates across hosts are returned twice with distinct refs, and what `variant` disambiguates. It stops short of noting limits or empty-result behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short, front-loaded paragraphs with no filler; the matching rule and scope come first. Some of the result-shape explanation (ref, variant) is arguably redundant given an output schema exists, but it is brief and earns its place through the cross-host duplicate caveat.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a four-parameter read-only search with a full schema and an output schema, the description covers the non-obvious parts: multi-host fan-out, duplicate results, ref-to-host binding, and variant disambiguation. Nothing an agent needs to call and interpret it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning on top: 'every word must match' specifies AND-matching that the schema's 'Words in the game's name' does not, and 'optionally in one system' clarifies the system parameter's filtering role.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — 'Find games in each MiSTer's Zaparoo library by name' — and pins down matching semantics ('every word must match') plus the optional system scope. This is clearly distinguishable from siblings like list_systems or list_nfc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: the mention of 'a ref for launch' hints at the search-then-launch workflow, but no alternative tool is named and no when-not condition is given. An agent must infer that this is the discovery step before launch.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_iniChange a MiSTer.ini settingA
Idempotent

Set (or remove) one MiSTer.ini setting, globally or for one core, keeping the rest of the file as it is.

The previous file is saved next to it first (MiSTer.ini..bak). The change takes effect the next time a core loads. A few keys are refused (main, bootcore, shared_folder, debug; see misterfpga://ini-keys). Needs SSH.

NameRequiredDescriptionDefault
keyYesA MiSTer.ini key, e.g. vscale_mode (misterfpga://ini-keys lists them).
fileNoMiSTer.ini, or an alternate such as MiSTer_crt.ini (get_ini lists them).MiSTer.ini
hostNoMiSTer name from get_status. Omit when the call already says which (a ref does).
valueYesThe new value, checked against the key's type and range. null removes the key.
dry_runNoShow the change (diff) without writing it.
sectionNoMiSTer (global), arcade, arcade_vertical, video=<WxH@Hz>, or a core name like SNES.MiSTer

Output Schema

NameRequiredDescription
keyYes
newYes
oldYes
diffYes
fileYes
hostYes
backupYes
detailYes
outcomeYes
sectionYes
warningsYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations: it discloses the timestamped backup filename, that the change only applies on next core load, that certain keys (main, bootcore, shared_folder, debug) are refused, and that SSH is required. These are exactly the operational facts an agent needs before mutating a config file.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the main action and scope, then backup, timing, refusals and prerequisite. Every sentence carries distinct, non-redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutating config tool with full annotations and an output schema, the description covers safety (backup), timing (next core load), blocked inputs and auth (SSH). Nothing an agent needs in order to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage the schema already documents key, file, host, value (including null removal), dry_run and section. The description reinforces the global/per-core and remove semantics but adds no syntax or constraints beyond what the schema provides, so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (set/remove), resource (one MiSTer.ini setting) and scope (globally or for one core), and clarifies it preserves the rest of the file. That cleanly separates it from the read-only sibling get_ini without naming it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clear context for when to use it: setting or removing a single key in a specific section, with the deferred-effect note. It does not explicitly point to get_ini for reading or to dry_run for previewing, so the alternative-routing guidance is implicit rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

write_nfc_cardWrite a game to an NFC cardA
Destructive

Write a game onto the NFC card sitting on the reader attached to the game's MiSTer, so tapping it launches the game. Overwrites whatever the card held. Ask the user to place the card first; the call waits up to a minute for it.

NameRequiredDescriptionDefault
gameYesA game ref from search_games or play_history, e.g. 'Family Room/4812'.
readerNoReader id from list_nfc, when there's more than one.

Output Schema

NameRequiredDescription
hostYes
detailYes
outcomeYes
warningsYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=false, and the description reinforces this concretely ('Overwrites whatever the card held'). It also discloses behavior annotations cannot express: a blocking wait of up to a minute and a required human step. Return value detail is omitted, but an output schema exists so that is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the action and its effect, followed by the destructive caveat and the human-in-the-loop timing. No filler and every sentence carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter, destructive hardware-writing tool with an output schema, the description covers purpose, side effect, precondition, and blocking behavior. The main omission is any mention of the map_nfc_card sibling, leaving the agent to infer which NFC operation to choose.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and both parameters ('game' with ref format, 'reader' tied to list_nfc) are fully documented in the schema. The description adds no parameter-level meaning beyond that, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Write a game onto the NFC card') plus the physical target (reader attached to the game's MiSTer) and the outcome (tapping launches the game). It does not distinguish itself from the sibling map_nfc_card, which an agent could easily confuse with this operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operational context: ask the user to place the card first, and the call blocks up to a minute. That is actionable pre-call guidance. It stops short of saying when to prefer this over map_nfc_card or what to do if the card is absent at timeout.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 14 tool updatesv0.1.0
    • First observedbackup_saves
    • First observedget_ini
    • First observedget_status
    • First observedlaunch
    • First observedlist_nfc
    • First observedlist_systems
    • First observedmap_nfc_card
    • First observedplay_history
    • First observedreturn_to_menu
    • First observedrun_update
    • First observedscreenshot
    • First observedsearch_games
    • First observedset_ini
    • First observedwrite_nfc_card

TDQS

A3.8/5.0

Scored across 14 tools

Disambiguation4/5

Most tools have clearly distinct resource+action targets, and the descriptions explicitly delimit the tricky pairs (write_nfc_card writes to the card vs map_nfc_card registers a UID mapping; get_ini vs set_ini; launch vs return_to_menu). Minor overlap remains between get_status, list_systems and play_history, which all return some form of device/system state, but the intended entry point and granularity are spelled out.

Naming Consistency4/5

The dominant pattern is verb_noun (get_status, list_systems, search_games, set_ini, backup_saves, run_update, write_nfc_card, map_nfc_card), which is predictable. A few deviations exist — play_history is noun-based, and launch/screenshot/return_to_menu are bare verbs or phrases — but they are still readable and unambiguous.

Tool Count5/5

14 tools sit comfortably in the well-scoped range for a device-control server covering discovery, launching, screen capture, config, backups, updates and NFC. Each tool maps to a distinct operation with no filler or redundant entries.

Completeness4/5

The lifecycle is largely covered: discover (status, systems, search, history), act (launch, return_to_menu, screenshot, run_update), configure (get/set_ini), and maintain (backup_saves, NFC list/write/map). Notable gaps are the absence of an unmap/remove NFC mapping operation (map exists with no inverse), no restore-from-backup, and no way to enumerate all games in a system without a name query.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to control Commodore 64 Ultimate hardware via REST API, supporting program execution, memory operations, disk management, audio playback, and device configuration through natural language commands.
    22
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    Enables controlling and querying Home Assistant devices and services via natural language. Supports state retrieval, listing states, and calling any Home Assistant service.
    13
    134 npm
    5
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to discover, read and control smart-home devices from multiple vendors through a unified capability model, and to author and trigger YAML scenes that a local deterministic engine executes without the AI in the control loop. High-risk actions such as door locks and garage doors are held in a confirmation queue for a human, and every call is written to a local audit log.
    Apache 2.0