From feaca4b37430840990d90d54343b641cef27c4a0 Mon Sep 17 00:00:00 2001 From: Craig M Date: Thu, 12 Nov 2026 12:26:20 -0800 Subject: [PATCH] robot-md: start the MCP server through the shipped launcher Syncs robot-md-mcp#18: .mcp.json runs python3 ${CLAUDE_PLUGIN_ROOT}/scripts/start-mcp-server.py, which the plugin directory's checks can read, instead of a bare robot-md command they hold for manual review. Adds the launcher to the sync list, and the sync script now creates parent folders for single-file copies. Plugin 0.6.1. The sync check fails until robot-md-mcp#18 is merged, because it compares against robot-md-mcp main. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LkzLwZG62ywkcaNNrw7DxP --- plugins/robot-md/.claude-plugin/plugin.json | 2 +- plugins/robot-md/.mcp.json | 4 +-- plugins/robot-md/README.md | 4 +-- plugins/robot-md/scripts/start-mcp-server.py | 37 ++++++++++++++++++++ scripts/sync-robot-md.sh | 2 ++ 5 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 plugins/robot-md/scripts/start-mcp-server.py diff --git a/plugins/robot-md/.claude-plugin/plugin.json b/plugins/robot-md/.claude-plugin/plugin.json index 6bde01e..87bca04 100644 --- a/plugins/robot-md/.claude-plugin/plugin.json +++ b/plugins/robot-md/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "robot-md", "displayName": "robot-md", - "version": "0.6.0", + "version": "0.6.1", "description": "Connect a robot to Claude through its ROBOT.md: read capabilities and safety limits, drive it through the robot-md MCP server, keep AI-driven motion behind a gate and test its limits with Bounded Embodiment, and manage its identity, keys and EU AI Act evidence at the Robot Registry Foundation. Requires `pip install 'robot-md[hardware]'`.", "author": { "name": "Robot Registry Foundation", diff --git a/plugins/robot-md/.mcp.json b/plugins/robot-md/.mcp.json index 485c07c..11a505f 100644 --- a/plugins/robot-md/.mcp.json +++ b/plugins/robot-md/.mcp.json @@ -1,6 +1,6 @@ { "robot-md": { - "command": "robot-md", - "args": ["mcp"] + "command": "python3", + "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/start-mcp-server.py"] } } diff --git a/plugins/robot-md/README.md b/plugins/robot-md/README.md index 770e923..29f2948 100644 --- a/plugins/robot-md/README.md +++ b/plugins/robot-md/README.md @@ -8,7 +8,7 @@ The robot-md plugin connects a robot to Claude through the `ROBOT.md` at the roo | `bounded-embodiment` skill | Keeps AI-driven motion behind a gate, drafts envelopes, rehearses EV-03 and EV-10 in simulation, reads verdicts honestly | [plugins/bounded-embodiment](../bounded-embodiment/skills/bounded-embodiment) | | `rrf-registry` skill | Registers the robot, rotates and revokes keys, proves manufacturer identity, emits and submits EU AI Act evidence | this plugin | | `/robot-md:enable-dispatch` | Writes dispatcher config and bearer tokens without printing the token into the conversation | [robot-md-mcp](https://github.com/RobotRegistryFoundation/robot-md-mcp/tree/main/commands) | -| `robot-md` MCP server | Manifest reads plus motion (`execute_task`, `execute_capability`, `vision_find`, `estop`), served by `robot-md mcp` | [robot-md](https://github.com/RobotRegistryFoundation/robot-md) (PyPI) | +| `robot-md` MCP server | Manifest reads plus motion (`execute_task`, `execute_capability`, `vision_find`, `estop`). `scripts/start-mcp-server.py` finds your installed `robot-md` CLI and runs `robot-md mcp` | [robot-md](https://github.com/RobotRegistryFoundation/robot-md) (PyPI) | ## Install @@ -34,4 +34,4 @@ The MCP server runs locally and sends nothing to the Robot Registry Foundation. ## Keeping copies in sync -The `using-robot-md`, `bounded-embodiment` and `enable-dispatch` files here are copies. Edit the canonical source, then run `scripts/sync-robot-md.sh` from the repository root. CI fails when a copy drifts. +The `using-robot-md`, `bounded-embodiment` and `enable-dispatch` files, `.mcp.json` and `scripts/start-mcp-server.py` here are copies. Edit the canonical source, then run `scripts/sync-robot-md.sh` from the repository root. CI fails when a copy drifts. diff --git a/plugins/robot-md/scripts/start-mcp-server.py b/plugins/robot-md/scripts/start-mcp-server.py new file mode 100644 index 0000000..42c0d51 --- /dev/null +++ b/plugins/robot-md/scripts/start-mcp-server.py @@ -0,0 +1,37 @@ +"""Start the robot-md MCP server for the Claude Code plugin. + +The plugin's .mcp.json runs this file with python3 instead of calling the +`robot-md` command directly, so the plugin directory's checks can read +exactly what the server command does: find the `robot-md` CLI the user +installed (`pip install 'robot-md[hardware]'`) and run `robot-md mcp` +with this process's stdin and stdout. Nothing is downloaded or installed. +""" + +import os +import shutil +import subprocess +import sys + +INSTALL_HINT = ( + "robot-md MCP server: the `robot-md` command was not found on PATH.\n" + "Install it with: python3 -m pip install --user 'robot-md[hardware]'\n" + "then restart Claude Code. Docs: https://docs.robotmd.dev/mcp/\n" +) + + +def main() -> int: + exe = shutil.which("robot-md") + if exe is None: + sys.stderr.write(INSTALL_HINT) + return 1 + argv = [exe, "mcp", *sys.argv[1:]] + if os.name == "nt": + # os.execv on Windows starts a new process and exits this one, + # which breaks the stdio pipe Claude Code holds. Run it as a child. + return subprocess.call(argv) + os.execv(exe, argv) + return 0 # not reached + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/sync-robot-md.sh b/scripts/sync-robot-md.sh index 46fd84b..21745d6 100755 --- a/scripts/sync-robot-md.sh +++ b/scripts/sync-robot-md.sh @@ -16,6 +16,7 @@ pairs=( "$work/robot-md-mcp/skills/using-robot-md|$DEST/skills/using-robot-md" "$work/robot-md-mcp/commands/enable-dispatch.md|$DEST/commands/enable-dispatch.md" "$work/robot-md-mcp/.mcp.json|$DEST/.mcp.json" + "$work/robot-md-mcp/scripts/start-mcp-server.py|$DEST/scripts/start-mcp-server.py" "plugins/bounded-embodiment/skills/bounded-embodiment|$DEST/skills/bounded-embodiment" ) @@ -30,6 +31,7 @@ for pair in "${pairs[@]}"; do fi else rm -rf "$dst" + mkdir -p "$(dirname "$dst")" cp -r "$src" "$dst" echo "synced $dst" fi