Skip to content

robot-md-mcp — v0.1 design

Status: Approved design, pre-implementation Scope: First shippable version of the Claude Desktop MCP server for ROBOT.md Complements: v0.2-design.md (signing / registry ingestion) Last updated: 2026-04-17


0. Preamble

robot-md-mcp is the Model Context Protocol server that exposes a ROBOT.md to Claude Desktop (and any other MCP-speaking client). This document defines v0.1 — the first release. The shape is deliberately scoped down from the ambitious sketch in integrations/claude-desktop/README.md so we can ship without pre-committing to API contracts that v0.2 signing will re-shape.

What ships here does not write to the robot. It reads the manifest and exposes it, plus two local-only tools (validate, render). Any dispatch to a running robot gateway — invoke_skill, query_status, anything with a signed-command story — is deferred to the post-§13 release and will be designed in a companion doc once the crypto decisions in v0.2-design.md §13 are finalized.

1. Goals and non-goals

Goals

  1. A Claude Desktop user whose robot ships a ROBOT.md can add one MCP config entry and have Claude understand what the robot is.
  2. The same server works with any MCP client (Zed, Cline, Continue, custom).
  3. Zero coupling to the v0.2 signing design — no resource or tool in this release makes a claim that signing semantics will invalidate.
  4. Parity with the Python CLI's validate and render verbs without requiring Python on the user's machine.

Non-goals

  • Robot-dispatch tools (invoke_skill, query_status, etc.) — v0.2.
  • Signature verification — v0.2.
  • Multi-manifest servers (fleet view) — post-v0.2.
  • Hot-reload via filesystem watcher — manual re-read per call is fine for small files.
  • Auto-discovery of ROBOT.md in CWD / home — the MCP config passes the path explicitly.

2. Decisions, recorded

# Decision Alternatives considered Why
1 Scope: resources + local tools (no dispatch) (A) resources-only, (C) full v0.2 vision (C) pre-commits API shape that §13 crypto may require us to change; (A) leaves the server without any tools, which is a valid but weaker story
2 Language/runtime: TypeScript, new repo (1) integrate into Python CLI, (2) separate Python package Matches MCP ecosystem norm; uses tsup/vitest tooling we already run for rcan-ts; no Python runtime burden on Claude Desktop users
3 Parser: in-house yaml + ajv port Python parser, shell out to Python CLI JSON schema is the single source of truth for "valid"; duplicated parsing logic is not load-bearing
4 Distribution: npm + GitHub release tarballs GitHub only, npm only npm is idiomatic; GH release tarball is the bridge while NPM_TOKEN is unresolved

3. Repo + package

  • Repo: github.com/RobotRegistryFoundation/robot-md-mcp (sibling to robot-md).
  • License: Apache-2.0 (matches robot-md).
  • npm name: robot-md-mcp, unscoped. (Rationale: matches rcan-ts; @RobotRegistryFoundation org on npm isn't claimed and we explicitly don't want to take on that operational surface yet.)
  • Toolchain:
  • tsup for build (CJS + ESM outputs, plus a bin entrypoint)
  • vitest for tests
  • strict: true TypeScript, Node 18+
  • ajv for JSON-schema validation (draft 2020-12)
  • yaml for frontmatter parsing
  • @modelcontextprotocol/sdk for MCP wire protocol
  • Repo layout:
robot-md-mcp/
├─ src/
│  ├─ index.ts                 # library exports (parseRobotMd, etc.)
│  ├─ parser.ts                # parseRobotMd(text) → ParsedRobotMd
│  ├─ validate.ts              # validateParsed(parsed) → ValidateResult
│  ├─ render.ts                # renderYaml(parsed) → string
│  ├─ server.ts                # MCP server wiring (resources + tools)
│  ├─ bin.ts                   # CLI entrypoint — parse argv, start server
│  └─ schema/
│     └─ robot.schema.json     # bundled copy, kept in sync via script
├─ tests/
│  ├─ parser.test.ts
│  ├─ validate.test.ts
│  ├─ render.test.ts
│  ├─ server.test.ts           # in-process MCP round-trip
│  └─ fixtures/                # copy of key fixtures from robot-md
├─ scripts/
│  └─ sync-schema.mjs          # pulls schema from ../robot-md or URL
├─ .github/workflows/
│  ├─ ci.yml                   # build + test + schema-sync-check
│  └─ release.yml              # tag → GH release; dispatch → npm
├─ package.json
├─ tsconfig.json
└─ README.md

4. Parser + validation

parseRobotMd(text: string): ParsedRobotMd — single entry point.

interface ParsedRobotMd {
  frontmatter: Record<string, unknown>;  // parsed YAML object
  body: string;                          // raw markdown after ---
  rawText: string;                       // the full input
}
  • Splits on the first pair of --- delimiters (same rule the Python parser uses). Throws ParseError if no frontmatter found.
  • yaml.parse with default options (which handle comments — important because autodetect-emitted drafts carry YAML comments).
  • Body is kept verbatim — the MCP server surfaces it as raw markdown.

validateParsed(parsed): ValidateResult returns:

interface ValidateResult {
  ok: boolean;
  summary: string;        // e.g. "bob (arm+camera, 6 DoF, 5 capabilities)"
  errors: string[];       // human-readable messages
}

Under the hood: ajv against the bundled schema + body checks (H1 matches metadata.robot_name, required sections present). Mirrors the Python validator's behavior.

Schema is a byte-for-byte copy of robot-md/schema/v1/robot.schema.json. scripts/sync-schema.mjs refreshes it; a CI step (schema-sync-check) fails the build if the copy has drifted. Follows the same pattern robot-md already uses for its Python bundle and the site-served copy.

