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¶
- A Claude Desktop user whose robot ships a
ROBOT.mdcan add one MCP config entry and have Claude understand what the robot is. - The same server works with any MCP client (Zed, Cline, Continue, custom).
- Zero coupling to the v0.2 signing design — no resource or tool in this release makes a claim that signing semantics will invalidate.
- Parity with the Python CLI's
validateandrenderverbs 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.mdin 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 torobot-md). - License: Apache-2.0 (matches
robot-md). - npm name:
robot-md-mcp, unscoped. (Rationale: matchesrcan-ts;@RobotRegistryFoundationorg on npm isn't claimed and we explicitly don't want to take on that operational surface yet.) - Toolchain:
tsupfor build (CJS + ESM outputs, plus a bin entrypoint)vitestfor testsstrict: trueTypeScript, Node 18+ajvfor JSON-schema validation (draft 2020-12)yamlfor frontmatter parsing@modelcontextprotocol/sdkfor 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). ThrowsParseErrorif no frontmatter found. yaml.parsewith default options (which handle comments — important becauseautodetect-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 thevalidatetool 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: parseRobotMdon valid fixtures (minimal, bob, so-arm101, turtlebot4 — copied from the Python test fixtures)parseRobotMdon malformed inputs (no frontmatter, bad YAML, mismatched H1)validateParsedon valid + each category of invalid fixturerenderYamlround-trip behavior- One integration test that:
- Spawns
server.tswith 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-checkCI step: MD5 compare the bundled schema against a freshly-fetched canonical copy; fail if they differ.
10. Distribution¶
- Release workflow follows the
rcan-tspattern we established: - Tag push (
v*.*.*) →build-and-testjob →github-releasejob that attaches the.tgzto the GitHub release. workflow_dispatchwithpublish_npm=true→npm-publishjob that usesNPM_TOKEN.- v0.1.0 ships as a GitHub release with tarball attached; npm
publish runs once
NPM_TOKENis 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.sigsemantics 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
--provenancepublish 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.mdthennpx 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.mjsshould fetch fromhttps://robotmd.dev/schema/v1/robot.schema.jsonas 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:
- Repo scaffold + toolchain
- Parser + validator + render (pure functions, tests first)
- MCP server wiring + tools
- Fixtures + integration test
- CI workflows + schema-sync check
- README + release v0.1.0 (GH release only)
- Post-release: enable npm publish once
NPM_TOKENis sorted
Each step is an independent PR with its own review gate.