Reference

Using Roam via MCP

How to call Roam from Claude Code, Cursor, Codex, Gemini CLI, Amp, VS Code, and any other Model Context Protocol client. Covers first-run setup, the cold-start envelope your agent will see on a fresh repo, the tools that work without an index, the canonical agent flow, slow-tool patterns, and the failure modes that have tripped up real users.

MCP setup itself (per-platform commands and config snippets) lives in Integration Tutorials. This page assumes the server is already wired up and focuses on what your agent should actually do with it.

First-run setup

Client setup and protocol support are different. Run roam mcp --compat-profile all to inspect the installed SDK's supported revisions. The tested stack supports the handshake through 2025-11-25; the newer July 2026 lifecycle is not yet a supported integration. See the protocol compatibility guide. Install dependencies before launching stdio; keep installation output out of the protocol stream.

Install the MCP extra (Python 3.10+ required) and run roam init in your project root before the first MCP call. roam init builds .roam/index.db by default — the local SQLite graph used by indexed queries. Indexing time depends on the repository, its history, and your machine. Refresh it with roam index as code changes; later runs can reuse unchanged files.

pip install "roam-code[mcp]"   # Python 3.10+; pulls fastmcp + tree-sitter
cd /path/to/repo
roam init                       # creates .roam/index.db + fitness rules; CI is opt-in
roam --json doctor              # inspect environment and index readiness
roam mcp-setup claude-code       # preview one supported client config

Review the generated configuration, then add --write when you intend to install it. Restart the server in your client and check its connected tool list. Use an absolute executable path if the editor cannot find the same Roam installation as your terminal.

Why this is a manual step: indexing can take long enough to exceed an MCP call timeout, and forcing an automatic build on first use hides that cost from the agent. We surface the cost explicitly so the agent (or the human driving it) can budget for it. See the next section for what your agent receives when it skips this step.

The cold-start envelope

When an MCP tool that needs an index is called on a project where the configured index is not built, the call's cold-start guard does not build it. It returns a structured envelope the agent can read and act on:

{
  "command": "roam_health",
  "isError": true,
  "status": "index_not_built",
  "summary": {
    "verdict": "Index not built. Run `roam init` in a terminal first, then retry MCP tools.",
    "level": "blocker",
    "partial_success": false
  },
  "next_command": "roam init",
  "expected_duration_seconds": 60,
  "retry_after_seconds": 60,
  "agent_contract": {
    "facts": [
      "0 of 8 evidence questions answered without indexed symbols",
      "1 prerequisite command unmet -- run roam init"
    ],
    "next_commands": [
      "roam init",
      "# then retry the MCP tool that returned this envelope"
    ]
  },
  "_meta": {
    "guard": "w296_cold_start",
    "tool": "roam_health"
  }
}

Wire your agent prompt to recognise status == "index_not_built" and either run roam init itself (if the agent has shell access) or ask the human to. The next_command field is copy-paste executable; timing fields are estimates, not a completion signal. Wait for indexing to finish successfully before retrying. This is an error result even though partial_success is false: inspect isError and status before the summary. Server startup and an explicitly enabled watcher have separate index-refresh behavior.

To bypass the guard for tests or scripts, set ROAM_MCP_DISABLE_COLD_START_GUARD=1 in the server environment. Production agent setups should leave it on.

Tools that don't need an index

An index exemption does not make a tool available in your preset. For bootstrap and diagnostics, use roam init and roam --json doctor in a terminal at the project root. These metadata and delivery tools are available in the default core preset without an index:

ToolPurpose
roam_expand_toolsetInspect preset contents (core / review / refactor / debug / architecture / compliance / compile-curated / full). Changing the active toolset requires a server restart.
roam_fetch_handleFetch all or part of a large MCP payload by handle (byte slice, section pick, jq projection).

Inspect roam mcp --list-tools-json for the installed inventory and argument schemas. This describes that local process, not the server already connected to your editor.

Typical agent flow

Start with a repository question, then inspect the relevant code. These illustrative calls use tools and argument names in the default core preset. Replace auth_login with a symbol returned from your project; these are MCP calls, not shell commands. The server must be rooted in that project.

roam_understand(root=".")
roam_search_symbol(query="auth_login", root=".")
roam_uses(symbol="auth_login", root=".")
roam_prepare_change(symbol="auth_login", root=".")

Read the returned locations and limitations before editing. Indexed references are not every possible runtime caller; suggested tests still need execution. After changes, refresh the index and run the project's tests and review checks. The Agent Contract explains how to choose those checks. Some MCP tools combine multiple CLI operations; a single tool call is not a promise of one underlying process.

Tools that take longer than expected

Separate computation time from response size. A handle makes a large result easier to retrieve; it does not make the analysis faster. Narrow scope only with arguments the connected tool's schema supports.

When a response contains is_handle: true, use its handle to retrieve the stored result. For byte-slice delivery, start at offset zero and follow next_offset while has_more is true. A slice is not a complete JSON document. Use the same project root as the original call.

