@trent105-unnc/mir_mcp
v0.0.2
Published
MCP server exposing the RAWSim-O warehouse simulation (status / control / task injection / backend switch) to AI agents
Maintainers
Readme
mir_mcp
An MCP (Model Context Protocol) server that exposes the RAWSim-O warehouse simulation to AI agents (Claude Code, Cursor, VS Code Copilot, etc.) — the same control surface the mir_fe dashboard uses. It lets an agent read live system state, launch a suitable scenario, inject tasks, and switch the simulation backend.
Supports both transports:
- Streamable HTTP (
/mcp, protocol 2025-03-26) — Claude Code / Cursor / newer clients. - SSE (
/sse+/messages, protocol 2024-11-05) — older clients. - stdio — local subprocess integration.
What It Does
Provides 13 MCP tools that let an AI assistant observe and drive a RAWSim-O warehouse:
- Read: live status, rich per-bot metadata, a full positional scene snapshot, the current backend mode.
- Scenario: pick a named instance from a bundled catalog (12 layouts, from 15-bot pickup-heavy to 300-AGV scale tests), start/stop/pause/resume, set internal-mode speed.
- Tasks: append orders at runtime (line items, station pinning, scheduling, high-priority dispatch).
- Backend: switch between RAWSim-O internal kinematics and Isaac Lab external physics.
Configuration
| Env var | Default | Meaning |
| ---------- | ---------- | --------- |
| RAWSIMO_URL | http://rawsimo:8880 | Base URL of the RAWSim-O web server. |
| MCP_TRANSPORT | stdio | stdio · http · all (run both). |
| PORT | 3045 | HTTP listen port (http transport). |
| MODEL_BASE_URL | — | OpenAI-compatible chat completions base URL (enables /api/chat). |
| MODEL_API_KEY | — | Bearer key for the model API. |
| MODEL_NAME | — | Model id (e.g. gpt-4o-mini). |
| CHAT_CORS_ORIGIN | * | Allowed origin for /api/chat (for browser frontends). |
Chat endpoint (/api/chat)
When MODEL_BASE_URL + MODEL_API_KEY + MODEL_NAME are set, mir_mcp also serves
POST /api/chat — a streaming (SSE) LLM agent that drives the warehouse through mir_mcp's own
tools (in-process). Each event is a data: {json}\n\n line:
| event type | fields | meaning |
| --- | --- | --- |
| delta | text | a piece of the assistant's reply (streamed token) |
| tool | id, name, args | the agent is calling a mir_mcp tool |
| tool_result | id, name, result, isError | the tool's output |
| done | — | turn finished |
| error | message | failure |
Request body: { "messages": [{ "role": "user"\|"assistant", "content": "..." }] }. Example:
curl -N -X POST http://localhost:3045/api/chat -H 'content-type: application/json' \
-d '{"messages":[{"role":"user","content":"what is the warehouse status?"}]}'This is what the mir_fe "AI Assistant" panel (under Scheduling simulator) talks to. When the
model env vars are unset, /api/chat returns 503 and mir_mcp behaves as a plain MCP server.
IDE Configuration
Option A: npx (no install)
Once published to npm, anyone can run it with no local checkout. Set RAWSIMO_URL to a reachable
RAWSim-O server (it plays the same role as OBS_WEBSOCKET_PASSWORD in other MCP servers).
Claude Code
Project — add to .mcp.json:
{
"mcpServers": {
"mir": {
"command": "npx",
"args": ["-y", "@trent105-unnc/mir_mcp@latest"],
"env": { "RAWSIMO_URL": "http://localhost:8880" }
}
}
}Global — run once via CLI:
claude mcp add --scope user mir -- npx -y @trent105-unnc/mir_mcp@latest
# then set the env var via the generated config, or prefix: RAWSIMO_URL=http://localhost:8880VSCode + GitHub Copilot
Add to .vscode/mcp.json:
{
"servers": {
"mir": {
"type": "local",
"command": "npx",
"args": ["-y", "@trent105-unnc/mir_mcp@latest"],
"env": { "RAWSIMO_URL": "http://localhost:8880" }
}
}
}Cursor
Settings → MCP → Add new MCP server → type stdio, command npx, args -y @trent105-unnc/mir_mcp@latest, env
RAWSIMO_URL=http://localhost:8880.
Publishing: this option works once
@trent105-unnc/mir_mcpis on npm. Tag a release (git tag v0.1.0 && git push --tags) and thePublish to npmworkflow publishes it to the@trent105-unncorg (theNPM_TOKENaccount must be a member of that npm org with publish rights; scoped public packages needaccess: public, already set inpublishConfig).
Option B: Local Docker (HTTP)
docker compose up -d --build
# MCP endpoint at http://localhost:3045/mcp (and /sse)Claude Code
Project — add to .mcp.json in the project root:
{
"mcpServers": {
"mir": {
"type": "http",
"url": "http://localhost:3045/mcp"
}
}
}Global — run once via CLI:
claude mcp add --scope user --transport http mir http://localhost:3045/mcpVSCode + GitHub Copilot
Add to .vscode/mcp.json:
{
"servers": {
"mir": {
"type": "http",
"url": "http://localhost:3045/mcp"
}
}
}Cursor
Settings → MCP → Add: http://localhost:3045/mcp.
Option C: Local Node.js
pnpm install
pnpm build
# HTTP + SSE:
MCP_TRANSPORT=http PORT=3045 RAWSIMO_URL=http://rawsimo:8880 pnpm start
# or stdio (Claude Code launches the server as a subprocess):
RAWSIMO_URL=http://rawsimo:8880 node dist/index.jsClaude Code (stdio)
Add to .mcp.json:
{
"mcpServers": {
"mir": {
"command": "node",
"args": ["/absolute/path/to/mir_mcp/dist/index.js"],
"env": { "RAWSIMO_URL": "http://rawsimo:8880" }
}
}
}MCP Tools
| Tool | Parameters | Purpose |
| ---------- | ---------- | --------- |
| get_health | — | RAWSim-O liveness. |
| get_simulation_status | — | Running? sim time, error, available item descriptions. |
| get_test_metadata | — | Counts (AGV/pod/station/waypoint/orders), idle/busy, per-bot state. |
| get_live_snapshot | — | One full-scene frame: every bot/pod/station/waypoint + positions. |
| get_sim_backend | — | Current backend mode (internal/external). |
| list_instances | — | Named scenario bundles with metadata (AGV/station counts, workload tags). |
| start_instance | name, seed?, tag? | Start a sim from a named bundle. |
| stop_simulation | — | Stop + finalize + write stats. |
| pause_simulation / resume_simulation | — | Pause / resume. |
| set_speed | speed_multiplier | Internal-mode speed multiplier (1–400). |
| append_tasks | tasks[], high_priority? | Add orders at runtime. |
| set_sim_backend | backend | Switch backend; reports the confirmed mode. |
Picking a "suitable instance" from semantics
RAWSim-O has no instance-catalog endpoint, so mir_mcp ships the same 12 named bundles mir_fe ships (under data/). The agent calls list_instances, reads the metadata (e.g. scale-300 → 300 AGV, pickup-5x → pickup-heavy 15-bot), and starts the best match with start_instance.
Development
pnpm install # install dependencies
pnpm typecheck # tsc --noEmit
pnpm build # tsup bundle -> dist/index.js
pnpm test # vitest (catalog + tool registration smoke test)
pnpm dev # tsx watch src/index.tsArchitecture
src/
index.ts # entry: transport selection (MCP_TRANSPORT=stdio|http|all)
server.ts # createServer(client) -> McpServer + registerAllTools
rawsimo-client.ts # typed fetch client for the 14 simulation/ endpoints
config-catalog.ts # 12 named instance bundles + metadata + XML/zip loader
types.ts # RAWSim-O DTO shapes (snake_case wire format)
transports/
stdio.ts # StdioServerTransport
http.ts # SSE (/sse) + Streamable HTTP (/mcp) via createMcpExpressApp
tools/
_shared.ts # text/err/run result helpers
status.ts # read tools
scenario.ts # lifecycle + instance tools
tasks.ts # append_tasks
backend.ts # set_sim_backend
index.ts # registerAllTools
data/ # bundled sim configs (copied from mir_fe)
default_sim_config/
test_sim_config/
tests/smoke.ts # vitest catalog + tool-registration testsAll RAWSim-O endpoints are under /simulation/<Action> and use snake_case JSON. The HTTP transport reads the raw body itself — do not add an express.json() middleware (it starves the transport). See ~/RAWSim-O/RAWSimO.WebServer/SimulationHost/Controllers/SimulationHostController.cs for the authoritative endpoint source.
