Advanced Metal Research
GitHub Contact AMR

RosieOS MCP server

On this page
  1. Quick start
  2. What an agent can do
  3. Robot actions: three modes
  4. Why robot actions go through OLP only
  5. Jogging, bounded per call
  6. The heartbeat window
  7. Run program asks first
  8. Programs: OLP's own engine
  9. Tools
  10. rt-core and OLP status
  11. Robot actions
  12. Programs
  13. Motion servers, planner, repository and deploy
  14. Resources and prompts
  15. Configuration
  16. Build and test
  17. Maturity
  18. Related pages

rosieos-mcp-server is a Model Context Protocol (MCP) server for RosieOS. It lets an AI agent in Cursor, Claude Code or any other MCP client read a cell, author and plan programs, and, only when the cell's owner turns it on, drive the robot. It lives in mcp/v1 in the repository.

It reaches every layer of a cell:

  • rt-core, through rt-control's public API. Observation only.
  • Offline programming (OLP), the controller of record: machine status, cells, control and maintenance, the cell program store, and the robot actions OLP gates.
  • Programs: OLP's spreadsheet, edited by OLP's own TypeScript engine. Create, open, edit row by row and field by field, teach waypoints, import STEP parts with their detected seams, place parts, search seams, add welds, plan and save.
  • The motion servers: the dense trajectory daemon's TCP ingest and the Cartesian motion server's resolve-only kinematics.
  • The weld planner: health, planning a .weldplan and fetching the dense trajectory it filed.
  • The repository: cell manifests, the drive alarm table, .rdt files and the component documentation.
  • The deploy module's read-only commands.

Every route it calls already exists and is documented on that component's page. It adds no new way to move the robot: robot actions are OLP requests, with the same gates as the browser and the pendant.

Warning

Energised motion. With ROSIE_MCP_ROBOT_ACTIONS set to simulation or cell, tools on this page can arm, load, play and jog the robot. RosieOS has no software E-stop: keep the hardware E-stop within reach and clear the cell before you allow robot actions on a real cell. Jog and moves are checked against joint limits only, not against collisions. See the safety model.

Quick start#

The server is TypeScript on Node 20 or later. It bundles OLP's program engine, so it needs OLP's UI packages installed first. From the repository root:

(cd offline-programming/v1/ui && npm ci)   # the engine bundle's packages
cd mcp/v1
npm ci
npm run build                              # dist/core/olp-core.mjs + dist/src/index.js
node dist/src/index.js --help              # prints every environment variable

Then register it with your client. The server speaks stdio only; your client starts it as a child process.

.cursor/mcp.jsonJSON
{
  "mcpServers": {
    "rosieos": {
      "command": "node",
      "args": ["/path/to/RosieOS/mcp/v1/dist/src/index.js"],
      "env": { "ROSIE_MCP_ROBOT_ACTIONS": "off" }
    }
  }
}

The repository's own .cursor/mcp.json already registers it with robot actions off, so opening the RosieOS checkout in Cursor offers the server once it is built. Other clients take the same command:

# Claude Code
claude mcp add rosieos -- node /path/to/RosieOS/mcp/v1/dist/src/index.js

# The MCP Inspector, to try the tools by hand
npx @modelcontextprotocol/inspector node dist/src/index.js

With nothing else running, the offline tools work at once: cell manifests, drive alarms, .rdt inspection and the docs. Start a simulated cell with the quickstart (OLP on 127.0.0.1:8794) and the OLP, program and machine-status tools answer too. Against a cell from a Mac, point ROSIE_OLP_URL at the olp ui start tunnel, http://127.0.0.1:18894.

Note

Only stdio is served. A network listener would expose Stop and, in the other modes, Arm and Play, and the server has no authentication of its own. Don't wrap it in a network transport.

What an agent can do#

AreaToolsWhat they do
rt-core4Describe, Status, Maintenance and events from rt-control. Read only.
OLP status7Health, machine status (SIMULATION or REAL ROBOT), cells, who has control, the cell program store, the local simulator
Robot actions12Stop, Disarm, Pause and Finish cycle in every mode. Select cell, Arm, Load, Play, Run program and three bounded jogs only when robot actions allow them.
Programs34OLP's spreadsheet through OLP's own engine: rows, waypoints, events, STEP parts, fixtures, seams, welds, weld profiles, planning, path preview, save
Motion servers4Dense daemon status and dry-run validation; forward kinematics and straight-line moves from the Cartesian server, computation only
Weld planner3Health, plan a .weldplan, fetch the dense trajectory
Repository and deploy6Cell manifests, drive alarm lookup, .rdt inspection, docs search, read-only deploy commands

