MrJ-JMRI-MCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| JMRI_URL | No | Base URL of the JMRI Web Server | http://localhost:12080 |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_systemsA | List every DCC power system known to JMRI, with its current power state. Use this to discover what systems exist before calling get_power, or to answer "what systems are there?". No side effects. |
| get_powerA | Get the current power state (ON/OFF/UNKNOWN/IDLE) of one DCC system. Args: system: System name, prefix, or fragment (e.g. "ohara", "O"). Case-insensitive. Omit to use JMRI's default system. No side effects — this only reads state, it never changes power. |
| set_powerA | Turn a DCC system's power ON or OFF, and report the state actually observed. Args: system: System name, prefix, or fragment (e.g. "ohara", "O"). Case-insensitive. Omit to use JMRI's default system. turn_on: True to turn power ON, False to turn it OFF. This writes to JMRI. The reported state is re-read ~1s after the command (JMRI's immediate POST response is transient/unreliable) — if the observed state doesn't match the request, "confirmed" will be false and the caller should say so honestly rather than assume success. |
| system_statusA | One-call diagnostic: is JMRI reachable, and what state is it in? Reports JMRI reachability/version and every power system's state. Call this first when something isn't responding, instead of guessing which tool to retry. No side effects. |
| list_rosterA | List every locomotive in JMRI's roster: name, DCC address, road, model. Use this to discover what locomotives exist and their DCC addresses before calling acquire_throttle/set_speed/etc. — those tools take a DCC address, not a name, and this is currently the only way to find out which address belongs to which named loco (e.g. the user says "start the Autorail" but set_speed needs address=4). road/model can be empty strings if the user never filled them in in JMRI — that's normal, not an error. No side effects. This does NOT resolve a name to an address for you automatically — use find_locomotive for that. |
| find_locomotiveA | Resolve a locomotive's spoken/typed name to its DCC address. Use this whenever the user names a locomotive ("the Autorail", "141R", "start the Pacific") instead of giving a DCC address directly — call this first to get the address, then pass that address to acquire_throttle/set_speed/set_direction/set_function/ lights_on/lights_off/etc. Matching is tolerant: case-insensitive, accent-insensitive (useful for French names — "boite a sel" matches "Boite à Sel"), and accepts an exact name or an unambiguous partial match ("autorail" matches "Autorail"). If the name matches more than one roster entry, or matches none, this returns an "error" explaining why (listing the candidates or the full roster) instead of guessing — ask the user to clarify rather than picking one yourself. |
| get_locomotive_functionsA | List a locomotive's named decoder functions (e.g. "F2": "Rear lights"). JMRI lets the user label each loco's functions individually in its roster editor — call this BEFORE set_function whenever the user refers to a function by what it does ("turn on the rear lights", "blow the whistle") instead of an F-number, so you can look up the right number instead of guessing or asking. Only labels the user actually set are returned; most locos have few or none (an empty "functions" dict is normal, not an error — it means this loco has no custom labels, so ask the user for an F-number instead). Args: name: The locomotive's name (fuzzy-resolved the same way as find_locomotive — call this directly, you don't need to call find_locomotive first just to get the exact name). Returns functions as {"F0": "label", ...}. Function numbers with no label set are omitted entirely (JMRI has 29 possible slots, F0-F28, per loco — only the labeled ones are useful to you). |
| acquire_throttleA | Acquire control of a locomotive by its DCC address, and report its current state. Args: address: The locomotive's DCC address. prefix: Optional command station prefix (e.g. "O", "Z", "R") to target when more than one DCC system is connected. Omit to use JMRI's default command station. You usually do NOT need to call this explicitly before set_speed/
stop/emergency_stop — those acquire the throttle automatically on
first use for a smoother voice UX ("speed up the 3" just works).
Call acquire_throttle directly when you specifically want to know a
loco's current speed/direction before deciding what to do (the
acquire reply reports both), or to target a non-default command
station via Safe to call again on an address already acquired by this session — JMRI just re-confirms it, it does not error or reset the loco. Release with release_throttle when done, though it's not required: JMRI releases every throttle this session holds automatically if the MCP server disconnects. |
| release_throttleA | Release this session's control of a locomotive acquired with acquire_throttle. Args: address: The locomotive's DCC address. Good practice once you're done controlling a loco (frees it up for other JMRI clients/throttles to acquire without contention), but not required for correctness — JMRI releases it automatically when the MCP server's connection to JMRI closes, so a missed release_throttle does not leave the loco "stuck" for other clients across restarts. |
| set_speedA | Set a locomotive's speed as a percentage of its maximum (0-100%). Args: address: The locomotive's DCC address. speed_percent: 0-100. Values outside this range are clamped, not rejected. Acquires the throttle automatically if this session doesn't already hold it — no need to call acquire_throttle first for a simple "speed up the 3" style voice command. Returns the actual speed JMRI reports back, as a percentage — this may differ slightly from what was requested (DCC uses a small number of discrete speed steps, so exact percentages get rounded). Use stop for a controlled halt (speed 0%) or emergency_stop for a panic stop — don't call set_speed(speed_percent=0) for an emergency, it's a different command to JMRI, not just "speed 0". A locomotive's speed can be changed by something other than this tool at any time — another JMRI panel, PanelPro, another MCP/voice session controlling the same loco. If the requested speed already matches the current one (whoever set it), JMRI does not send a confirmation and this call returns immediately without writing anything new to the layout — this is expected, not a failure; the reported speed_percent in the response is still accurate because it's read from a cache kept continuously up to date by JMRI's own state broadcasts, not from what this tool last sent. |
| stopA | Bring a locomotive to a controlled stop (speed 0%), like releasing the throttle. Args: address: The locomotive's DCC address. Acquires the throttle automatically if this session doesn't already hold it. For a panic/safety stop (derailment risk, collision course, or any
"stop it NOW") use emergency_stop instead — JMRI treats it as a
distinct decoder command (an immediate power cut to the motor), not
just "speed 0". Use this Safe to call repeatedly, including when the loco is already stopped: JMRI silently ignores a redundant "already at this speed" request instead of replying, and this tool's client keeps a local speed cache continuously refreshed by JMRI's own state broadcasts (which fire for ANY client's changes, not just this tool's), so a repeat call still returns the correct current speed_percent (very likely 0) without hanging or erroring. |
| emergency_stopA | Emergency-stop a locomotive immediately (JMRI's decoder e-stop command). Args: address: The locomotive's DCC address. Acquires the throttle automatically if this session doesn't already hold it. Use this ONLY for safety-critical stops: derailment risk, imminent
collision, or any situation calling for an immediate halt rather than
a smooth deceleration. This is JMRI's actual decoder emergency-stop
command (speed -1.0 on the wire, distinct from a normal speed
command) — it cuts power abruptly rather than ramping down, which is
rougher on the mechanism/cargo, so don't use it as a synonym for a
routine Returns |
| set_directionA | Set a locomotive's direction of travel: "forward" or "reverse". Args: address: The locomotive's DCC address. Acquires the throttle automatically if this session doesn't already hold it. direction: Must be exactly "forward" or "reverse" (case- insensitive). "forward"/"reverse" here mean the loco's own notion of front/back as wired in its decoder — not compass direction or "toward/away from the operator" — so if a user says "turn it around" or "go the other way", flip whatever the current reported direction is rather than guessing. Best practice: for a moving loco, bring it to a stop first — DCC decoders generally accept a direction change at speed, but it can cause a rough jolt or be ignored/delayed by the decoder depending on its configuration; this tool does not enforce that, it just forwards the request. Returns the direction JMRI actually reports back as "forward" or "reverse" (translated from JMRI's own true/false), not "stopped" — direction and speed are independent fields on the same throttle, so set_direction never changes speed and doesn't report one. Like set_speed/stop/emergency_stop, this is safe to call repeatedly with the same direction: JMRI silently no-ops a redundant "already going this way" request instead of replying, and this tool checks a local direction cache — kept fresh by JMRI's own broadcasts of state changes from ANY client, not just this one — before deciding whether to send anything, so a repeat call (or a direction that was actually last changed by a JMRI panel/PanelPro, not this session) still reports the correct current direction instead of hanging. |
| set_functionA | Turn one of a locomotive's decoder functions (F0-F28) on or off. Args: address: The locomotive's DCC address. Acquires the throttle automatically if this session doesn't already hold it. function: Function number, 0-28 inclusive (validated; anything outside that range returns an error rather than being sent to JMRI). What each number actually controls is decoder/ roster-specific — F0 is almost universally the headlight(s) (see lights_on/lights_off below for that common case), but F1-F28 vary loco to loco (bell, horn, sound effects, couplers, etc.). If a user names a function by effect ("turn on the bell", "rear lights") rather than a number, call get_locomotive_functions(name) FIRST to check for a user-set label before guessing or asking — only fall back to asking the user for the F-number if that loco has no label matching what they described. state: True to turn the function on, False to turn it off. Safe to call repeatedly with the same state: like set_speed/ set_direction, JMRI silently no-ops a redundant "already in this state" request instead of replying, and this tool checks a local per-function cache — kept fresh by JMRI's own broadcasts from ANY client holding this address, not just this one — before deciding whether to send anything, so a repeat call (or a function last toggled by a JMRI panel/PanelPro) still reports the correct current state instead of hanging. |
| lights_onA | Turn on a locomotive's headlight(s): shortcut for set_function(address, 0, True). F0 is almost universally the headlight function across DCC decoders (this is a very strong convention, not a JMRI/protocol guarantee), so this is the tool to reach for on a plain "turn the lights on" voice request without asking the user for a function number. Same auto-acquire and no-op-safe behavior as set_function. |
| lights_offA | Turn off a locomotive's headlight(s): shortcut for set_function(address, 0, False). See lights_on for why F0. Same auto-acquire and no-op-safe behavior as set_function. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/HO44-PROJECT/MrJ-JMRI-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server