⚠️ Proof of Concept - This is a prototype implementation for exploring how discourse graphs can be integrated with AI assistants through the Model Context Protocol (MCP). It is intended for prototyping and experimental purposes only.
An AI bridge to your Roam Research graph. Ask questions about your discourse graph, explore relationships between research nodes, analyze pilot user feedback, and read/write pages and blocks — all through natural conversation with Claude.
- "Show me all the evidence that supports this claim"
- "What did the team do this week?"
- "Search for anything related to CRISPR"
- "Which pilots mention Canvas?" (live search, works immediately)
- "What should we build next according to our pilots?" (needs the knowledge index built first)
- Full read/write access to Roam pages and blocks
- Raw Datalog queries, typed discourse relations, query builder execution, K-hop graph traversal
No code required. You talk to Claude; Claude talks to your graph.
- Roam Desktop running with the Local API enabled
- Node.js 18+
npx @roam-research/roam-mcp connectFollow the prompts to authenticate. Your token is saved to ~/.roam-tools.json.
git clone <repo-url>
cd discourse-graph-mcp
npm installClaude Code:
claude mcp add -s user discourse-graph -- npx tsx /path/to/discourse-graph-mcp/src/index.tsClaude Desktop — add to your MCP config:
Config file locations
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Codex CLI:
Use the built version for the most reliable setup:
npm run build
codex mcp add discourse-graph -- node /path/to/discourse-graph-mcp/dist/index.jsIf you are actively developing the server, you can also run it directly from source:
codex mcp add discourse-graph -- npx tsx /path/to/discourse-graph-mcp/src/index.tsVerify it was added:
codex mcp list{
"mcpServers": {
"discourse-graph": {
"command": "npx",
"args": ["tsx", "/path/to/discourse-graph-mcp/src/index.ts"]
}
}
}The server makes many API calls during operations like deep search and indexing. To avoid approving each one individually, allow the MCP tools upfront.
In your Claude Code settings (~/.claude/settings.json), add:
{
"permissions": {
"allow": [
"mcp__discourse-graph__*"
]
}
}This allows all discourse-graph tools to run without per-call approval. For more control, allow only read operations:
{
"permissions": {
"allow": [
"mcp__discourse-graph__search_*",
"mcp__discourse-graph__get_*",
"mcp__discourse-graph__query_*",
"mcp__discourse-graph__deep_*",
"mcp__discourse-graph__check_*",
"mcp__discourse-graph__index_*",
"mcp__discourse-graph__catch_*",
"mcp__discourse-graph__run_*",
"mcp__discourse-graph__save_*"
]
}
}This still prompts for writes (create_*, update_*, delete_*).
Open a new Claude session and try:
"What discourse node types are in my graph?"
Note: This server includes all standard Roam MCP tools. You do not need
@roam-research/roam-mcpinstalled separately — remove it if you have it to avoid duplicate tools.
| You say... | What happens |
|---|---|
| "What types of nodes are in my graph?" | Returns all node type definitions (Claim, Evidence, Question, etc.) and how they relate |
| "Find all claims created this month" | Searches for discourse node instances filtered by date |
| "Search for anything about mitochondria" | Keyword search across all node titles |
| "Show me node abc123" | Full content tree, creator, and dates for a specific node |
| You say... | What happens |
|---|---|
| "What evidence supports this claim?" | Follows typed discourse relations (Supports, Opposes, Informs) |
| "What links to and from this node?" | All outgoing references and incoming backlinks |
| "Explore 2 hops out from this node" | Graph traversal — the neighborhood around a node |
| "Run the query on block xyz789" | Executes a query builder query that someone built in Roam |
| You say... | What happens |
|---|---|
| "What did [username] do this week?" | Nodes created/edited, daily log entries, pages touched |
| "Who are the top contributors?" | Authors ranked by node count |
| "Show me the Summary section of this claim" | One template section without the whole tree |
| You say... | What happens |
|---|---|
| "Read the page about our experiment" | Fetches the page content tree |
| "Create a new claim: X causes Y" | Creates a new page in the graph |
| "Add a block under this node" | Creates a child block |
| "Move this block under that parent" | Restructures content |
Works immediately, no setup needed. Scans pilot pages in real time.
| You say... | What happens |
|---|---|
| "List all our pilot users" | Returns every pilot page with UIDs |
| "Which pilots mention Canvas?" | Live layered search — wikilinks, text matches, sentiment signals |
Requires building the index first (see next section). Once built, queries are instant.
| You say... | What happens |
|---|---|
| "What should we build next?" | Cross-pilot rollup rankings |
| "What are [pilot name]'s pain points?" | Single pilot's classified topics |
| "Top feature requests across all pilots?" | Aggregated rankings |
| "Deep search for left sidebar feedback" | Searches both the index and live data in one call |
| "Is my pilot index up to date?" | Compares index timestamps to live edit times |
The knowledge index is a structured summary of your pilot user pages — feedback, feature requests, pain points, workflows, and more. Once built, queries against it are instant.
~/.discourse-graph-mcp/pilot-index.json — local to your machine. Never committed to the repo, never shared. It contains your graph's data. If you lose it, rebuild it.
The MCP server extracts raw data from pilot pages. Claude reads the content, classifies it into topics, and saves the results. No separate API key needed.
"Index all pilot pages"
|
index_pilot_pages --> extracts batch of 5 pilots
|
Claude reads and classifies into topics
|
save_pilot_index --> writes to disk
|
index_pilot_pages (next batch) --> repeats
|
... until all pilots are done ...
|
Claude generates cross-pilot rollups
|
save_pilot_index --> writes rollups
|
Done. Future queries are instant.
Say:
"Index all my pilot pages"
Claude handles the rest — discovers all pilots, processes them in batches, classifies each pilot's content into topics like feature_requests, pain_points, workflow, feedback, challenges. Categories aren't fixed — Claude adapts them to what it finds.
After all pilots are processed, Claude generates cross-pilot rollups: ranked feature requests, common pain points, and what to build next.
"Check if the pilot index is up to date"
Compares your index against the live graph. If pilots changed since last index:
"Re-index the stale pilots"
Only changed pages get re-processed.
44 tools: 23 Roam base + 21 Discourse Graph.
Graph Management — connect and inspect your Roam graph
| Tool | Description |
|---|---|
list_graphs |
List all connected Roam graphs |
setup_new_graph |
Connect to a new Roam graph |
get_graph_guidelines |
Get the graph's custom guidelines/conventions |
Pages — create, read, update, and delete pages
| Tool | Description |
|---|---|
create_page |
Create a new page with a title |
get_page |
Get a page's content tree by title or UID |
update_page |
Update a page's title |
delete_page |
Delete a page |
Blocks — create, read, update, delete, and move blocks
| Tool | Description |
|---|---|
create_block |
Create a new block under a parent |
get_block |
Get a block's content and path |
update_block |
Update a block's text |
delete_block |
Delete a block |
move_block |
Move a block to a new parent/position |
get_backlinks |
Get all pages/blocks that reference a given page |
Search & Query — find content across the graph
| Tool | Description |
|---|---|
search |
Full-text search across pages and blocks |
search_templates |
Search for Roam templates |
roam_query |
Execute a raw Datalog query |
Files — manage file attachments
| Tool | Description |
|---|---|
file_get |
Download a file from the graph |
file_upload |
Upload a file to the graph |
file_delete |
Delete a file from the graph |
UI Control — interact with the Roam Desktop window
| Tool | Description |
|---|---|
get_open_windows |
List open pages in main window and sidebar |
get_selection |
Get the currently selected block(s) |
open_main_window |
Open a page in the main window |
open_sidebar |
Open a page in the right sidebar |
Discourse Node Types — understand the graph's schema
| Tool | Description |
|---|---|
get_discourse_node_types |
All node type and relation definitions |
get_users |
List all graph contributors |
Discourse Node Discovery — find and inspect nodes
| Tool | Description |
|---|---|
get_all_discourse_nodes |
Find all instances of a node type, optionally filter by date |
search_nodes |
Keyword search across discourse node titles |
get_node |
Full node details: title, content tree, creator, dates |
Discourse Graph Exploration — traverse relationships
| Tool | Description |
|---|---|
get_linked_nodes |
Outgoing references + incoming backlinks |
get_relationships |
Typed discourse relations (Supports, Opposes, Informs, etc.) |
get_node_neighborhood |
K-hop BFS traversal around a node |
get_node_images |
Extract image URLs from a node's content |
Discourse Content & Analysis — sections, contributions, activity
| Tool | Description |
|---|---|
get_node_section |
Extract a specific template section (Summary, Evidence, etc.) |
get_researcher_contributions |
Nodes by author, or contributor stats |
catch_me_up |
User's recent activity: nodes, daily logs, pages touched |
Discourse Query Builder — run structured queries
| Tool | Description |
|---|---|
run_discourse_query |
Execute a query builder query by block UID, with optional inputs and explicit reporting for unsupported selections |
Pilot Analysis — understand pilot user feedback
| Tool | Description |
|---|---|
get_pilot_users |
List all pilot user pages with names and UIDs |
search_pilots_live |
Live layered search for a feature across pilot pages |
index_pilot_pages |
Build/update the knowledge index (auto-paginated) |
query_pilot_insights |
Query the index by pilot, topic, or both (instant) |
check_index_freshness |
Compare index to live data, find stale pilots |
deep_pilot_search |
Combined index + live search in one call |
Indexing Pipeline (internal) — used by Claude during indexing, not called directly
| Tool | Description |
|---|---|
extract_pilot_data |
Fetch specific pilot pages chunked by section |
save_pilot_index |
Write classified data and rollups to the index file |
These are part of the indexing workflow. When you say "index all pilot pages", Claude orchestrates these automatically. You don't need to call them directly.
Claude (any MCP client)
|
| stdio (JSON-RPC 2.0)
v
discourse-graph-mcp
|-- 23 Roam base tools (from @roam-research/roam-tools-core)
|-- 21 Discourse Graph tools
|
| HTTP to localhost
v
Roam Desktop (Local API)
v
Your Roam Graph
Local data:
~/.discourse-graph-mcp/pilot-index.json (knowledge index, never shared)
- Discourse graph tools are read-only. Writes use the Roam base tools.
- Auth reuses
~/.roam-tools.jsonfromroam-mcp connect. No separate setup. - The knowledge index is local to your machine. It's a cache — rebuild anytime.
- Roam Desktop must be running. Connects to the Local API on localhost.
- Tree fetching is slow for large pages. One API call per tree level per block. The knowledge index exists to do this work once. Tree-based tools now surface depth metadata when a page hits the traversal cap.
- Some Datalog features don't work via Local API.
pull,:keys, and severalclojure.stringfunctions are unsafe or silently fail. The server works around this with tuple queries, regex-based matching, and JS-side filtering. - Query builder is not full browser parity. Date conditions and context-dependent targets (
{current},{this page},{current user}) are still unsupported. - Some query-builder selections are intentionally partial. Unsupported selections are reported in the tool output instead of being silently dropped.
npm install
# Type check
npx tsc --noEmit --skipLibCheck
# Verify tools register
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}\n{"jsonrpc":"2.0","method":"notifications/initialized"}\n{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}\n' | timeout 5 npx tsx src/index.ts 2>/dev/nullMIT