That is 70 tools, 62 of them registered with robot actions off. The full list is in Tools.

When a client connects, the server tells the agent how to work: read before you act (who has the robot, and whether the selected cell is a simulation), Stop is always available, quote refusals in OLP's own words and never work around them.

Robot actions: three modes#

ROSIE_MCP_ROBOT_ACTIONS decides which robot-action tools exist at all:

ModeRegisteredRefused
off (default)Stop, Disarm, Pause, Finish cycle—
simulationThe above, plus Select cell, Arm, Load, Play, Run program and the bounded jogs (Move joints, Cartesian step, Jog hold)Each of those on a cell that rt-core does not describe as backend simulation
cellThe same toolsNothing beyond OLP's own gates. Real robots included. This is the owner's choice, never a default.

Stop, Disarm, Pause and Finish cycle only end motion sooner. OLP passes them from any client through every gate, so they exist in every mode.

Under simulation, each motion tool reads OLP's machine status immediately before it acts and refuses unless the target is a simulation. OLP derives that flag from rt-core's own Describe backend, not from a cell name. The action carries that read's target fence (X-RT-Target-Generation, X-RT-Target-Cell), so if another client switches cells between the read and the action, OLP refuses it as target_changed. A real-robot target gets no request at all.

Never offered, in any mode:

  • Home. Home makes the current position the reference. Only an operator runs it, at the mechanical home pose, and never as a recovery.
  • Fault reset. Arm already resets what the drive profile classes as resettable. Anything else is the owner's call.
  • Recover-to-zero, the browser's All joints to 0° press, which can arm.
  • Open-ended hold-to-run jogging. The jogs below are bounded per call.
  • Test-program upload, cell catalogue edits, program store delete, and every deploy command that builds, installs, restarts, locks, rolls back or reboots.

Why robot actions go through OLP only#

rt-control grants one controller at a time. OLP holds that grant for the browser and both pendants, runs the 5 s heartbeat deadman, and applies its maintenance, control and focus gates. A second grant taken by the MCP server would sit where OLP's STOP cannot reach. So the rt-core tools are reads, and every robot action is an OLP request. Each request carries the server's own X-Rosie-Client label and X-Rosie-Client-Id (mcp- and 16 hex digits, one per process), so OLP's journal and its maintenance overlay name the agent.

Jogging, bounded per call#

An agent has no hand on a trigger, so the jog tools take the browser's own requests and bound each call before OLP sees it, on top of OLP's and rt-core's limits. They are admitted like Arm and Play: a fresh status read, the mode's refusal and the read's target fence on every request. Arm first; session_id is the status's session and revision comes from the jog or Cartesian state.