roam_fetch_handle(handle="<returned handle>", root=".", offset=0, limit=20000)

Alternatively request a supported top-level section or jq projection, not both. A projection answers only that narrower question. Retrieve the required evidence before accepting a verdict; a handle preview is not the full result.

CLI flags and MCP parameters are separate interfaces; check each tool's schema for supported arguments and defaults. Read evidence limits for deletion verdicts, test mapping, heuristic scores, and partial analyses.

Check whether missing detail is recoverable delivery or incomplete computation. Fetching a stored partial analysis does not complete it. If storage failed, inspect the operation's outcome before retrying: an operation with side effects may already have run. If that outcome remains unknown, report it and do not repeat the write merely because its response could not be stored.

Troubleshooting

Tool hangs or times out

Check that .roam/index.db exists in the working directory the MCP server was launched from. If it does not, the cold-start guard should have returned an index_not_built envelope rather than hanging — if you see a real hang, run roam doctor to confirm the install, then roam init to build the index. Cloud-sync filesystems (OneDrive, Dropbox, iCloud) sometimes lock the SQLite file; see Troubleshooting #2.

A readable summary inside an error result

Check isError, status, and any error_code before using the summary. Keep useful diagnostics, but do not treat readable JSON or a reassuring verdict as completed analysis when the surrounding result reports failure. Resolve the named prerequisite or invocation problem first.

Empty result from roam_dead_code or roam_smells

A completed scan can find zero candidates within its stated scope. Check what was scanned, index freshness, resolution, and any warnings, failed checks, caps, or partial_success disclosure. An empty list without evidence that the required scan completed is unknown, not clearance. Keep valid positive findings from a partial result as leads; investigate the missing scope separately.

Tool not found

First check the connected installation and preset; a stale connection can differ from the configuration you just generated. The default is core. Use roam_expand_toolset() to inspect available presets, then inspect the relevant preset by name. Select one that contains the needed tool; full is an option, not a required repair for every missing-tool error. A preset change requires a server restart.

Set the variable in the editor's MCP server configuration. For a manual PowerShell launch, run $env:ROAM_MCP_PRESET = "full", then roam mcp. Confirm the connection in the client's tool list; local diagnostics inspect their own process, not the editor's connection.

Server returns stale data after a git pull

Run roam index and wait for the refresh to finish. For optional reindexing on file changes, install watchdog in the same Python environment as the MCP server; the roam-code[mcp] extra does not include it. Set ROAM_MCP_WATCH=1 in the server environment, restart, and confirm file watcher started in its startup log. If it does not start, keep refreshing with roam index. The watcher can emit notifications/resources/updated to supported clients. A notification is not proof that your next result covers the intended revision; inspect index freshness after the update.

MCP security stance: inside-server vs gateway

Roam's MCP server is the inside-server half of the agent's trust boundary. It runs locally as the same user as the editor, reads your code, and emits structured evidence on every sensitive call. It does not sit as a network gateway between agent and model; it does not proxy or rewrite model traffic; and it does not introspect prompts or responses on the wire. Roam applies producer-side secret-pattern redaction and exposes supported structural injection markers. Those bounded checks do not detect every secret or malicious instruction. A host or gateway can add semantic inspection, traffic policy, and tool-call controls.

What Roam does contribute to security is local, structural, and tamper-evident:

Read the inside-server vs gateway discussion for the longer framing of which class of defence Roam owns and which class belongs to the host.

Generate config: don't hand-edit

Every per-client config snippet on Integration Tutorials comes from one command:

roam mcp-setup claude-code     # or: cursor, codex-cli, gemini-cli, vscode, windsurf
roam mcp-setup cursor --preset compliance     # 14-tool AI-governance subset
roam mcp-setup vscode --write                 # write the file in place
roam --json mcp-setup vscode                  # structured envelope for scripting

The generator uses maintained client configuration templates and can add the selected preset to the server environment. It does not inspect your connected client or discover its active tools. Review the preview, preserve unrelated settings, then restart the connection after a change. Check the client's tool inventory against the intended installation and preset.

When to use MCP vs the CLI directly

SurfaceUse it when
MCP An AI coding agent (Claude Code, Cursor, Codex, Gemini CLI, Amp, custom) is driving the workflow. The agent gets structured envelopes, schema-versioned output, and shared session memory across calls.
CLI An agent has shell access, a person is exploring, or a CI pipeline or script is running checks. Use --json for machine-consumed results; selected commands also support SARIF. Inspect process exit and the command's result state.

Both surfaces use Roam's analysis engine. MCP also has metadata, session, delivery, and compound tools; names, arguments, and defaults are not always identical to the CLI. Choose the supported interface for the task, not an assumed one-to-one mapping.

See it run

The worked change-review demo — local queries, patch review, a saved bundle, and run-ledger verification. Inspect missing checks along the way; setup time varies by project.

See also