5. MCP server wiring

  • Entrypoint: robot-md-mcp <path>. <path> is a required positional argument. No env var, no auto-discovery. Claude Desktop's MCP config line is the single source of truth for which file is served.

  • Example ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "robot-md": {
      "command": "npx",
      "args": ["-y", "robot-md-mcp", "/path/to/ROBOT.md"]
    }
  }
}
  • On startup: read + parse the file once to determine metadata.robot_name (used in resource URIs). Fail fast with a clear stderr message if the file is missing or unparseable. Do not refuse to start on schema-violation — that would hide a broken manifest; instead, serve it and let the validate tool surface the errors.

  • On each resource/tool call: re-read and re-parse the file. Files are tiny (<10 KB in practice); the simplicity is worth the extra disk read. No FS watcher, no cache-invalidation logic.

6. Resources

Resource URIs use robot-md://<robot_name>/<kind> where <robot_name> is metadata.robot_name from startup.

URI mimeType Payload
robot-md://<name>/frontmatter application/json entire parsed frontmatter object
robot-md://<name>/capabilities application/json frontmatter.capabilities array (JSON-stringified)
robot-md://<name>/safety application/json frontmatter.safety object (JSON-stringified)
robot-md://<name>/body text/markdown prose body verbatim

resources/list returns all four. resources/read returns the current contents (post-reload).

Design choice: the resources are flat and typed, not nested. A consumer can fetch /capabilities without paying for the whole manifest. Keeps MCP traffic small.

7. Tools

Name Args Returns
validate {} { ok: boolean, summary: string, errors: string[] }
render {} { yaml: string } — canonical YAML of the frontmatter

Both tools are pure, read-only, and operate on the already-known file path. No argument means no chance of misuse against a different file than the operator configured.

Explicitly not exposed in v0.1:

  • invoke_skill(skill_name, params) — deferred to post-§13.
  • query_status() — same.
  • refresh() — unnecessary since every call re-reads.
  • set_path(path) — security footgun; operator owns the MCP config.

8. Error surfaces

Scenario Behavior
File path missing at startup stderr message + non-zero exit; MCP client shows server as failed
File unreadable (permissions) same as above
File has no frontmatter same — startup fails; robot_name is required to namespace resources
File has invalid frontmatter YAML same
File has valid YAML but schema violations server starts; validate tool surfaces the errors; resources still serve the parsed (possibly-invalid) content
File is deleted or modified after startup next resource/tool call re-reads and either serves fresh content or errors (with a clear "file no longer readable" message)

Principle: fail at startup for structural problems, surface content problems through the validate tool. Operators can fix the file and re-run; consumers (Claude) can read the validation errors and explain them.

9. Testing

  • Unit tests (vitest) covering:
  • parseRobotMd on valid fixtures (minimal, bob, so-arm101, turtlebot4 — copied from the Python test fixtures)
  • parseRobotMd on malformed inputs (no frontmatter, bad YAML, mismatched H1)
  • validateParsed on valid + each category of invalid fixture
  • renderYaml round-trip behavior
  • One integration test that:
  • Spawns server.ts with an in-process MCP transport from the SDK
  • Calls resources/list, resources/read, tools/call validate, tools/call render
  • Asserts the returned payloads match expected shapes
  • CI matrix: Node 18, 20, 22.
  • schema-sync-check CI step: MD5 compare the bundled schema against a freshly-fetched canonical copy; fail if they differ.

10. Distribution

  • Release workflow follows the rcan-ts pattern we established:
  • Tag push (v*.*.*) → build-and-test job → github-release job that attaches the .tgz to the GitHub release.
  • workflow_dispatch with publish_npm=truenpm-publish job that uses NPM_TOKEN.
  • v0.1.0 ships as a GitHub release with tarball attached; npm publish runs once NPM_TOKEN is configured (tracked separately — not this design's concern).
  • README install sections document both paths:
# once on npm
npx robot-md-mcp /path/to/ROBOT.md

# during the npm-blocked window
npm i github:RobotRegistryFoundation/robot-md-mcp#v0.1.0

11. Coupling with the v0.2 signing design

This release is strictly a reader. It makes zero claims about authenticity, so it takes no dependency on v0.2-design.md §13.

Post-§13 changes the server will grow:

  • A new resource: robot-md://<name>/signature (or /envelope) — once detached .sig semantics land.
  • A new tool: verify — returns signature-validity + key-fingerprint
  • manifest_version.
  • New tools: invoke_skill, query_status — but only once the signed-command wire shape is frozen. The design of those tools is explicitly out of scope for this document.

The v0.1 resource and tool URIs are stable and will not change when those additions arrive.

12. Open items (tracked for implementation, not blocking)

  • Package-signing / provenance: npm supports --provenance publish which records a signed attestation tied to the GH Actions workflow. Low-cost to add; do it in v0.1 release if the npm publish step lands cleanly.
  • README example using autodetect: once a v0.1 release is live, a two-line README recipe — robot-md autodetect --write ROBOT.md then npx robot-md-mcp ROBOT.md — closes the Tier 0 60-second loop in a way a new operator can follow end-to-end.
  • Schema fetching fallback: if scripts/sync-schema.mjs should fetch from https://robotmd.dev/schema/v1/robot.schema.json as a fallback when the repo-relative path is unavailable. Minor.

13. Implementation plan (deferred to writing-plans)

This design is the input to a separate implementation plan (writing-plans skill). The plan will sequence:

  1. Repo scaffold + toolchain
  2. Parser + validator + render (pure functions, tests first)
  3. MCP server wiring + tools
  4. Fixtures + integration test
  5. CI workflows + schema-sync check
  6. README + release v0.1.0 (GH release only)
  7. Post-release: enable npm publish once NPM_TOKEN is sorted

Each step is an independent PR with its own review gate.