Skip to content

The MCP Server

reef-mcp — a TypeScript MCP server. Six tools. Runs via npx @semanticintent/reef-mcp (or node dist/index.js) from Claude Desktop or Claude Code.

The MCP does not interpret, summarize, or visualize — Claude does all of that. The MCP's job is to surface the right slice of TRACE when Claude needs it, write events reliably, execute arms with structural safety, and author new arms and workflows on the fly.


read_trace

The main retrieval tool. Returns matching TRACE events as NDJSON.

read_trace(
  source?:     string,   // "outlook.inbox", "git.*", "teams.planner"
  since?:      string,   // "2026-05-01", "last-week", "3-months-ago"
  until?:      string,   // "2026-06-01", "today"
  event_type?: string,   // "ARM_COMPLETE", "HUMAN_DECIDED"
  run_id?:     string    // a specific run
) → TraceEvent[]

Any combination of parameters. Returns everything that matches in chronological order. Claude decides what to do with it.


write_trace

The canonical write path. Appends a single TRACE event, atomically (lockfile-protected — safe under concurrent writes).

write_trace(
  run_id:      string,
  event_type:  string,   // one of the 12-event vocabulary
  source:      string,
  payload:     object,
  trace_id?:   string,   // generated if omitted
  timestamp?:  string    // generated if omitted
) → { trace_id, timestamp }

run_arm

Executes a .cs arm via dotnet-script and records ARM_START / ARM_COMPLETE / ARM_FAIL unconditionally as TRACE events.

run_arm(
  arm:         string,    // "REPO-STATUS", "FLARE"
  run_id:      string,
  arm_path?:   string,    // absolute path override
  args?:       object,    // forwarded to the arm as JSON on stdin
  timeout_ms?: number,    // default 60000
  confirmed?:  boolean    // required for ACT-mode arms — see below
) → { event_type, exit_code, duration_ms, stdout, trace_id }

The gate is structural, not a suggestion. Before spawning anything, run_arm reads the arm's own declared MODE (see below). READ arms run immediately, same as always. ACT arms — or arms with no declared MODE at all, which defaults to the safer assumption — do not execute without confirmed: true on the call. Without it, run_arm writes a PAUSE_SURFACED TRACE event and returns a preview instead of running anything. Call it again with confirmed: true and the same run_id, and it writes HUMAN_DECIDED, then actually runs it. Even if a client's own permission prompt were somehow bypassed, the arm still doesn't execute without that second, explicit call.


write_arm

Authors or updates a .cs arm from Claude-generated source and saves it.

write_arm(
  arm:        string,   // "REPO-STATUS"
  intent:     string,   // Semantic Intent block: SI: … :SI
  cs_source:  string,   // the full .cs file content
  run_id:     string,
  arm_path?:  string
) → { created: boolean, arm_path, trace_id }

The full file, not a separate DSL that compiles down later — write_arm persists exactly the C# Claude wrote. It refuses to write anything without a valid // MODE: READ | ACT line in cs_source — no file, no TRACE event. Declaring a mode isn't optional metadata; it's the thing run_arm's gate checks.


write_octo

Authors or updates a .octo orchestration plan.

write_octo(
  octo:         string,   // "morning-brief"
  intent:       string,   // Semantic Intent block
  octo_source:  string,   // the full .octo file content
  run_id:       string,
  octo_path?:   string
) → { created: boolean, octo_path, trace_id }

There is no run_octo. A .octo file is a plan Claude reads and walks through — calling run_arm for each listed arm, in order, then synthesizing the surface itself. Orchestration is a conversational act, not a separate execution engine.


check_habitat

No arguments. Reports what this instance can actually reach right now — not what's declared, what's real.

check_habitat() → {
  status: "green" | "yellow" | "red",
  reef_file: { path, exists, sources_declared },
  config: { trace_file, arms_dir, octo_dir, dotnet_root, dotnet_script, workspace, reach_repo },
  arms: { count, arms: [{ name, mode, tier }] },
  octo_workflows: { count, names }
}

For each config value: what it resolved to, and whether that came from an env var, from .reef, or from neither. A live dotnet-script --version check — not just "does the file exist," but "does the runtime actually work." Read-only throughout; it writes no TRACE event, because nothing here changes anything.


The MODE / TIER Header

Every arm's SI block now carries two more lines:

// SI:
// ARM: ARM-NAME
// INTENT: one line
// SOURCES: dot.path, dot.path
// OUTPUT: JSON — { field, field }
// MODE: READ | ACT
// TIER: STANDING | IN-SESSION
// :SI

MODE is enforced — write_arm won't save an arm without it, run_arm gates execution on it. READ means the arm only observes; it never sends, posts, modifies, or changes state anywhere. ACT means it does, and it always stops for confirmation first — this is what makes "an act arm always shows you what it's about to do before it happens" true in code, not just a stated intention.

TIER is declared and reported (check_habitat surfaces it per arm) but not yet enforced. STANDING — stored or OS-level access the practitioner already has: local files, git, an already-authenticated desktop app via COM. IN-SESSION — the human is live, already authenticated, watching; nothing about the session persists past it. There's no enforcement mechanism for IN-SESSION yet because no arm has needed it — the tier exists so it's declared honestly the day one does.


What Conversation Looks Like

User:   what have my arms actually been doing today?

Claude: read_trace(since="today")
        → 11 events across 3 runs
        REPO-STATUS ran clean. MARKET-BRIEF failed once on a compile
        error, then succeeded after a fix. Here's the timeline.

User:   is everything actually working right now?

Claude: check_habitat()
        → status: green — all config resolved via .reef,
          dotnet-script responds, 4 arms on disk, 0 octo workflows
        Everything's healthy.

User:   I want a snapshot of what's happening for a second pair of eyes

Claude: write_arm("FLARE", intent="...", cs_source="...")
        → ARM_CREATED (MODE: READ)
        run_arm("FLARE", run_id="...")
        → composed flare/flare-2026-07-19T23-07-28Z.md —
          recent events, habitat summary, any ARM_FAILs or
          unclosed runs flagged

Semantic Intent as Emitted Directive

Every tool description in reef-mcp follows the same shape every SI block in this ecosystem does — I need / I accept / I will / I return / I do not / I estimate — not as documentation for a human reading a webpage, but as the literal text Claude receives when it calls the tool. Claude reads that block as an action plan and acts on it directly. The tool's input schema is a typed, validated Zod schema (not parsed from the description text) — but the description itself is still a real, load-bearing Semantic Intent block, read by Claude the same way it reads one in an arm's own SI header.

Described in full in One Grammar, Two Readers · Formalised in Semantic Intent as Emitted Directive.


Next

GitHub: semanticintent