ToolOLP requestBound per call (default)
rosie_olp_move_jointsPOST dense-execution/move, one joint or several, relative or absoluteAt most 30° per joint, absolute targets measured from the live position, inside the described range; speed fraction at most 0.5, default 0.1
rosie_olp_cartesian_stepPOST dense-execution/cartesian/move in base or tool frameAt most 100 mm or 15° (the browser's default step); speed fraction as above
rosie_olp_jog_holdJoint jog/begin, update every 50 ms, end; or Cartesian start, intent, haltAt most 3 s (default 1 s) at a fraction of at most 0.25 (default 0.1), with the travel still under the per-call caps

A move returns when OLP reports it done. A hold always sends End or Halt from a finally block, with the browser's end_cause. It sends Stop only after a hold that began and then failed to end cleanly: a refused press started nothing, and a Stop there would end the program that caused the refusal. OLP refuses every jog while a program plays or a loop runs (jog_mode_conflict), and the answer says which. The bounds are set by environment variables; see Configuration.

The heartbeat window#

OLP stops the robot when it holds the grant and no client heartbeat has landed for 5 s (ui_heartbeat_lost). Every motion tool starts heartbeats before its request is sent and keeps them up while it is in flight. After a successful answer, the server keeps heartbeating the session once a second for ROSIE_MCP_HEARTBEAT_WINDOW_S (default 120 s), and each later motion tool restarts the window.

A refused or failed request does not extend the window, so refusals keep nothing alive. The beats stop early on Stop or Disarm, or when OLP no longer holds a session. When the window ends or the server exits, OLP's deadman stops the robot unless an operator's browser or pendant is keeping the session alive. Loops started from the server are always attended, so they end with the window too.

Run program asks first#

rosie_olp_run_program mirrors the operator's confirmation dialog. The first call answers state: "confirm" with the action (for example Move to program start and run) and every joint's from, to and delta, and nothing moves. The agent shows that to you. Only when you agree does it call again with the confirm flags and the same joints. OLP refuses confirmation_stale if any joint has moved more than 0.1° since.

Programs: OLP's own engine#

A program is edited by OLP's TypeScript, bundled for Node exactly as the Steam Deck pendant bundles it. The agent edits the same spreadsheet the browser shows, with the same parameter specs, units, ranges and refusals, and plans through OLP's own /weld-plan. The rails weld search and accept logic moved into a shared module (offline-programming/v1/ui/src/rails/authoring.ts), so the browser and the server run the same code. Authoring and planning move nothing.

A typical session, with the rosie_author_weld_program prompt:

  1. rosie_program_new for the connected cell's robot, then rosie_program_import_step with the part's STEP file. The part is centred on the work frame and its seams are detected.
  2. rosie_program_place_part where the part sits in the cell.
  3. rosie_program_list_seams, then rosie_program_search_seam for each seam: clear, partial (with its clear runs) or blocked, with the session's torch and angle search.
  4. rosie_program_add_weld for the seams you approve, and rosie_program_view to show the spreadsheet.
  5. rosie_program_plan. A refusal comes back with OLP's code, sentence and row.
  6. rosie_program_save to the cell store (revision-checked; a conflict never overwrites) or to a file.

Taught programs work the same way: rosie_program_add_waypoint adds MoveJ, MoveL or MoveC waypoints from joint angles, rosie_program_record_robot_pose records the selected cell's measured pose without moving it, and rosie_program_add_event adds dwell, I/O and Home rows.

STEP bytes are never shared between clients. The server keeps every part and STEP fixture it imported under its fingerprint in ROSIE_MCP_STATE_DIR/sources (default ~/.rosie/mcp), and planned trajectories in .../dense, as the browser keeps them in IndexedDB. A program opened from the cell whose part this server never imported needs step_path once, checked against the program's fingerprint.

A plan carries the active part only: its welds and its STEP. Every other part goes to the planner as a mesh obstacle where it is placed, beside the workcell fixtures. The server authors rails and waypoint welds; the legacy posed_polyline weld is not offered.

Tools#

rt-core and OLP status#

ToolReadsNotes
rosie_rtcore_describert-control GET /v1/describeBackend, digests, axis limits, capabilities
rosie_rtcore_get_statusGET /v1/statusArmed, lease, faults and recovery classes, Home, per-axis alarms
rosie_rtcore_get_maintenanceGET /v1/maintenanceServo-host notice, grant holder
rosie_rtcore_list_eventsGET /v1/events?after=Paginated by sequence
rosie_olp_get_healthOLP /health, /capabilitiesSeam worker and motion origin reported separately
rosie_olp_get_machine_statusdense-execution/statusSIMULATION or REAL ROBOT, session, cursor, loop, axes, heartbeat keeper
rosie_olp_list_cellsdense-execution/cells, /targets
rosie_olp_get_control/maintenance, /controlWho controls the robot, connected clients
rosie_olp_list_cell_programs, rosie_olp_get_cell_programcell-programsRead only
rosie_olp_get_local_simulator_statuslocal-simulator/status

The rt-core tools need ROSIE_RT_CONTROL_SOCKET, or ROSIE_RT_CONTROL_URL with a client certificate for remote access.

Robot actions#

ToolModeNotes
rosie_olp_stop, rosie_olp_disarm, rosie_olp_pause, rosie_olp_finish_loopEvery modeEnd motion sooner
rosie_olp_select_cell, rosie_olp_arm, rosie_olp_load, rosie_olp_play, rosie_olp_run_programsimulation or cellLoad and Run program take from_open_program, the open program's current plan
rosie_olp_move_joints, rosie_olp_cartesian_step, rosie_olp_jog_holdsimulation or cellBounded per call

Programs#

ToolNotes
rosie_program_new, rosie_program_open, rosie_program_saveOpen and save from the cell store are revision-checked; a 409 never overwrites
rosie_program_view, rosie_program_get_rowThe spreadsheet; every field of a row with its path, unit and range
rosie_program_edit_rowAny field by OLP path, in display units. OLP refuses out-of-range and read-only values.
rosie_program_add_waypoint, rosie_program_reteach_waypointMoveJ, MoveL or MoveC from joint angles, with an optional Cartesian target and via poses
rosie_program_record_robot_poseNew, reteach or via waypoint from the selected cell's measured pose. Moves nothing; refused while the cell's program is loaded, staged, playing or paused.
rosie_program_robot_settingsShow, pull the cell's settings and calibration, adopt the current robot description
rosie_program_add_event, rosie_program_remove_row, rosie_program_move_row, rosie_program_enable_row, rosie_program_set_plan_speed, rosie_program_undoDwell, I/O and Home rows; OLP's reorder and delete rules
rosie_program_import_step, rosie_program_add_part, rosie_program_place_partThe part, centred, then placed in the work frame; a second import adds a part
rosie_program_list_parts, rosie_program_switch_part, rosie_program_remove_partWhich part is active (edited and planned)
rosie_program_list_fixtures, rosie_program_edit_fixtureStand box and STEP obstacles: add, edit, replace with STEP, cell default, remove
rosie_program_rails_settingsFitted torch and angle search: show, set a preset or values, reset
rosie_program_list_seams, rosie_program_search_seam, rosie_program_add_weldClear, partial (per-run welds) or blocked; accept or trim
rosie_program_set_weld_profileA rails weld's work or travel angle, constant or as points along the seam
rosie_program_add_waypoint_weld, rosie_program_edit_waypoint_weldWaypoint, line and arc welds from torch poses in the part frame
rosie_program_plan, rosie_program_export_weldplanPOST /weld-plan and /weld-plan/export. Starts from the measured pose when a cell is selected, or from the program start.
rosie_program_preview_pathThe planned TCP path per segment through OLP's forward kinematics (mm, chord deviation, peak speed). Moves nothing.

Motion servers, planner, repository and deploy#

ToolReads or runsNotes
rosie_dense_daemon_statusDaemon TCP statusInert
rosie_dense_daemon_validateDaemon TCP validateThe upload's dry run; nothing stored
rosie_kinematics_forwardrobot-v4-cartesiand --resolve-only, fkComputation only
rosie_kinematics_cartesian_line--resolve-only, moveJoint waypoints of a straight TCP move, or the refusal
rosie_planner_get_healthPlanner /api/motion/health, /progress
rosie_planner_plan_weldplanPOST /api/motion/planFull result saved to a file, summary returned
rosie_planner_fetch_denseGET /api/motion/dense/{digest}Saved as .rdt and inspected
rosie_list_cells, rosie_get_cell_manifestrt-core/config/cellsThe committed pins
rosie_lookup_drive_alarmThe A6-EC alarm tableWhether an rt-core reset clears the alarm
rosie_inspect_dense_trajectoryA local .rdtEvery ingest rule, digest and identity (format)
rosie_search_docs22 component documentsfile:line hits with headings
rosie_deploy_inspectgo run . in steamdeck/real/v4Read-only: release plan, OLP status, precheck, verify, config dry run, maintenance notice

Resources and prompts#

ResourceContents
rosieos://docs/<id>The same 22 component documents rosie_search_docs searches
rosieos://cells/<cell>A cell manifest with a summary
rosieos://mcp/configEndpoints, robot-action mode and heartbeat state in effect
PromptWhat it asks the agent to do
rosie_diagnose_cellRead every layer in order (control, machine status, rt-core status and events, drive alarms, planner and daemon) and explain the cell's state, citing a code and a document for each claim. Acts on nothing.
rosie_author_weld_programNew program from a STEP file, place the part, search and accept the seams you approve, plan, and save only when you say so
rosie_run_program_in_simulationConfirm the target is a simulation, then take a plan through Run program with your confirmation. With robot actions off, it stops after the checks.
rosie_review_releaseRead-only: a commit's release plan for a cell, the cell's state, and the command the owner would run

Every prompt carries the same rules: never re-home to clear a fault, never route around a drive or hardware failure, a non-resettable drive alarm is the owner's call, deploys are the owner's, and a Stop receipt is not proof of standstill.

Configuration#

VariableDefaultMeaning
ROSIE_OLP_URLhttp://127.0.0.1:8794OLP API. Against a cell from a Mac: http://127.0.0.1:18894, the olp ui start tunnel.
ROSIE_PLANNER_URLhttp://127.0.0.1:8796Weld planner
ROSIE_DENSE_DAEMON_ADDR127.0.0.1:8797Dense daemon ingest
ROSIE_RT_CONTROL_SOCKETunsetrt-control's control.sock, for the rt-core tools. Paths are in Unix sockets.
ROSIE_RT_CONTROL_URL, ROSIE_RT_CONTROL_CA, ROSIE_RT_CONTROL_CERT, ROSIE_RT_CONTROL_KEYunsetThe mutual-TLS listener instead of the socket
ROSIE_CARTESIAND_COMMANDThe repository's robot-v4-cartesiand, if builtA path, or a JSON argv prefix (below)
ROSIE_CARTESIAND_REPO_ROOTThe repository rootThe root as robot-v4-cartesiand sees it (a WSL path under WSL)
ROSIE_MCP_ROBOT_ACTIONSoffoff, simulation or cell. See Robot actions.
ROSIE_MCP_HEARTBEAT_WINDOW_S120Heartbeat window after a robot action, 5 to 3600 s
ROSIE_MCP_JOG_MAX_DEG, ROSIE_MCP_CARTESIAN_MAX_MM, ROSIE_MCP_CARTESIAN_MAX_DEG30, 100, 15Most travel per call for a joint move or hold, and a Cartesian step or hold
ROSIE_MCP_JOG_MAX_FRACTION, ROSIE_MCP_JOG_HOLD_MAX_S, ROSIE_MCP_JOG_HOLD_MAX_FRACTION0.5, 3, 0.25Largest move or step speed fraction, hold duration and hold speed fraction
ROSIE_MCP_STATE_DIR~/.rosie/mcpImported STEP parts (sources/) and planned trajectories (dense/)
ROSIE_REPO_ROOTFound from the install pathThe RosieOS checkout the repository tools read
ROSIE_GOgoGo binary for rosie_deploy_inspect
ROSIE_MCP_CLIENT_LABELrosieos-mcp-server (<host>)The X-Rosie-Client label OLP shows
ROSIE_MCP_REQUEST_TIMEOUT_MS15000Per-request timeout

An invalid value stops the server at start with a message naming the variable and its allowed range.

On Windows, the kinematics tools can run robot-v4-cartesiand inside WSL. Give the command as a JSON argv prefix, and the repository root as WSL sees it:

ROSIE_CARTESIAND_COMMAND='["wsl","-e","/home/me/RosieOS/motion-server/v1/bin/robot-v4-cartesiand"]'
ROSIE_CARTESIAND_REPO_ROOT=/home/me/RosieOS

Build and test#

cd mcp/v1
npm test          # builds the engine bundle and the server, then runs 52 tests: no network, no robot
npm run typecheck # the server, and its engine entry against OLP's own sources

The tests drive the server through the MCP SDK's own client. Their fakes sit at the process boundary: OLP over HTTP, rt-control over a Unix socket (a named pipe on Windows) and the daemon over TCP. The offline tools run against the checkout's committed files. The mcp-server.yml workflow runs the typecheck and the tests on every change to the server, OLP's UI sources, the pendant's program core, the dense trajectory test data and the cell and drive configuration.

Maturity#

The server is version 0.1.0.

  • Tested live, read-only and authoring: against a locally running OLP and weld planner, robot actions off. Health, machine status, cells and planner health answered. A taught program was created, edited and planned on the planner's GPU-less stand-in, and its trajectory passed every ingest rule. A STEP part was imported with its seams detected, a seam searched and accepted as a weld, a fixture and a second part added, and the .weldplan exported.
  • Not yet run live: a weld plan on the CUDA planner, a save to a live cell store, and every robot action and jog. Those are tested against fakes only. The first live run is the cell owner's.
  • Not reproduced: the browser's edge clicks (seams are chosen by id), the Steam Deck pendant's focus headers, and torch presets saved in a browser (give their values instead).