RomM MCP
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., "@RomM MCPsearch my ROMs for Super Mario on SNES"
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.
RomM MCP
A token-efficient MCP server for a self-hosted RomM library. Runs locally over stdio or as an authenticated Streamable HTTP service. Covers the complete OpenAPI HTTP surface in RomM 5.3.1 and 5.4.0, plus the hidden 5.4 WebDAV routes and 13 callable Socket.IO events, with explicit operator switches for writes, files, administration, authentication, tasks, and realtime connections. All switches default to false.
Tools and discovery
The server advertises 10 tools by default, or 14 with writes enabled. HTTP operations are discovered on demand from the configured RomM OpenAPI schema, so hundreds of endpoint schemas do not inflate every MCP tool listing.
Tool | Purpose |
| Find platform IDs and library counts |
| Search title, platform, collection, and RetroAchievements availability |
| Bounded synopsis; opt into metadata, files, or personal progress |
| Find regular collections by name |
| Collection details and paginated contents |
| Create a private collection, verified by readback; requires writes |
| Atomically add/remove membership, verified by readback; requires writes |
| Change supplied progress fields, verified by readback; requires writes |
| Search all HTTP operations, scopes, required switches, and enabled status |
| Inspect one operation and resolve component schemas on demand |
| Invoke an enabled HTTP read by its discovered operation ID |
| Invoke an enabled HTTP operation that changes state; requires writes |
| Stage uploads or retrieve downloads using bounded temporary handles |
| Discover, open, send, listen, and close main/netplay/device Socket.IO sessions |
Disabled operations remain discoverable. Start with romm_api_list, then
romm_api_describe, then call the appropriate read/write tool using the returned
operation_id. Requests support path, query (including repeated values), declared
headers, JSON body, form, multipart files, raw_artifact_id, or DAV xml_body. Known gaps in
RomM's upload schemas have explicit corrections in operation descriptions. Callers
cannot supply arbitrary destinations, URL paths, or upstream authorization headers.
For example, romm_api_read can retrieve a ROM through the generic interface:
{
"operation_id": "get_rom_simple_api_roms__id__simple_get",
"request": {"path": {"id": 123}, "response_pointer": "/name"}
}All clients share the configured RomM identity, artifacts, and sessions. This is a single-user gateway. API coverage does not grant missing upstream permissions or replace external provider, emulator, browser-login, or streaming prerequisites.
Related MCP server: crewmeister-api
Environment and authorization
Variable | Default | Effect |
| Required | RomM base URL without |
| Required | Upstream RomM bearer/client token |
|
| Master switch for state changes; registers four write tools |
|
| Deletions, replacement-capable uploads, regeneration, and destructive task paths |
|
| Administrative configuration, users, permissions, logs, and management endpoints |
|
| Authentication/token workflows and nondefault HTTP authentication modes |
|
| Binary transfers and file-affecting operations; permits artifact read/delete |
|
| Task endpoints, exports, streaming mutations, refresh/redownload actions, and socket sends |
|
| Main/netplay/device Socket.IO connections |
| Empty | Optional configured credentials for HTTP |
| Empty | Value only of the |
| Empty | Separate device-bound client token for |
| System temporary directory | Existing parent for private temporary artifact storage |
|
| Aggregate artifact storage cap in bytes, across all clients |
Switches are additive, checked on every invocation, and enabled by the value
true (case-insensitive). For example, a replacement-capable upload needs writes,
files, and destructive access; administrative mutation also needs admin access.
The operation catalog reports the required switches. Token scopes and the upstream
user's role remain separate requirements; a switch cannot override a RomM denial.
Read-only mode means upstream mutations are blocked, not that every GET is safe.
RomM's OpenID GETs, memory-card version listing, and task-status GET change state.
Reading save content also changes sync state when session_id is supplied, or when
device_id is supplied without optimistic=false. These calls require
romm_api_write and the applicable switches. Some file, admin, and auth reads need
their category switch even without writes enabled. Artifact creation/append requires
both files and writes; deleting a local artifact requires only files.
Create a scoped RomM client token as the user whose library state you want to manage.
The compact read tools need me.read, roms.read, platforms.read,
collections.read, and roms.user.read; compact writes additionally need
collections.write and roms.user.write. The bootstrap identity needs me.write
to create a client token; notification writes in 5.4 also need me.write. For other HTTP endpoints,
use the scopes returned by discovery and grant only the capabilities you enable.
Service accounts have their own collections and progress.
HTTP defaults to bearer authentication. With auth enabled, a request may choose
auth_mode=basic, session, device, or none. Basic mode uses configured credentials.
Device mode uses only ROMM_DEVICE_TOKEN, cannot mix with an auth session, and
is required for install claim/report when the main token is not device-bound.
Use auth_session="new" to start an isolated cookie jar and reuse the returned
opaque handle for a multi-step login flow; session mode requires such a handle.
At most eight HTTP auth sessions are retained, expiring after ten minutes of
inactivity. ROMM_SESSION_COOKIE is for main/netplay Socket.IO, not an implicit HTTP cookie.
Credentials belong in a secret manager or protected environment, never committed
client configuration. Treat auth endpoint outputs as sensitive.
Bounded responses and files
Compact searches default to 20 results (maximum 100). Follow next_offset until
null; get_collection uses roms.next_offset. Platform and collection filtering
is local because those upstream endpoints return full arrays. Search suppresses
RomM's full-library character, filter, and ROM-ID indexes. Compact synopses cap at
1,500 characters and optional file lists at 50 entries; truncated_fields reports
those limits. Summary names cap at 200 characters, regions/languages at ten values.
Generic responses support JSON Pointer selection with response_pointer and local
offset/limit pagination. Follow next_offset and inspect truncation markers;
select a nested field when its value was omitted. Local response pagination does
not advance the upstream query: use RomM's declared query pagination to retrieve
additional server-side pages. Non-download HTTP responses and OpenAPI loading are
bounded at 16 MiB. Only the OpenAPI schema is cached for the process.
For uploads, create an artifact with romm_artifact, then append strict base64
chunks of at most 32,768 decoded bytes, supplying the exact current byte offset.
Stale offsets reject retries. Pass its handle in a multipart files entry
(field, artifact_id, filename, optional content_type) or the ROM chunk
endpoint's raw_artifact_id. For downloads, set request.download=true, then
read the returned artifact in chunks; reads default to 4,096 bytes, maximum 32,768.
Artifacts use opaque handles, never caller-chosen filesystem paths. At most 16 artifacts share the configured byte cap (default 64 MiB). They expire 3,600 seconds after creation; this TTL has no environment setting. Expiry is checked on operations, and shutdown cleans storage. Open uploads keep their quota and artifact slot until their last read handle closes, and cannot be appended or deleted while in use. Failed writes retain reserved capacity until cleanup succeeds. Delete artifacts when finished; failed streamed downloads are cleaned up. Size the container's temporary filesystem to accommodate the configured transfer cap.
Realtime API
romm_socket(action="catalog") describes all 13 callable main/netplay events and
captured response events without opening a connection. Use open, then send and
listen with the same session handle, then close. Opening requires realtime;
sending additionally requires writes and tasks. Starting a scan also requires files
and destructive access. Capturing logs:entry needs admin.
Main events cover scan start/stop and activity start/heartbeat/stop. Netplay events
cover room open/join/leave, WebRTC signaling/error, data messages, snapshots, and
inputs. Main Socket.IO and authenticated 5.4 netplay require ROMM_SESSION_COOKIE.
Hosting needs a valid ROM ID in extra.game_id; passwords belong in top-level
password. Legacy extra.room_password is rejected to prevent silently creating
an unprotected room. Password hosting targets RomM 5.4; do not use it on 5.3.
Guest password joins can omit the cookie. Main sessions also capture notification,
activity refresh, and install updates. service="devices" listens on /devices
using only ROMM_DEVICE_TOKEN, without the general bearer or session cookie;
it captures install queue/cancellation events and accepts no send events.
Binary event values use
{"$binary_base64":"..."} within the payload limit.
There are at most four sessions, each lasting 300 seconds, with bounded queues of 20 events, 16 KiB JSON payload limits, and listen waits of at most ten seconds. Truncation is explicit. Disconnect clears activity and leaves netplay rooms; background scans continue. A successful send confirms transport acceptance only; inspect received events and HTTP state to verify its effects.
RomM 5.4 HTTP behavior
New REST endpoints are discovered from the running server; restart MCP after an upstream upgrade to refresh its cached schema. Device install mutations require tasks and files. Scans require tasks, files, and destructive access. Save/state file renames require files. Audit history and notification-channel mutations require admin: an administrator-owned token can otherwise reach broader data or private-network notification destinations despite narrow declared scopes. Sending a notification to explicit recipients also requires admin.
A ROM download GET with query.format can start a background conversion and must
use romm_api_write with writes, tasks, and files enabled. HTTP 202 returns a
pending result and retry-after, without a download artifact or automatic retry.
Check status and retry deliberately; a subsequent completed download yields an artifact.
The synthetic romm_webdav_* operations expose /api/sync/retroarch/ on 5.4+.
Supply a relative path.file_path (empty for root), with literal slash-separated
segments. Encoded traversal, URL destinations, and caller authorization headers
are rejected. PUT uses raw_artifact_id; PROPFIND and LOCK may use a bounded
xml_body. XML responses use normal bounded text output or download artifacts.
All DAV operations require files; GET/HEAD also require writes because upstream
can update device/asset records. PUT, DELETE and MOVE require destructive access.
RomM 5.4 implements MOVE as deletion and ignores Destination: it is not a rename.
ROM-library GET redirects are returned without following them; use the regular
ROM content endpoint to retrieve those bytes. Upstream user roles and scopes still apply.
Run locally
Install Python 3.12+ and uv, then:
git clone https://github.com/sharkusmanch/romm-mcp.git
cd romm-mcp
uv sync --locked
export ROMM_URL=https://romm.example.com
export ROMM_TOKEN=YOUR_ROMM_CLIENT_TOKEN
uv run romm-mcp --transport stdioTo enable compact mutations, add ROMM_ALLOW_WRITES=true. Enable additional
category switches only for the operations you intend to use.
HTTP / containers
export MCP_AUTH_TOKEN=YOUR_SEPARATE_RANDOM_TOKEN_AT_LEAST_32_CHARACTERS
export MCP_ALLOWED_HOSTS=localhost:8080,127.0.0.1:8080
uv run romm-mcp --transport http --host 127.0.0.1 --port 8080
# Published multiarchitecture image (HTTP on port 8080 by default):
docker run --rm --read-only --tmpfs /tmp:rw,nosuid,nodev,size=80m \
-p 127.0.0.1:8080:8080 \
-e ROMM_URL -e ROMM_TOKEN -e MCP_AUTH_TOKEN -e MCP_ALLOWED_HOSTS \
ghcr.io/sharkusmanch/romm-mcp:v0.3.0
# Container as a local stdio server:
docker run --rm -i --read-only --tmpfs /tmp:rw,nosuid,nodev,size=80m \
-e ROMM_URL -e ROMM_TOKEN \
ghcr.io/sharkusmanch/romm-mcp:v0.3.0 --transport stdioPass the desired ROMM_ALLOW_* variables into the container explicitly with -e.
Connect to /mcp using Authorization: Bearer <MCP_AUTH_TOKEN>. Static bearer
access is not a complete OAuth authorization server; TLS termination belongs at
your reverse proxy. HTTP refuses startup without at least 32 non-whitespace ASCII
token characters. The incoming MCP token is never forwarded to RomM.
MCP_ALLOWED_HOSTS is a comma-separated Host allowlist, including ports when used.
Defaults permit loopback hosts on any port. Requests with an Origin header are
rejected unless it appears in MCP_ALLOWED_ORIGINS; native MCP clients normally
send no Origin. /healthz provides unauthenticated process liveness only.
MCP_TRANSPORT, MCP_HOST, and MCP_PORT are supported; CLI flags win. Native
execution defaults to stdio; HTTP binds loopback unless overridden. The image runs
as UID/GID 1000 and requires no database, persistent volume, or Kubernetes access.
Client configuration
Claude Code supports a project-local HTTP server with a headersHelper returning
an Authorization JSON object; Codex supports project .codex/config.toml with
http_headers_helper. Use a helper reading a protected token file, or the clients'
environment-backed header options. See Claude MCP configuration
and Codex MCP configuration.
For stdio, use command uv, args run --directory /path/to/romm-mcp romm-mcp --transport stdio, and provide RomM credentials/switches through the environment.
Writes and errors
The three compact mutation tools independently read back their writes. Membership
changes use atomic endpoints and preserve other members; progress changes include
only supplied fields. Progress statuses are incomplete, finished,
completed_100, retired, or never_playing; backlogged and now_playing are
separate flags. status: null clears status. Ratings/difficulty range 0–10;
completion ranges 0–100. Private collection names are checked per owner to reduce
accidental duplicates.
Removing collection membership requires writes; deleting the collection itself also requires destructive access. Generic tools return structured MCP results.
Generic HTTP mutations report verification: "not_performed". HTTP success does
not prove the intended state, so independently read it back when needed. Neither
interface automatically retries writes. Transport ambiguity or failed compact
verification is reported as an uncertain outcome; inspect current state before
retrying. Concurrent outside edits can race compact readback because those RomM
calls lack ETag preconditions. Redirects are reported without being followed.
RomM content is untrusted data and must not be treated as instructions.
Development and releases
uv sync --locked
uv run ruff check .
uv run ruff format --check .
uv run pytest
uv build --no-sourcesTests cover real SDK clients over stdio and TCP HTTP, authentication/Host/Origin, compact write verification, complete operation discovery, request serialization, additive gates, bounded output/transfers, adversarial artifact cleanup, and sockets. CI tests Python 3.12 and 3.14 and builds/runs the restricted container. Release tags must match the package version. Tags publish amd64/arm64 images, per-platform BuildKit SBOMs, GitHub-signed provenance as OCI referrers, and attested Python archives.
gh attestation verify oci://ghcr.io/sharkusmanch/romm-mcp:v0.3.0 \
--repo sharkusmanch/romm-mcpDependency updates use the hosted Renovate GitHub App. Grant that app access to this repository using its installation settings. The repository does not need a Renovate Actions workflow or PAT secret.
Renovate configuration uses recommended defaults, pins GitHub
Actions and Docker digests, refreshes uv.lock weekly before 04:00 Monday UTC,
and disables automerge. Native managers cover the Dockerfile's Python ARG and uv
COPY --from, the setup-uv workflow version, and Python dependencies/lockfile; no
custom regex manager is needed. See official Dockerfile support,
GitHub Actions inputs,
and PEP 621 lockfile support.
See research and sources and the current full API design. The original design and implementation plan describe the superseded v0.1.0 scope. Licensed under MIT.
Available Tools
10 toolsget_collectionARead-onlyIdempotent
Get a regular collection and one page of its ROMs. Follow roms.next_offset.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| collection_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| roms | Yes | |
| collection | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive and closed-world, so the safety profile is covered. The description adds value beyond that by disclosing that the response is paginated ('one page of its ROMs') and how to advance ('Follow roms.next_offset'), which the annotations do not convey.
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 short sentences with zero waste, pagination guidance front-loaded second. Every clause earns its place and nothing is padded.
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 a rich annotation set and an existing output schema, the description appropriately defers return-shape details and instead explains pagination mechanics via roms.next_offset. It is nearly complete for this tool, with only the parameter semantics gap preventing a 5.
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 0%, so the description must compensate, but it only alludes to pagination via 'one page' and 'next_offset'. It never mentions the limit, offset, or collection_id parameters, leaving their semantics entirely to the schema.
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+resource pair ('Get a regular collection and one page of its ROMs'), which is clear and distinguishes the tool from the list_collections sibling by implying single-item retrieval. It also scopes the return to 'one page of its ROMs', but does not explicitly name the sibling it contrasts 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?
The phrase 'Follow roms.next_offset' gives concrete pagination guidance for iterating, which is a real usage hint. However, there is no explicit statement of when to use this versus list_collections or search_roms, leaving usage only partially implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_romARead-onlyIdempotent
Get one ROM and synopsis. Opt into metadata, files (max 50), or progress.
| Name | Required | Description | Default |
|---|---|---|---|
| rom_id | Yes | ||
| sections | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| name | Yes | |
| files | No | |
| ra_id | No | |
| regions | No | |
| summary | No | |
| metadata | No | |
| platform | No | |
| progress | No | |
| languages | No | |
| platform_id | No | |
| truncated_fields | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description adds one genuine behavioral constraint beyond that — the 50-file cap — but says nothing about truncation, permissions, or error 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?
Two short sentences, zero filler, and the core action ('Get one ROM') is front-loaded before the optional-section detail. Every clause earns its place.
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 return values need no explanation, and the rich annotations cover safety. For a simple two-parameter read tool the definition is nearly complete, missing only error/empty-result behavior and explicit sibling routing.
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 0%, so the description must carry the param burden. It explains that sections are opt-in and adds the 'max 50' constraint on files (not represented in the schema's maxItems=3), but rom_id is left entirely unexplained and the enum values themselves merely restate the schema.
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 ('Get one ROM and synopsis'), making the single-item retrieval scope clear against siblings like search_roms or list_collections. It does not explicitly name the sibling to avoid, so it stops just short of a 5.
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?
'Opt into metadata, files (max 50), or progress' gives useful guidance on when to include optional sections, but there is no statement of when to use this tool versus search_roms, nor any prerequisite or exclusion. Usage is implied rather than prescribed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_collectionsBRead-onlyIdempotent
List regular collections and counts; filter by name. Follow next_offset.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | No | ||
| offset | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| next_offset | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, so the safety profile is covered. The description adds one genuinely useful behavior ('Follow next_offset') that is not in the annotations or schema, but says nothing about what the counts contain or rate-limit 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?
A single tight sentence with no filler and the operation is front-loaded. The compressed phrasing ('and counts') is slightly elliptical but stays within an acceptable length.
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 return shape need not be described, and annotations cover safety. Still, 'regular' is unexplained, the name-filter parameter is not tied to the query field, and pagination semantics are reduced to two words – enough to be called but with clear gaps.
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 0%, so the description must compensate, but it only gestures at 'filter by name' and 'next_offset' without mapping either to the actual parameter names (query, offset) or clarifying limit/default/max. It is marginally better than nothing, which is the baseline 3.
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+resource ('List regular collections') and adds scope ('and counts'), which distinguishes it from the sibling get_collection. However, 'regular' is undefined jargon and it does not distinguish itself from list_platforms or the paginated siblings; a small gap keeps it from a 5.
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?
No when-to-use vs alternatives guidance at all; the agent must guess whether get_collection or list_collections is appropriate. 'filter by name' is the only conditional, and it is not framed as such.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_platformsBRead-onlyIdempotent
List library platforms and ROM counts; optional name filter. Follow next_offset.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | No | ||
| offset | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| next_offset | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and closed-world, so the safety profile is covered. The description adds paging behavior ("Follow next_offset"), which is genuinely useful context, but says nothing about result size, rate limits, or what a page contains.
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 short, front-loaded clauses with no filler; the core capability comes first and the operational note second. The only cost is that "Follow next_offset" is cryptic against an input parameter actually named offset.
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 return-value explanation is unnecessary, and annotations cover safety. Still, with 0% schema description coverage the agent is left guessing about limit semantics and the offset/next_offset relationship for a paginated list 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 0%, so the description must carry the parameter burden. It explains the query filter and points at offset-based paging, but never clarifies limit or the offset parameter's range/default, and it names "next_offset" (an output field) rather than the input parameter "offset".
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 uses a specific verb+resource ("List library platforms") and adds the distinguishing payload ("ROM counts"), which separates it from search_roms and list_collections. It stops short of explicitly naming a sibling or stating scope boundaries, so it is clear but not fully differentiated.
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?
"optional name filter" implies when to pass a query and "Follow next_offset" implies iterative paging, so usage is inferable. However, there is no explicit when-to-use versus search_roms (the obvious alternative for filtered lookups), and no stated prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
romm_api_describeCRead-onlyIdempotent
Fetch one operation's schema; resolve #/components/schemas/... on demand.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| schema_ref | No | ||
| operation_id | Yes | ||
| response_pointer | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description usefully adds that schema references can be dereferenced on demand, but says nothing about pagination behavior for limit/offset or how response_pointer alters output.
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 the core action front-loaded and no filler. The semicolon-clause about #/components/schemas/... is slightly cryptic but does carry 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?
Five parameters with 0% schema coverage, including pagination controls, and the description addresses only the schema-fetch/ref-resolution aspect. An output schema exists so return values need no explanation, but the parameter and pagination story is materially incomplete for a tool of this complexity.
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 0%, so the schema documents parameter names only. The description hints at schema_ref/pointer resolution behavior but leaves limit, offset, and response_pointer entirely unexplained, so it only partially compensates for the coverage gap.
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: 'Fetch one operation's schema', and adds the second capability of resolving component schema references. An agent can distinguish this introspection tool from data fetchers like get_rom, though siblings such as romm_api_list and romm_api_read are never named for contrast.
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?
'resolve ... on demand' faintly implies when ref resolution is needed, but there is no explicit when-to-use, no when-not, and no routing among the api_* siblings (romm_api_list, romm_api_read, romm_artifact). The agent must infer the boundaries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
romm_api_listCRead-onlyIdempotent
Discover every HTTP operation, required scopes and switches. Follow next_offset.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | ||
| limit | No | ||
| query | No | ||
| method | No | ||
| offset | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already grant readOnlyHint, idempotentHint, destructiveHint=false and closed-world, so the safety profile is covered. The description contributes one extra behavioral fact — that results are cursor-paginated via next_offset — which is real added value beyond the annotations, but it discloses nothing about result size, rate limits or what 'switches' contains.
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?
Eleven words in two sentences, with the discovery purpose front-loaded before the pagination note. Nothing is padded, though the terseness shades into cryptic rather than efficient at points.
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 return-value shape need not be spelled out, and annotations cover safety. The gap is input-side: five undocumented filter/pagination parameters with 0% schema coverage and no compensating prose leaves an agent unable to use the catalog's filters correctly.
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 0% across five parameters (tag, limit, query, method, offset), so the description carries the full burden and adds nothing. It never mentions filtering by tag, query, method or paging via limit/offset, leaving the agent to guess the filter semantics from bare property names.
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 (HTTP operations) and what it surfaces (required scopes, switches), which is more specific than a bare name restatement. However, 'switches' is undefined jargon, and it draws no line against close siblings romm_api_describe and romm_api_read, so an agent cannot tell from the text alone which of the trio to pick.
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?
'Follow next_offset' is a pagination instruction, not usage guidance. There is no statement of when to call this versus romm_api_describe or romm_api_read, and no prerequisites or exclusions. The agent is left to infer the discovery-vs-inspection split from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
romm_api_readCRead-onlyIdempotent
Invoke a read operation by discovered ID. Select/paginate output with request fields.
| Name | Required | Description | Default |
|---|---|---|---|
| request | No | ||
| operation_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds only 'Select/paginate output with request fields,' which hints at pagination but says nothing about how request fields (auth_mode, download, response_pointer, etc.) alter behavior — the biggest behavioral unknowns for this tool.
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 short sentences with no filler, and the core action is front-loaded. But the brevity is under-specification rather than genuine conciseness given the tool's complexity, and the second sentence is vague about which request fields do what.
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 return values need not be explained. Still, for a dynamic dispatcher with a large nested request object at 0% schema coverage, the description leaves the agent unable to construct a valid request or know how to discover the operation_id.
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 0%: neither operation_id nor any of the many APIRequest properties are documented beyond bare titles. The description mentions a 'discovered ID' and 'request fields' to select/paginate, but does not explain the meaning of limit/offset, auth_mode, download, or response_pointer, so it fails to compensate for the coverage gap.
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 verb+resource ('invoke a read operation') and identifies the key input ('discovered ID'), so the agent knows this is a read dispatcher. However, it does not distinguish this tool from its siblings, especially romm_api_list or romm_api_describe, which likely share the same dynamic-dispatch pattern.
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?
The phrase 'by discovered ID' faintly implies the operation_id must first be obtained (presumably via romm_api_describe), but no explicit when-to-use, when-not-to-use, or alternative is stated. An agent has no guidance on choosing this over romm_api_list or the dedicated get_rom/get_collection tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
romm_artifactCDestructive
Move file bytes through opaque temporary handles. Requires FILES; create/append also WRITES.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| action | Yes | ||
| offset | No | ||
| artifact_id | No | ||
| data_base64 | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, readOnlyHint=false, and idempotentHint=false, so the safety profile is covered. The description adds a real detail the annotations do not: which specific actions (create/append) mutate versus which do not, plus a FILES permission requirement. It stops short of describing handle lifetime, offset/limit behavior, or error modes, so the added value is moderate.
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 terse fragments with no filler, and the core operation leads before the permission caveat. The capitalized FILES/WRITES emphasizes the constraints effectively, though "opaque" costs clarity for brevity.
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 five parameters at 0% schema coverage and a destructive mutation surface, the description should explain the create-then-handle workflow, offset/limit paging, and data format. An output schema exists so return values need not be described, but the parameter and workflow gaps leave an agent unable to invoke this correctly from the description alone.
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 0% across five parameters, so the description carries the full burden and largely fails it. Only artifact_id is faintly implied ("opaque temporary handles"); limit, offset, and data_base64 receive no explanation of units, base64 encoding, or paging semantics.
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 states a rough verb+resource ("Move file bytes") but the operative phrase "through opaque temporary handles" is jargon that obscures what a caller actually does. It never references the create/append/read/delete actions in the enum, and it does nothing to separate this tool from siblings like romm_api_read or romm_api_list. Purpose is inferable but vague.
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?
"Requires FILES; create/append also WRITES" gestures at a precondition but never says when to choose this tool over the byte-oriented siblings (romm_api_read, romm_api_list, romm_socket). There is no when-to-use, no when-not-to-use, and no named alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
romm_socketCDestructive
Discover/use bounded realtime sessions. REALTIME enables connections; sends require WRITES+TASKS.
| Name | Required | Description | Default |
|---|---|---|---|
| event | No | ||
| action | Yes | ||
| payload | No | ||
| seconds | No | ||
| service | No | main | |
| max_events | No | ||
| session_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, openWorldHint=true, and non-idempotent, so the safety profile is partly covered. The description does add value beyond annotations by disclosing the WRITES+TASKS permission requirement for sends and framing sessions as 'bounded'. It still omits what a send can destroy, session lifetime, and the bounded window (seconds/max_events) semantics.
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 short clauses, front-loaded, with no filler. It is terse to the point of being cryptic, but nothing is wasted and the structural discipline is good.
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 return values need not be described. But this is a multi-action lifecycle tool (catalog/open/send/listen/close) with 7 parameters and zero schema coverage, and the description neither maps actions to behavior nor ties them to the session_id/seconds/max_events controls. It is substantially incomplete for the tool's complexity.
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 0% and there are 7 parameters, so the description must carry the burden, but it explains none of them. event, payload, seconds, max_events, session_id and service are left entirely undocumented; only the action enum supplies any meaning. REALTIME/WRITES/TASKS are permissions, not parameters, so they do not compensate.
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 resource (realtime sessions) but pairs it with a vague dual verb ('Discover/use'), so an agent cannot tell whether this lists sessions, opens them, or both. It never references the action enum (catalog/open/send/listen/close) that actually defines what the tool does. The sibling set is a different domain so confusion is limited, but the purpose remains under-specified.
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?
'REALTIME enables connections; sends require WRITES+TASKS' gives a genuine prerequisite for the send path, implying usage conditions tied to permissions. However it gives no when-to-use-vs-alternatives guidance and no explanation of which action maps to which lifecycle step. Usage is only partially implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_romsBRead-onlyIdempotent
Search ROMs by title/platform/collection/RA. Compact pages; follow next_offset.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | No | ||
| has_ra | No | ||
| offset | No | ||
| platform_ids | No | ||
| collection_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| next_offset | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds genuinely useful pagination behavior ('Compact pages; follow next_offset'), but does not explain what 'compact' omits or how the result set behaves across pages.
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 terse sentences, front-loaded with the purpose and then the pagination instruction. Nothing is wasted, though the extreme brevity borders on under-specification rather than pure conciseness.
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 six parameters, zero schema description coverage, and no usage guidance, the description is too thin to call the tool correctly without opening the schema. An output schema exists, so return values need not be described, but the input-side gaps remain significant.
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 0%, so the description carries the burden, and it does loosely map four of six parameters (query→title, platform_ids→platform, collection_id→collection, has_ra→RA). However, limit and offset semantics are only hinted at by 'next_offset', and the query string format is never explained.
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 verb (Search) and resource (ROMs) and enumerates the dimensions searchable — title, platform, collection, RA. This clearly separates it from list_platforms, list_collections, and get_rom, though it does not name those siblings explicitly.
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 when-to-use or when-not-to-use guidance, no prerequisites, and no mention of the sibling tools an agent should prefer for unfiltered listings. The agent must infer that this is the tool for filtered ROM lookup.
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.
10 tool updates
v0.2.1- First observed
get_collection - First observed
get_rom - First observed
list_collections - First observed
list_platforms - First observed
romm_api_describe - First observed
romm_api_list - First observed
romm_api_read - First observed
romm_artifact - First observed
romm_socket - First observed
search_roms
TDQS
Scored across 10 tools
Most tools target distinct resources and actions: list_platforms, search_roms, get_rom, list_collections, and get_collection are clearly separable. However, romm_api_read is a generic read escape hatch that can overlap conceptually with the specific read tools, which could cause an agent to hesitate between dedicated and generic paths.
Several tools follow a clean verb_noun pattern (list_platforms, search_roms, get_rom, list_collections, get_collection). Others use a romm_ prefix with varied forms (romm_socket, romm_api_list, romm_api_describe, romm_api_read, romm_artifact), creating a mixed convention that remains readable but not fully predictable.
Ten tools is well-scoped for a ROM management server that needs library browsing, collection access, generic API discovery, realtime sessions, and artifact transfer. Each tool appears to earn its place without excessive duplication.
The surface covers reading, searching, listing, API discovery, realtime sessions, and file bytes. However, explicit create, update, and delete operations for ROMs or collections are missing; romm_api_read is read-only and romm_artifact only moves bytes, leaving notable lifecycle gaps.
Maintenance
Related MCP Connectors
Remote MCP server exposing your Node Command fleet (files, shell, processes, browser) to AI hosts.
1Develop, manage, and debug Railway projects, services, and deployments from within agents.
Persistent memory, hybrid search and a goal graph for AI agents, over stdio or remote HTTP.
Securely control computers you explicitly pair through files, terminals, processes, screenshots, desktop UI/input, clipboard, browser automation, diagnostics, and document tools.
Related MCP Servers
- AlicenseCqualityBmaintenanceExposes the SSH + tmux client over stdio so agents can drive hosts, tmux sessions, commands, broadcasts, tunnels and audit logs programmatically. It offers 47 grouped tools covering host and session management, connectivity checks, and background port forwarding.47MIT
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to interact with the Crewmeister API over local stdio, covering operations for members, teams, time tracking, salary exports, and related platform resources.Apache 2.0
- AlicenseBqualityAmaintenanceEnables querying RetroAchievements data — user profiles, unlocks, game progress, game and achievement details, leaderboards, comments, tickets, and site feeds — through 15 read-only, token-efficient tools that cover all documented Web API endpoints over stdio or Streamable HTTP.151MIT
- AlicenseDqualityCmaintenanceEnables secure, configurable access to one or more Syncthing instances over MCP, with read-only defaults and opt-in write, destructive, and administrative operations. Supports stdio and authenticated Streamable HTTP transports, typed tools, bounded responses, and multi-instance configuration.40MIT