roam-research-mcp 1.6.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -13
- package/build/Roam_Markdown_Cheatsheet.md +30 -12
- package/build/cli/batch/resolver.js +138 -0
- package/build/cli/batch/translator.js +363 -0
- package/build/cli/batch/types.js +4 -0
- package/build/cli/commands/batch.js +352 -0
- package/build/cli/commands/get.js +101 -19
- package/build/cli/commands/refs.js +21 -8
- package/build/cli/commands/rename.js +58 -0
- package/build/cli/commands/save.js +433 -56
- package/build/cli/commands/search.js +179 -18
- package/build/cli/commands/status.js +91 -0
- package/build/cli/commands/update.js +151 -0
- package/build/cli/roam.js +18 -1
- package/build/cli/utils/graph.js +56 -0
- package/build/cli/utils/output.js +34 -0
- package/build/config/environment.js +70 -34
- package/build/config/graph-registry.js +221 -0
- package/build/config/graph-registry.test.js +30 -0
- package/build/search/status-search.js +5 -4
- package/build/server/roam-server.js +98 -53
- package/build/shared/validation.js +10 -5
- package/build/tools/helpers/refs.js +50 -31
- package/build/tools/operations/blocks.js +38 -1
- package/build/tools/operations/memory.js +51 -5
- package/build/tools/operations/pages.js +186 -111
- package/build/tools/operations/search/index.js +5 -1
- package/build/tools/operations/todos.js +1 -1
- package/build/tools/schemas.js +115 -39
- package/build/tools/tool-handlers.js +9 -2
- package/build/utils/helpers.js +22 -0
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
|
-
# Roam Research MCP
|
|
3
|
+
# Roam Research MCP + CLI
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/roam-research-mcp)
|
|
6
|
-
[](https://www.repostatus.org/#active)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
[](https://github.com/2b3pro/roam-research-mcp/blob/main/LICENSE)
|
|
9
9
|
|
|
10
|
-
A Model Context Protocol (MCP) server and standalone CLI that provides comprehensive access to Roam Research's API functionality. The MCP server enables AI assistants like Claude to interact with your Roam Research graph through a standardized interface, while the CLI (`roam`) lets you fetch, search, and import content directly from the command line. Supports standard input/output (stdio) and HTTP Stream communication. (
|
|
10
|
+
A Model Context Protocol (MCP) server and standalone CLI that provides comprehensive access to Roam Research's API functionality. The MCP server enables AI assistants like Claude to interact with your Roam Research graph through a standardized interface, while the CLI (`roam`) lets you fetch, search, and import content directly from the command line. Supports multi-graph configurations, write protection, standard input/output (stdio) and HTTP Stream communication. (Personal project, not officially endorsed by Roam Research)
|
|
11
11
|
|
|
12
12
|
<a href="https://glama.ai/mcp/servers/fzfznyaflu"><img width="380" height="200" src="https://glama.ai/mcp/servers/fzfznyaflu/badge" alt="Roam Research MCP server" /></a>
|
|
13
13
|
<a href="https://mseep.ai/app/2b3pro-roam-research-mcp"><img width="380" height="200" src="https://mseep.net/pr/2b3pro-roam-research-mcp-badge.png" alt="MseeP.ai Security Assessment Badge" /></a>
|
|
@@ -85,7 +85,7 @@ docker run -p 3000:3000 -p 8088:8088 --env-file .env roam-research-mcp
|
|
|
85
85
|
|
|
86
86
|
## Standalone CLI: `roam`
|
|
87
87
|
|
|
88
|
-
A standalone command-line tool for interacting with Roam Research directly, without running the MCP server. Provides
|
|
88
|
+
A standalone command-line tool for interacting with Roam Research directly, without running the MCP server. Provides eight subcommands: `get`, `search`, `save`, `refs`, `update`, `batch`, `rename`, and `status`.
|
|
89
89
|
|
|
90
90
|
### Installation
|
|
91
91
|
|
|
@@ -111,7 +111,7 @@ Configure via `.env` file in the project root or set as environment variables.
|
|
|
111
111
|
|
|
112
112
|
---
|
|
113
113
|
|
|
114
|
-
### `roam get` - Fetch pages or
|
|
114
|
+
### `roam get` - Fetch pages, blocks, or TODOs
|
|
115
115
|
|
|
116
116
|
Fetch content from Roam and output as markdown or JSON.
|
|
117
117
|
|
|
@@ -132,6 +132,18 @@ roam get "Daily Notes" --depth 2
|
|
|
132
132
|
# Flatten hierarchy
|
|
133
133
|
roam get "Daily Notes" --flat
|
|
134
134
|
|
|
135
|
+
# Fetch TODO items
|
|
136
|
+
roam get --todo
|
|
137
|
+
|
|
138
|
+
# Fetch DONE items
|
|
139
|
+
roam get --done
|
|
140
|
+
|
|
141
|
+
# Filter TODOs by page
|
|
142
|
+
roam get --todo -p "January 2nd, 2026"
|
|
143
|
+
|
|
144
|
+
# Include/exclude filter
|
|
145
|
+
roam get --todo -i "urgent,important" -e "someday"
|
|
146
|
+
|
|
135
147
|
# Debug mode
|
|
136
148
|
roam get "Daily Notes" --debug
|
|
137
149
|
```
|
|
@@ -141,6 +153,11 @@ roam get "Daily Notes" --debug
|
|
|
141
153
|
- `--depth <n>` - Child levels to fetch (default: 4)
|
|
142
154
|
- `--refs <n>` - Block ref expansion depth (default: 1)
|
|
143
155
|
- `--flat` - Flatten hierarchy to single-level list
|
|
156
|
+
- `--todo` - Fetch TODO items
|
|
157
|
+
- `--done` - Fetch DONE items
|
|
158
|
+
- `-p, --page <title>` - Filter TODOs/DONEs by page title
|
|
159
|
+
- `-i, --include <terms>` - Include only items containing these terms (comma-separated)
|
|
160
|
+
- `-e, --exclude <terms>` - Exclude items containing these terms (comma-separated)
|
|
144
161
|
- `--debug` - Show query metadata
|
|
145
162
|
|
|
146
163
|
---
|
|
@@ -186,9 +203,9 @@ roam search "keyword" --json
|
|
|
186
203
|
|
|
187
204
|
---
|
|
188
205
|
|
|
189
|
-
### `roam save` - Import markdown
|
|
206
|
+
### `roam save` - Import markdown or create TODOs
|
|
190
207
|
|
|
191
|
-
Import markdown content to Roam, creating or updating pages.
|
|
208
|
+
Import markdown content to Roam, creating or updating pages, or add TODO items.
|
|
192
209
|
|
|
193
210
|
```bash
|
|
194
211
|
# From a file (title derived from filename)
|
|
@@ -211,11 +228,21 @@ roam save --title "Quick Note" << EOF
|
|
|
211
228
|
- Item 2
|
|
212
229
|
- Nested item
|
|
213
230
|
EOF
|
|
231
|
+
|
|
232
|
+
# Create a TODO item on today's daily page
|
|
233
|
+
roam save --todo "Buy groceries"
|
|
234
|
+
|
|
235
|
+
# Create multiple TODOs from stdin (newline-separated)
|
|
236
|
+
echo -e "Task 1\nTask 2\nTask 3" | roam save --todo
|
|
237
|
+
|
|
238
|
+
# Pipe TODO list from file
|
|
239
|
+
cat todos.txt | roam save --todo
|
|
214
240
|
```
|
|
215
241
|
|
|
216
242
|
**Options:**
|
|
217
243
|
- `--title <title>` - Page title (defaults to filename without `.md`)
|
|
218
244
|
- `--update` - Update existing page using smart diff (preserves block UIDs)
|
|
245
|
+
- `-t, --todo [text]` - Add a TODO item to today's daily page (text or stdin)
|
|
219
246
|
- `--debug` - Show debug information
|
|
220
247
|
|
|
221
248
|
**Features:**
|
|
@@ -223,6 +250,7 @@ EOF
|
|
|
223
250
|
- Automatically links the new page from today's daily page
|
|
224
251
|
- Converts standard markdown to Roam-flavored markdown
|
|
225
252
|
- Smart diff mode (`--update`) preserves block UIDs for existing content
|
|
253
|
+
- TODO mode creates `{{[[TODO]]}}` items on the daily page
|
|
226
254
|
|
|
227
255
|
---
|
|
228
256
|
|
|
@@ -278,6 +306,103 @@ JSON output for programmatic use:
|
|
|
278
306
|
|
|
279
307
|
---
|
|
280
308
|
|
|
309
|
+
### `roam batch` - Execute multiple operations in one API call
|
|
310
|
+
|
|
311
|
+
Execute multiple operations in a single batch API call to reduce rate limiting issues.
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# From a JSON file
|
|
315
|
+
roam batch commands.json
|
|
316
|
+
|
|
317
|
+
# From stdin
|
|
318
|
+
cat commands.json | roam batch
|
|
319
|
+
|
|
320
|
+
# Dry run (validate without executing)
|
|
321
|
+
roam batch --dry-run commands.json
|
|
322
|
+
|
|
323
|
+
# With debug output
|
|
324
|
+
roam batch --debug commands.json
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Command Format:**
|
|
328
|
+
|
|
329
|
+
Input is a JSON array of command objects:
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
[
|
|
333
|
+
{ "command": "create", "params": { "text": "Parent block", "parent": "pageUid", "as": "p1" } },
|
|
334
|
+
{ "command": "create", "params": { "text": "Child block", "parent": "{{p1}}" } },
|
|
335
|
+
{ "command": "todo", "params": { "text": "Task item" } },
|
|
336
|
+
{ "command": "codeblock", "params": { "parent": "{{p1}}", "language": "ts", "code": "const x = 1;" } }
|
|
337
|
+
]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Supported Commands:**
|
|
341
|
+
|
|
342
|
+
| Command | Description |
|
|
343
|
+
|---------|-------------|
|
|
344
|
+
| `create` | Create a block |
|
|
345
|
+
| `update` | Update block content |
|
|
346
|
+
| `delete` | Delete a block |
|
|
347
|
+
| `move` | Move block to new location |
|
|
348
|
+
| `todo` | Create TODO item |
|
|
349
|
+
| `table` | Create table structure |
|
|
350
|
+
| `outline` | Create nested outline |
|
|
351
|
+
| `remember` | Create tagged memory |
|
|
352
|
+
| `page` | Create page with content |
|
|
353
|
+
| `codeblock` | Create code block |
|
|
354
|
+
|
|
355
|
+
**Features:**
|
|
356
|
+
|
|
357
|
+
- Placeholder references (`{{name}}`) for cross-command dependencies
|
|
358
|
+
- Automatic page title → UID resolution
|
|
359
|
+
- Daily page auto-resolution for `todo` and `remember`
|
|
360
|
+
- Level-based hierarchy for `outline` command
|
|
361
|
+
- `--dry-run` mode for validation
|
|
362
|
+
|
|
363
|
+
**Options:**
|
|
364
|
+
- `--dry-run` - Validate and show planned actions without executing
|
|
365
|
+
- `--debug` - Show debug information
|
|
366
|
+
- `-g, --graph <name>` - Target graph (multi-graph mode)
|
|
367
|
+
- `--write-key <key>` - Write confirmation key
|
|
368
|
+
|
|
369
|
+
See [docs/batch-cli-spec.md](docs/batch-cli-spec.md) for full specification.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
### `roam status` - Show available graphs
|
|
374
|
+
|
|
375
|
+
Display configured graphs and their connection status.
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# Show available graphs
|
|
379
|
+
roam status
|
|
380
|
+
|
|
381
|
+
# Test connectivity to all graphs
|
|
382
|
+
roam status --ping
|
|
383
|
+
|
|
384
|
+
# Output as JSON
|
|
385
|
+
roam status --json
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**Example Output:**
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
Roam Research MCP v2.4.0
|
|
392
|
+
|
|
393
|
+
Graphs:
|
|
394
|
+
• personal (default) ✓ connected
|
|
395
|
+
• work [protected] ✓ connected
|
|
396
|
+
|
|
397
|
+
Write-protected graphs require --write-key flag for modifications.
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Options:**
|
|
401
|
+
- `--ping` - Test connection to each graph
|
|
402
|
+
- `--json` - Output as JSON for scripting
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
281
406
|
## To Test
|
|
282
407
|
|
|
283
408
|
Run [MCP Inspector](https://github.com/modelcontextprotocol/inspector) after build using the provided npm script:
|
|
@@ -304,7 +429,7 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
304
429
|
|
|
305
430
|
1. `roam_fetch_page_by_title`: Fetch page content by title. Returns content in the specified format.
|
|
306
431
|
2. `roam_fetch_block_with_children`: Fetch a block by its UID along with its hierarchical children down to a specified depth. Automatically handles `((UID))` formatting.
|
|
307
|
-
3. `roam_create_page`: Create new pages with optional content and headings. **Now supports mixed content types** - content array can include both text blocks and tables in a single call using `{type: "table", headers, rows}` format.
|
|
432
|
+
3. `roam_create_page`: Create new pages with optional content and headings. **Now supports mixed content types** - content array can include both text blocks and tables in a single call using `{type: "table", headers, rows}` format. Automatically adds a "Processed: [[date]]" block at the end of the page linking to today's daily page.
|
|
308
433
|
4. `roam_create_table`: Create a properly formatted Roam table with specified headers and rows. Abstracts Roam's complex nested table structure, validates row/column consistency, and handles empty cells automatically.
|
|
309
434
|
5. `roam_import_markdown`: Import nested markdown content under a specific block. (Internally uses `roam_process_batch_actions`.)
|
|
310
435
|
6. `roam_add_todo`: Add a list of todo items to today's daily page. (Internally uses `roam_process_batch_actions`.)
|
|
@@ -316,12 +441,13 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
316
441
|
12. `roam_search_by_status`: Search for blocks with a specific status (TODO/DONE) across all pages or within a specific page.
|
|
317
442
|
13. `roam_search_by_date`: Search for blocks or pages based on creation or modification dates.
|
|
318
443
|
14. `roam_search_for_tag`: Search for blocks containing a specific tag and optionally filter by blocks that also contain another tag nearby or exclude blocks with a specific tag. This tool supports pagination via the `limit` and `offset` parameters.
|
|
319
|
-
15. `roam_remember`: Add a memory or piece of information to remember. (Internally uses `roam_process_batch_actions`.)
|
|
444
|
+
15. `roam_remember`: Add a memory or piece of information to remember. Supports optional `heading` parameter to nest under a specific heading on the daily page (created if missing), or `parent_uid` to nest under a specific block. (Internally uses `roam_process_batch_actions`.)
|
|
320
445
|
16. `roam_recall`: Retrieve all stored memories.
|
|
321
446
|
17. `roam_datomic_query`: Execute a custom Datomic query on the Roam graph for advanced data retrieval beyond the available search tools. Now supports client-side regex filtering for enhanced post-query processing. Optimal for complex filtering (including regex), highly complex boolean logic, arbitrary sorting criteria, and proximity search.
|
|
322
447
|
18. `roam_markdown_cheatsheet`: Provides the content of the Roam Markdown Cheatsheet resource, optionally concatenated with custom instructions if `CUSTOM_INSTRUCTIONS_PATH` environment variable is set.
|
|
323
448
|
19. `roam_process_batch_actions`: Execute a sequence of low-level block actions (create, update, move, delete) in a single, non-transactional batch. Provides granular control for complex nesting like tables. **Now includes pre-validation** that catches errors before API execution, with structured error responses and automatic rate limit retry with exponential backoff. (Note: For actions on existing blocks or within a specific page context, it is often necessary to first obtain valid page or block UIDs using tools like `roam_fetch_page_by_title`.)
|
|
324
449
|
20. `roam_update_page_markdown`: Update an existing page with new markdown content using smart diff. **Preserves block UIDs** where possible, keeping references intact across the graph. Uses three-phase matching (exact text → normalized → position fallback) to generate minimal operations. Supports `dry_run` mode to preview changes. Ideal for syncing external markdown files, AI-assisted content updates, and batch modifications without losing block references.
|
|
450
|
+
21. `roam_move_block`: Move a block to a new location (different parent or position). Convenience wrapper around `roam_process_batch_actions` for single block moves. Parameters: `block_uid` (required), `parent_uid` (required), `order` (optional, defaults to "last").
|
|
325
451
|
|
|
326
452
|
**Deprecated Tools**:
|
|
327
453
|
The following tools have been deprecated as of `v0.36.2` in favor of the more powerful and flexible `roam_process_batch_actions`:
|
|
@@ -462,9 +588,13 @@ The tool will match existing blocks by content, update changed text, add new blo
|
|
|
462
588
|
- Create a new token
|
|
463
589
|
|
|
464
590
|
2. Configure the environment variables:
|
|
465
|
-
You have two options for configuring the required environment variables:
|
|
466
591
|
|
|
467
|
-
|
|
592
|
+
### Single Graph Mode (Default)
|
|
593
|
+
|
|
594
|
+
For most users with one Roam graph, use the simple configuration:
|
|
595
|
+
|
|
596
|
+
**Option 1: Using a .env file (Recommended for development)**
|
|
597
|
+
|
|
468
598
|
Create a `.env` file in the roam-research directory:
|
|
469
599
|
|
|
470
600
|
```
|
|
@@ -475,7 +605,8 @@ The tool will match existing blocks by content, update changed text, add new blo
|
|
|
475
605
|
HTTP_STREAM_PORT=8088 # Or your desired port for HTTP Stream communication
|
|
476
606
|
```
|
|
477
607
|
|
|
478
|
-
Option 2: Using MCP settings (Alternative method)
|
|
608
|
+
**Option 2: Using MCP settings (Alternative method)**
|
|
609
|
+
|
|
479
610
|
Add the configuration to your MCP settings file. Note that you may need to update the `args` to `["/path/to/roam-research-mcp/build/index.js"]` if you are running the server directly.
|
|
480
611
|
|
|
481
612
|
- For Cline (`~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`):
|
|
@@ -501,6 +632,62 @@ The tool will match existing blocks by content, update changed text, add new blo
|
|
|
501
632
|
|
|
502
633
|
Note: The server will first try to load from .env file, then fall back to environment variables from MCP settings.
|
|
503
634
|
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
### Multi-Graph Mode (v2.0.0+)
|
|
638
|
+
|
|
639
|
+
For users with multiple Roam graphs, you can configure a single MCP server instance to connect to all of them. This is more token-efficient than running multiple server instances.
|
|
640
|
+
|
|
641
|
+
**Configuration:**
|
|
642
|
+
|
|
643
|
+
```json
|
|
644
|
+
ROAM_GRAPHS="{\"personal\":{\"token\":\"roam-graph-token-xxx\",\"graph\":\"my-personal-graph\"},\"work\":{\"token\":\"roam-graph-token-yyy\",\"graph\":\"company-graph\",\"write_key\":\"confirm-work-write\"}}"
|
|
645
|
+
ROAM_DEFAULT_GRAPH=personal
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
| Field | Required | Description |
|
|
649
|
+
|-------|----------|-------------|
|
|
650
|
+
| `token` | Yes | Roam API token for this graph |
|
|
651
|
+
| `graph` | Yes | Roam graph name |
|
|
652
|
+
| `write_key` | No | Required confirmation string for writes to non-default graphs |
|
|
653
|
+
|
|
654
|
+
**Usage in Tools:**
|
|
655
|
+
|
|
656
|
+
All tools accept optional `graph` and `write_key` parameters:
|
|
657
|
+
|
|
658
|
+
```json
|
|
659
|
+
{
|
|
660
|
+
"title": "My Page",
|
|
661
|
+
"graph": "work",
|
|
662
|
+
"write_key": "confirm-work-write"
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
- **Read operations**: Can target any graph using the `graph` parameter
|
|
667
|
+
- **Write operations on default graph**: Work without additional parameters
|
|
668
|
+
- **Write operations on non-default graphs**: Require the `write_key` if configured
|
|
669
|
+
|
|
670
|
+
**CLI Usage:**
|
|
671
|
+
|
|
672
|
+
All CLI commands support the `-g, --graph` flag:
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
# Read from work graph
|
|
676
|
+
roam get "Meeting Notes" -g work
|
|
677
|
+
|
|
678
|
+
# Write to work graph (requires --write-key if configured)
|
|
679
|
+
roam save notes.md -g work --write-key "confirm-work-write"
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Safety Model:**
|
|
683
|
+
|
|
684
|
+
The `write_key` serves as a confirmation gate (not a secret) to prevent accidental writes to non-default graphs. When a write is attempted without the required key, the error message reveals the expected key:
|
|
685
|
+
|
|
686
|
+
```
|
|
687
|
+
Write to "work" graph requires write_key confirmation.
|
|
688
|
+
Provide write_key: "confirm-work-write" to proceed.
|
|
689
|
+
```
|
|
690
|
+
|
|
504
691
|
3. Build the server (make sure you're in the root directory of the MCP):
|
|
505
692
|
|
|
506
693
|
Note: Customize 'Roam_Markdown_Cheatsheet.md' with any notes and preferences specific to your graph BEFORE building.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Roam Markdown Cheatsheet — Generic Foundation v2.0.
|
|
1
|
+
# Roam Markdown Cheatsheet — Generic Foundation v2.0.1
|
|
2
2
|
|
|
3
3
|
> ⚠️ **MODEL DIRECTIVE**: Always consult this cheatsheet BEFORE making any Roam tool calls. Syntax errors in Roam are unforgiving.
|
|
4
4
|
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
|
|
37
37
|
⚠️ **CRITICAL**: Never concatenate multi-word tags. `#knowledgemanagement` ≠ `#[[knowledge management]]`
|
|
38
38
|
|
|
39
|
+
⚠️ **CRITICAL**: Never use `#` to mean "number" (e.g., `#1`, `#2`). In Roam, `#` **always** creates a hashtag. Write `Step 1`, `No. 1`, or just spell out the number instead.
|
|
40
|
+
|
|
39
41
|
### Dates
|
|
40
42
|
- **Always use ordinal format**: `[[January 1st, 2025]]`, `[[December 23rd, 2024]]`
|
|
41
43
|
- Ordinals: 1st, 2nd, 3rd, 4th–20th, 21st, 22nd, 23rd, 24th–30th, 31st
|
|
@@ -72,7 +74,7 @@ Source:: https://example.com
|
|
|
72
74
|
| `Note:: Some observation` | Just write the text, or use `#note` | One-off labels don't need attribute syntax |
|
|
73
75
|
| `Summary:: The main point` | `**Summary:** The main point` | Section headers are formatting, not metadata |
|
|
74
76
|
| `Definition:: Some text` | `Term:: Definition` | Only use for actual definitions you want to query |
|
|
75
|
-
| `Implementation Tier 3 (Societal Restructuring):: Some text` | `**
|
|
77
|
+
| `Implementation Tier 3 (Societal Restructuring):: Some text` | `**Implementation Tier 3 (Societal Restructuring):** Some text` | Label is specific to current concept |
|
|
76
78
|
|
|
77
79
|
⚠️ **The Test**: Ask yourself: "Will I ever query for all blocks with this attribute across my graph?" If no, use **bold formatting** (`**Label:**`) instead of `::` syntax.
|
|
78
80
|
|
|
@@ -174,6 +176,7 @@ Tables use nested indentation. Each column header/cell nests ONE LEVEL DEEPER th
|
|
|
174
176
|
|----------|-----------|-----|
|
|
175
177
|
| `Step 1:: Do this` | `**Step 1:** Do this` | `::` creates queryable attributes; use bold for page-specific labels |
|
|
176
178
|
| `#multiplewords` | `#[[multiple words]]` | Concatenated tags create dead references |
|
|
179
|
+
| `#1`, `#2`, `#3` | `Step 1`, `No. 1`, or spell out | `#` always creates hashtags, never means "number" |
|
|
177
180
|
| `[[january 1, 2025]]` | `[[January 1st, 2025]]` | Must use ordinal format with proper capitalization |
|
|
178
181
|
| `[text](((block-uid)))` | `[text](<((block-uid))>)` | Block ref links need angle bracket wrapper |
|
|
179
182
|
| `{{embed: ((uid))}}` | `{{[[embed]]: ((uid))}}` | Embed requires double brackets around keyword |
|
|
@@ -198,9 +201,17 @@ CREATING CONTENT IN ROAM:
|
|
|
198
201
|
│ └─ Complex/nested markdown → roam_import_markdown
|
|
199
202
|
│ (for deeply nested content, tables, etc.)
|
|
200
203
|
│
|
|
204
|
+
├─ Replacing/revising ENTIRE page content?
|
|
205
|
+
│ └─ roam_update_page_markdown
|
|
206
|
+
│ (fetches page internally, computes smart diff, preserves UIDs)
|
|
207
|
+
│
|
|
201
208
|
├─ Need to CREATE, UPDATE, MOVE, or DELETE individual blocks?
|
|
202
209
|
│ └─ roam_process_batch_actions
|
|
203
|
-
│ (fine-grained control,
|
|
210
|
+
│ (fine-grained control, UID placeholders for parent refs)
|
|
211
|
+
│
|
|
212
|
+
├─ Creating a TABLE?
|
|
213
|
+
│ └─ roam_create_table
|
|
214
|
+
│ (handles complex nested structure automatically)
|
|
204
215
|
│
|
|
205
216
|
├─ Adding a memory/note to remember?
|
|
206
217
|
│ └─ roam_remember (auto-tags with MEMORIES_TAG)
|
|
@@ -210,9 +221,11 @@ CREATING CONTENT IN ROAM:
|
|
|
210
221
|
│
|
|
211
222
|
└─ SEARCHING/READING:
|
|
212
223
|
├─ Find by tag → roam_search_for_tag
|
|
213
|
-
├─ Find by text → roam_search_by_text
|
|
224
|
+
├─ Find by text → roam_search_by_text
|
|
214
225
|
├─ Find by date range → roam_search_by_date
|
|
215
226
|
├─ Find by status → roam_search_by_status
|
|
227
|
+
├─ Find block/page references → roam_search_block_refs
|
|
228
|
+
├─ Find pages modified today → roam_find_pages_modified_today
|
|
216
229
|
├─ Get page content → roam_fetch_page_by_title
|
|
217
230
|
├─ Get block + children → roam_fetch_block_with_children
|
|
218
231
|
├─ Recall memories → roam_recall
|
|
@@ -226,18 +239,18 @@ CREATING CONTENT IN ROAM:
|
|
|
226
239
|
The Roam API has rate limits. Follow these guidelines to minimize API calls:
|
|
227
240
|
|
|
228
241
|
### Tool Efficiency Ranking (Best to Worst)
|
|
229
|
-
1. **`
|
|
230
|
-
2. **`
|
|
231
|
-
3. **`
|
|
232
|
-
4.
|
|
242
|
+
1. **`roam_update_page_markdown`** - Single call: fetches, diffs, and updates (MOST EFFICIENT for revisions)
|
|
243
|
+
2. **`roam_process_batch_actions`** - Single API call for multiple operations
|
|
244
|
+
3. **`roam_create_page`** - Batches content with page creation
|
|
245
|
+
4. **`roam_create_outline` / `roam_import_markdown`** - Include verification queries (use for smaller operations)
|
|
246
|
+
5. **Multiple sequential tool calls** - Each call = multiple API requests (AVOID)
|
|
233
247
|
|
|
234
248
|
### Best Practices for Intensive Operations
|
|
235
249
|
|
|
236
250
|
#### When Updating/Revising a Page:
|
|
237
|
-
1.
|
|
238
|
-
2.
|
|
239
|
-
3.
|
|
240
|
-
4. Do NOT fetch-modify-fetch-modify in a loop
|
|
251
|
+
1. **Preferred**: Use `roam_update_page_markdown` — it fetches, diffs, and updates in one call
|
|
252
|
+
2. **Alternative** (for fine-grained control): Fetch once with `roam_fetch_page_by_title`, then execute ALL changes in a SINGLE `roam_process_batch_actions` call
|
|
253
|
+
3. Do NOT fetch-modify-fetch-modify in a loop
|
|
241
254
|
|
|
242
255
|
#### When Creating Large Content:
|
|
243
256
|
- For 10+ blocks: Use `roam_process_batch_actions` with nested structure
|
|
@@ -285,6 +298,11 @@ When using `roam_process_batch_actions` to create nested blocks, use **placehold
|
|
|
285
298
|
|
|
286
299
|
**Do this:**
|
|
287
300
|
```
|
|
301
|
+
1. roam_update_page_markdown → single call handles fetch, diff, and updates
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Alternative** (when you need fine-grained control):
|
|
305
|
+
```
|
|
288
306
|
1. roam_fetch_page_by_title → get page UID and content
|
|
289
307
|
2. roam_process_batch_actions → ALL creates/updates in one call
|
|
290
308
|
```
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolver for page/block title lookups before batch execution
|
|
3
|
+
*/
|
|
4
|
+
import { q } from '@roam-research/roam-api-sdk';
|
|
5
|
+
import { capitalizeWords } from '../../tools/helpers/text.js';
|
|
6
|
+
import { formatRoamDate } from '../../utils/helpers.js';
|
|
7
|
+
import { pageUidCache } from '../../cache/page-uid-cache.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a page title to its UID, trying multiple case variations
|
|
10
|
+
*/
|
|
11
|
+
export async function resolvePageUid(graph, title) {
|
|
12
|
+
// Check cache first
|
|
13
|
+
const cachedUid = pageUidCache.get(title);
|
|
14
|
+
if (cachedUid) {
|
|
15
|
+
return cachedUid;
|
|
16
|
+
}
|
|
17
|
+
// Try different case variations
|
|
18
|
+
const variations = [
|
|
19
|
+
title,
|
|
20
|
+
capitalizeWords(title),
|
|
21
|
+
title.toLowerCase()
|
|
22
|
+
];
|
|
23
|
+
const orClause = variations.map(v => `[?e :node/title "${v}"]`).join(' ');
|
|
24
|
+
const searchQuery = `[:find ?uid .
|
|
25
|
+
:where [?e :block/uid ?uid]
|
|
26
|
+
(or ${orClause})]`;
|
|
27
|
+
const result = await q(graph, searchQuery, []);
|
|
28
|
+
const uid = (result === null || result === undefined) ? null : String(result);
|
|
29
|
+
// Cache the result
|
|
30
|
+
if (uid) {
|
|
31
|
+
pageUidCache.set(title, uid);
|
|
32
|
+
}
|
|
33
|
+
return uid;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get today's daily page title in Roam format
|
|
37
|
+
*/
|
|
38
|
+
export function getDailyPageTitle() {
|
|
39
|
+
return formatRoamDate(new Date());
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve today's daily page UID
|
|
43
|
+
*/
|
|
44
|
+
export async function resolveDailyPageUid(graph) {
|
|
45
|
+
const dailyTitle = getDailyPageTitle();
|
|
46
|
+
return resolvePageUid(graph, dailyTitle);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Collect all unique page titles that need resolution from commands
|
|
50
|
+
*/
|
|
51
|
+
export function collectPageTitles(commands) {
|
|
52
|
+
const titles = new Set();
|
|
53
|
+
for (const cmd of commands) {
|
|
54
|
+
const params = cmd.params;
|
|
55
|
+
// Commands that can have 'page' param
|
|
56
|
+
if ('page' in params && typeof params.page === 'string') {
|
|
57
|
+
titles.add(params.page);
|
|
58
|
+
}
|
|
59
|
+
// Remember command can have heading that needs parent page resolution
|
|
60
|
+
// But heading lookup is handled separately
|
|
61
|
+
// Todo/remember without explicit page need daily page
|
|
62
|
+
if (cmd.command === 'todo' || cmd.command === 'remember') {
|
|
63
|
+
if (!('page' in params) && !('pageUid' in params) && !('parent' in params)) {
|
|
64
|
+
titles.add(getDailyPageTitle());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return titles;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if any commands need daily page resolution
|
|
72
|
+
*/
|
|
73
|
+
export function needsDailyPage(commands) {
|
|
74
|
+
for (const cmd of commands) {
|
|
75
|
+
const params = cmd.params;
|
|
76
|
+
if (cmd.command === 'todo' || cmd.command === 'remember') {
|
|
77
|
+
if (!('page' in params) && !('pageUid' in params) && !('parent' in params)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve all page titles to UIDs
|
|
86
|
+
* Returns a map of title -> uid
|
|
87
|
+
*/
|
|
88
|
+
export async function resolveAllPages(graph, titles) {
|
|
89
|
+
const resolved = new Map();
|
|
90
|
+
// Resolve in parallel for efficiency
|
|
91
|
+
const entries = Array.from(titles);
|
|
92
|
+
const results = await Promise.all(entries.map(async (title) => {
|
|
93
|
+
const uid = await resolvePageUid(graph, title);
|
|
94
|
+
return [title, uid];
|
|
95
|
+
}));
|
|
96
|
+
for (const [title, uid] of results) {
|
|
97
|
+
if (uid) {
|
|
98
|
+
resolved.set(title, uid);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create initial resolution context
|
|
105
|
+
*/
|
|
106
|
+
export function createResolutionContext() {
|
|
107
|
+
return {
|
|
108
|
+
pageUids: new Map(),
|
|
109
|
+
placeholders: new Map(),
|
|
110
|
+
levelStack: [],
|
|
111
|
+
currentParent: null,
|
|
112
|
+
dailyPageUid: null
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a parent reference - could be a UID, placeholder, or page title
|
|
117
|
+
*/
|
|
118
|
+
export function resolveParentRef(ref, context) {
|
|
119
|
+
// Check if it's a placeholder reference {{name}}
|
|
120
|
+
const placeholderMatch = ref.match(/^\{\{([^}]+)\}\}$/);
|
|
121
|
+
if (placeholderMatch) {
|
|
122
|
+
const name = placeholderMatch[1];
|
|
123
|
+
return context.placeholders.get(name) || `{{uid:${name}}}`;
|
|
124
|
+
}
|
|
125
|
+
// Check if it's a resolved page title
|
|
126
|
+
if (context.pageUids.has(ref)) {
|
|
127
|
+
return context.pageUids.get(ref);
|
|
128
|
+
}
|
|
129
|
+
// Assume it's a direct UID
|
|
130
|
+
return ref;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generate a placeholder UID for tracking
|
|
134
|
+
* Returns the placeholder in {{uid:name}} format for batch processing
|
|
135
|
+
*/
|
|
136
|
+
export function generatePlaceholder(name) {
|
|
137
|
+
return `{{uid:${name}}}`;
|
|
138
|
+
}
|