mcp-server-misterfpga
This MCP server lets Claude/agents manage one or more MiSTer FPGA devices over Zaparoo Core and SSH, covering game discovery/launching, configuration, backups, NFC, updates, and Home Assistant play tracking.
Inspect each MiSTer’s status, model, running game/core, attract mode, library size, free space, capabilities, and limitations.
List launchable systems with game counts and associated MiSTer cores.
Search games by name/system and return refs for launching.
View recent or most-played history from Zaparoo records.
Launch a game by ref or open a system core with no game, and return to the MiSTer menu.
Take screenshots of what a MiSTer is showing.
Read and change
MiSTer.iniglobally or per core, with diffs, backups, dry runs, validation, and refusal of unsafe keys.Back up saves and save states into dated, hard-linked snapshots in a configured backup folder, pruning old snapshots.
Start Update All on the MiSTer screen after confirmation, rate-limited to once per hour per MiSTer.
List NFC readers, card mappings, and last scanned card; write a game to an NFC card or map a card UID to a game.
Publish play sessions, current activity, and related entities to Home Assistant over MQTT via a companion tracker service.
Run as stdio or HTTP behind Cloudflare Access, with dry-run mode, rate limits, and a simulator for testing without hardware.
Provides tools for managing NFC cards and readers on MiSTer devices, including listing NFC readers, card mappings, and the last card scanned; writing a game onto an NFC card; and mapping a card's UID to launch a game without writing to it.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-server-misterfpgalaunch Super Mario World on the Family Room"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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:
Run Update All. During its countdown, press UP to open its settings, turn on Zaparoo under Tools & Scripts, choose SAVE, and let it run.
On the MiSTer, open Scripts → zaparoo and accept the prompt to start it as a service at boot.
From another machine, open
http://<mister-ip>:7497/app/: that's Zaparoo's web UI. Let it build its media database (that's whatsearch_gamessearches).For accurate tracking of games launched from the MiSTer's own menu, Zaparoo asks for
recents=1andlog_file_entry=1in[MiSTer]ofMiSTer.ini. Once SSH works (below),set_inican do it;doctortells 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_ed25519check 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 doctorRelated 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, stdioFor 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 |
| each MiSTer: model, what's playing, running core, attract mode, library size, free space, capabilities and limitations | read-only |
| systems each MiSTer can launch, with game counts and the core that runs each | read-only |
| find games by name, optionally in one system; each result has a | read-only |
| recently played, or most played by total time, from Zaparoo's own records | read-only |
|
| read-only |
| NFC readers, card mappings, the last card scanned | read-only |
| a game by | idempotent |
| end what's running (unsaved progress is lost) | destructive |
| what the MiSTer shows, as an image | — |
| one | idempotent |
| saves and save states into a dated snapshot in your backup folder (rate-limited) | idempotent |
| start Update All on the MiSTer's screen (it may reboot); needs | destructive |
| write a game onto the card on the reader (overwrites the card) | destructive |
| 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*.iniandsaves/,savestates/; writes ofMiSTer*.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_inirefusesmain(another Main binary),bootcore,shared_folderanddebug, 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.
launchrefuses; 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 bycheck --ssh; a host without one is never connected to), optionaluser(root) andport(22).zaparoo_port: 7497 unless you changed Zaparoo'sapi_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_savessays 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
startedcomes from the MiSTer, and its length from Zaparoo's own count, so they agree withplay_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
In Home Assistant, create a user for it (Settings > People > Users, e.g.
misterfpga). The Mosquitto add-on accepts Home Assistant users.In
/etc/mcp-server-misterfpga/env(see Service and Cloudflare), setMISTERFPGA_MQTT_HOST,_USERand_PASSWORD. The file is readable by the service user only.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 |
| game name, or | system, rom, started, source, host, device |
| on while a game runs | |
| system id ( | |
| last game logged | system, rom, started, ended, duration_s, host, device |
| minutes | |
|
| game, system, rom, source, started (+ ended, duration_s at the end), host, device |
|
| |
availability |
|
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 on127.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_EMAILSif set. Anything else gets a plain 403, so a request that reaches the port from the LAN, around the tunnel, is refused too. SetMCP_ALLOWED_EMAILSto 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-authturns 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_updatestill needshostandconfirm, 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, startThe 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 |
|
|
|
|
|
|
|
|
|
|
| none: only loopback Host headers pass |
| none: serves only this machine | |
| 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):
Tunnel route: a public hostname, say
mcp.example.com, pathmisterfpga/*, tohttp://localhost:8716. Several servers can share the hostname, one path each.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.
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>, thenrc-service mcp-server-misterfpga restart.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 |
| high | simulator-only | Zaparoo's API: JSON-RPC 2.0 over |
| high | simulator-only | MiSTer admits unauthenticated LAN clients (no encryption by default) |
| high | simulator-only | such a client may launch, search, screenshot, use NFC; not device or update methods |
| high | simulator-only |
|
| medium | simulator-only |
|
| medium | simulator-only |
|
| medium | simulator-only | menu launches are tracked too (with |
| high | simulator-only |
|
| medium | simulator-only | play history includes menu launches |
| medium | simulator-only | a session's live start and its history entry agree (the tracker's catch-up) |
| high | simulator-only |
|
| medium | simulator-only |
|
| low | simulator-only |
|
| high | simulator-only | how MiSTer matches |
| high | simulator-only | the key table, from Main_MiSTer |
| medium | simulator-only | INI changes apply at the next core load |
| low | simulator-only | write-then-rename works on the SD card over SFTP |
| high | simulator-only |
|
| high | simulator-only |
|
| medium | simulator-only | saves and save states locations |
| medium | simulator-only | root SSH with a key, and SFTP |
| medium | simulator-only | attract mode ⇔ SAM's |
| medium | simulator-only | picking up a controller in attract mode reloads the game |
| high | simulator-only | Update All may reboot when it finishes |
| 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 toolsbackup_savesBack up savesAIdempotent
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).
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). | |
| dry_run | No | Count what would be copied, write nothing. |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | Yes | |
| files | Yes | |
| copied | Yes | |
| linked | Yes | |
| pruned | Yes | |
| outcome | Yes | |
| snapshot | Yes | |
| bytes_copied | Yes |
TDQS
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.
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.
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.
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.
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.
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.iniARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| file | No | MiSTer.ini, or an alternate such as MiSTer_crt.ini (get_ini lists them). | MiSTer.ini |
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). | |
| section | No | Only this section, e.g. MiSTer or SNES. Omit for all. |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | |
| host | Yes | |
| files | Yes | |
| sections | Yes |
TDQS
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.
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.
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.
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.
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.
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 statusARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). |
Output Schema
| Name | Required | Description |
|---|---|---|
| hosts | Yes | |
| dry_run | Yes |
TDQS
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.
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.
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.
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.
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.
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 coreAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| game | No | A game ref from search_games or play_history, e.g. 'Family Room/4812'. | |
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). | |
| system | No | Instead of a game: a system id from list_systems, to open its core with no game. |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | Yes | |
| detail | Yes | |
| outcome | Yes | |
| warnings | Yes |
TDQS
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.
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.
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.
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.
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.
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 cardsCRead-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).
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | Yes | |
| readers | Yes | |
| mappings | Yes | |
| last_card | Yes | |
| last_card_at | Yes |
TDQS
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.
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.
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.
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.
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.
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 systemsARead-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).
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). |
Output Schema
| Name | Required | Description |
|---|---|---|
| systems | Yes | |
| unavailable | Yes |
TDQS
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.
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.
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.
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.
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.
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 gameAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| uid | No | The card's UID. Omit to use the card scanned on that MiSTer in the last 2 minutes. | |
| game | Yes | A game ref from search_games or play_history, e.g. 'Family Room/4812'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | Yes | |
| detail | Yes | |
| outcome | Yes | |
| warnings | Yes |
TDQS
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.
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.
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.
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.
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.
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 historyARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). | |
| mode | No | recent: last played first, one row per game. top: most played by total time. | recent |
| limit | No | ||
| system | No | A system id from list_systems, e.g. 'SNES' or 'Genesis'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| entries | Yes | |
| unavailable | Yes |
TDQS
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.
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.
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.
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.
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.
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.
run_updateUpdate the MiSTerADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| host | Yes | MiSTer name from get_status. Required: there's no default for this. | |
| confirm | Yes | Must be true: you've confirmed with the user that it may reboot. |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | Yes | |
| detail | Yes | |
| outcome | Yes | |
| warnings | Yes |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). |
TDQS
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.
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.
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.
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.
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.
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 gamesARead-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).
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). | |
| limit | No | Most results per MiSTer. | |
| query | Yes | Words in the game's name, e.g. 'metroid'. | |
| system | No | A system id from list_systems, e.g. 'SNES' or 'Genesis'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| truncated | Yes | |
| unavailable | Yes |
TDQS
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.
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.
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.
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.
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.
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 settingAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | A MiSTer.ini key, e.g. vscale_mode (misterfpga://ini-keys lists them). | |
| file | No | MiSTer.ini, or an alternate such as MiSTer_crt.ini (get_ini lists them). | MiSTer.ini |
| host | No | MiSTer name from get_status. Omit when the call already says which (a ref does). | |
| value | Yes | The new value, checked against the key's type and range. null removes the key. | |
| dry_run | No | Show the change (diff) without writing it. | |
| section | No | MiSTer (global), arcade, arcade_vertical, video=<WxH@Hz>, or a core name like SNES. | MiSTer |
Output Schema
| Name | Required | Description |
|---|---|---|
| key | Yes | |
| new | Yes | |
| old | Yes | |
| diff | Yes | |
| file | Yes | |
| host | Yes | |
| backup | Yes | |
| detail | Yes | |
| outcome | Yes | |
| section | Yes | |
| warnings | Yes |
TDQS
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.
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.
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.
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.
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.
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 cardADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| game | Yes | A game ref from search_games or play_history, e.g. 'Family Room/4812'. | |
| reader | No | Reader id from list_nfc, when there's more than one. |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | Yes | |
| detail | Yes | |
| outcome | Yes | |
| warnings | Yes |
TDQS
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.
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.
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.
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.
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.
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.
14 tool updates
v0.1.0- First observed
backup_saves - First observed
get_ini - First observed
get_status - First observed
launch - First observed
list_nfc - First observed
list_systems - First observed
map_nfc_card - First observed
play_history - First observed
return_to_menu - First observed
run_update - First observed
screenshot - First observed
search_games - First observed
set_ini - First observed
write_nfc_card
TDQS
Scored across 14 tools
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.
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.
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.
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
Related MCP Connectors
Drive your retro handheld console: find your games, launch, save, set volume, get cheat codes.
Control your existing Philips lights, Sonos speakers, Roborock vacuums and other supported devices through Port OS cloud. Give ChatGPT, Claude and other MCP-compatible assistants access to the devices you connect. Turn off the living room lights, play music on your speakers, adjust the thermostat, run a vacuum in a room, check a camera snapshot or find a connected tracker. Available actions depend on your linked provider accounts and each device’s supported capabilities. Port OS runs in the cloud; no local server is required. You choose the account, device and capability permissions each assistant receives. Device operations require user OAuth; public tool discovery does not grant device access. Port OS is in alpha. Setup: https://portoperatingsystem.com/docs/getting-started/ Support: https://portoperatingsystem.com/contact/
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Reach your own computers from any AI app: files, search, commands, approvals and outage alerts.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables 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.22MIT
- AlicenseAqualityFmaintenanceEnables controlling and querying Home Assistant devices and services via natural language. Supports state retrieval, listing states, and calling any Home Assistant service.13134 npm5MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to control Home Assistant via natural language, including device control, automation management, and system monitoring.MIT
- AlicenseNot gradedqualityBmaintenanceEnables 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