roam-research-mcp 0.19.0 → 0.20.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 CHANGED
@@ -28,7 +28,7 @@ npm run build
28
28
 
29
29
  ## Features
30
30
 
31
- The server provides twelve powerful tools for interacting with Roam Research:
31
+ The server provides fourteen powerful tools for interacting with Roam Research:
32
32
 
33
33
  1. `roam_fetch_page_by_title`: Fetch and read a page's content by title, recursively resolving block references up to 4 levels deep
34
34
  2. `roam_create_page`: Create new pages with optional content
@@ -42,6 +42,8 @@ The server provides twelve powerful tools for interacting with Roam Research:
42
42
  10. `roam_search_by_text`: Search for blocks containing specific text across all pages or within a specific page
43
43
  11. `roam_update_block`: Update block content with direct text or pattern-based transformations
44
44
  12. `roam_search_by_date`: Search for blocks and pages based on creation or modification dates
45
+ 13. `roam_search_for_tag`: Search for blocks containing specific tags with optional filtering by nearby tags
46
+ 14. `roam_remember`: Store and categorize memories or information with automatic tagging
45
47
 
46
48
  ## Setup
47
49
 
@@ -60,28 +62,14 @@ The server provides twelve powerful tools for interacting with Roam Research:
60
62
  ```
61
63
  ROAM_API_TOKEN=your-api-token
62
64
  ROAM_GRAPH_NAME=your-graph-name
65
+ MEMORIES_TAG='#[[LLM/Memories]]'
66
+ PROFILE_PAGE='LLM/Profile' (not yet implemented)
63
67
  ```
64
68
 
65
69
  Option 2: Using MCP settings (Alternative method)
66
70
  Add the configuration to your MCP settings file:
67
71
 
68
72
  - For Cline (`~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`):
69
-
70
- ```json
71
- {
72
- "mcpServers": {
73
- "roam-research": {
74
- "command": "node",
75
- "args": ["/path/to/roam-research/build/index.js"],
76
- "env": {
77
- "ROAM_API_TOKEN": "your-api-token",
78
- "ROAM_GRAPH_NAME": "your-graph-name"
79
- }
80
- }
81
- }
82
- }
83
- ```
84
-
85
73
  - For Claude desktop app (`~/Library/Application Support/Claude/claude_desktop_config.json`):
86
74
 
87
75
  ```json
@@ -92,7 +80,9 @@ The server provides twelve powerful tools for interacting with Roam Research:
92
80
  "args": ["/path/to/roam-research/build/index.js"],
93
81
  "env": {
94
82
  "ROAM_API_TOKEN": "your-api-token",
95
- "ROAM_GRAPH_NAME": "your-graph-name"
83
+ "ROAM_GRAPH_NAME": "your-graph-name",
84
+ "MEMORIES_TAG": "#[[LLM/Memories]]",
85
+ "PROFILE_PAGE": "LLM/Profile"
96
86
  }
97
87
  }
98
88
  }
@@ -421,6 +411,86 @@ Returns:
421
411
  }
422
412
  ```
423
413
 
414
+ ### Search For Tags
415
+
416
+ Search for blocks containing specific tags with optional filtering by nearby tags:
417
+
418
+ ```typescript
419
+ use_mcp_tool roam-research roam_search_for_tag {
420
+ "primary_tag": "Project/Tasks",
421
+ "page_title_uid": "optional-page-title-or-uid",
422
+ "near_tag": "optional-secondary-tag",
423
+ "case_sensitive": true
424
+ }
425
+ ```
426
+
427
+ Features:
428
+
429
+ - Search for blocks containing specific tags
430
+ - Optional filtering by presence of another tag
431
+ - Page-scoped or graph-wide search
432
+ - Case-sensitive or case-insensitive search
433
+ - Returns block content with page context
434
+ - Efficient tag matching using Datalog queries
435
+
436
+ Parameters:
437
+
438
+ - `primary_tag`: The main tag to search for (required)
439
+ - `page_title_uid`: Title or UID of the page to search in (optional)
440
+ - `near_tag`: Another tag to filter results by (optional)
441
+ - `case_sensitive`: Whether to perform case-sensitive search (optional, default: true to match Roam's native behavior)
442
+
443
+ Returns:
444
+
445
+ ```json
446
+ {
447
+ "success": true,
448
+ "matches": [
449
+ {
450
+ "block_uid": "matching-block-uid",
451
+ "content": "Block content containing #[[primary_tag]]",
452
+ "page_title": "Page containing block"
453
+ }
454
+ ],
455
+ "message": "Found N block(s) referencing \"primary_tag\""
456
+ }
457
+ ```
458
+
459
+ ### Remember Information
460
+
461
+ Store memories or important information with automatic tagging and categorization:
462
+
463
+ ```typescript
464
+ use_mcp_tool roam-research roam_remember {
465
+ "memory": "Important information to remember",
466
+ "categories": ["Work", "Project/Alpha"]
467
+ }
468
+ ```
469
+
470
+ Features:
471
+
472
+ - Store information with #[[LLM/Memories]] tag
473
+ - Add optional category tags for organization
474
+ - Automatically adds to today's daily page
475
+ - Supports multiple categories per memory
476
+ - Easy retrieval using roam_search_for_tag
477
+ - Maintains chronological order of memories
478
+
479
+ Parameters:
480
+
481
+ - `memory`: The information to remember (required)
482
+ - `categories`: Optional array of categories to tag the memory with
483
+
484
+ Returns:
485
+
486
+ ```json
487
+ {
488
+ "success": true,
489
+ "block_uid": "created-block-uid",
490
+ "content": "Memory content with tags"
491
+ }
492
+ ```
493
+
424
494
  ### Search By Date
425
495
 
426
496
  Search for blocks and pages based on creation or modification dates:
@@ -22,6 +22,7 @@ export class RoamServer {
22
22
  }, {
23
23
  capabilities: {
24
24
  tools: {
25
+ roam_remember: {},
25
26
  roam_add_todo: {},
26
27
  roam_fetch_page_by_title: {},
27
28
  roam_create_page: {},
@@ -57,6 +58,13 @@ export class RoamServer {
57
58
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
58
59
  try {
59
60
  switch (request.params.name) {
61
+ case 'roam_remember': {
62
+ const { memory, categories } = request.params.arguments;
63
+ const result = await this.toolHandlers.remember(memory, categories);
64
+ return {
65
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
66
+ };
67
+ }
60
68
  case 'roam_fetch_page_by_title': {
61
69
  const { title } = request.params.arguments;
62
70
  const content = await this.toolHandlers.fetchPageByTitle(title);
@@ -74,7 +74,7 @@ export const toolSchemas = {
74
74
  },
75
75
  roam_create_outline: {
76
76
  name: 'roam_create_output_with_nested_structure',
77
- description: 'Create a structured outline or output with nested structure in Roam from an array of items with explicit levels. Can be added on a specific page or under a specific block.',
77
+ description: 'Create a structured outline or output with nested structure in Roam from an array of items with explicit levels. Can be added on a specific page or under a specific block. Ideal for saving a conversation with an LLM response, research, or organizing thoughts.',
78
78
  inputSchema: {
79
79
  type: 'object',
80
80
  properties: {
@@ -146,7 +146,7 @@ export const toolSchemas = {
146
146
  },
147
147
  roam_search_for_tag: {
148
148
  name: 'roam_search_for_tag',
149
- description: 'Search for blocks containing a specific tag and optionally filter by blocks that also contain another tag nearby.',
149
+ description: 'Search for blocks containing a specific tag and optionally filter by blocks that also contain another tag nearby. Example: Use this to search for memories that are tagged with the MEMORIES_TAG.',
150
150
  inputSchema: {
151
151
  type: 'object',
152
152
  properties: {
@@ -414,5 +414,26 @@ export const toolSchemas = {
414
414
  },
415
415
  required: ['start_date', 'type', 'scope']
416
416
  }
417
+ },
418
+ roam_remember: {
419
+ name: 'roam_remember',
420
+ description: 'Add a memory or piece of information to remember, stored on the daily page with #[[LLM/Memories]] tag and optional categories. Use roam_search_for_tag with "LLM/Memories" to find stored memories.\nNOTE on Roam-flavored markdown: For direct linking: use [[link]] syntax. For aliased linking, use [alias]([[link]]) syntax. Do not concatenate words in links/hashtags - correct: #[[multiple words]] #self-esteem (for typically hyphenated words).',
421
+ inputSchema: {
422
+ type: 'object',
423
+ properties: {
424
+ memory: {
425
+ type: 'string',
426
+ description: 'The memory or information to remember'
427
+ },
428
+ categories: {
429
+ type: 'array',
430
+ items: {
431
+ type: 'string'
432
+ },
433
+ description: 'Optional categories to tag the memory with (will be converted to Roam tags)'
434
+ }
435
+ },
436
+ required: ['memory']
437
+ }
417
438
  }
418
439
  };
@@ -1096,6 +1096,58 @@ export class ToolHandlers {
1096
1096
  message: `Found ${sortedMatches.length} matches for the given date range and criteria`
1097
1097
  };
1098
1098
  }
1099
+ async remember(memory, categories) {
1100
+ // Get today's date
1101
+ const today = new Date();
1102
+ const dateStr = formatRoamDate(today);
1103
+ // Try to find today's page
1104
+ const findQuery = `[:find ?uid :in $ ?title :where [?e :node/title ?title] [?e :block/uid ?uid]]`;
1105
+ const findResults = await q(this.graph, findQuery, [dateStr]);
1106
+ let pageUid;
1107
+ if (findResults && findResults.length > 0) {
1108
+ pageUid = findResults[0][0];
1109
+ }
1110
+ else {
1111
+ // Create today's page if it doesn't exist
1112
+ const success = await createPage(this.graph, {
1113
+ action: 'create-page',
1114
+ page: { title: dateStr }
1115
+ });
1116
+ if (!success) {
1117
+ throw new McpError(ErrorCode.InternalError, 'Failed to create today\'s page');
1118
+ }
1119
+ // Get the new page's UID
1120
+ const results = await q(this.graph, findQuery, [dateStr]);
1121
+ if (!results || results.length === 0) {
1122
+ throw new McpError(ErrorCode.InternalError, 'Could not find created today\'s page');
1123
+ }
1124
+ pageUid = results[0][0];
1125
+ }
1126
+ // Get memories tag from environment
1127
+ const memoriesTag = process.env.MEMORIES_TAG;
1128
+ if (!memoriesTag) {
1129
+ throw new McpError(ErrorCode.InternalError, 'MEMORIES_TAG environment variable not set');
1130
+ }
1131
+ // Format categories as Roam tags if provided
1132
+ const categoryTags = categories?.map(cat => {
1133
+ // Handle multi-word categories
1134
+ return cat.includes(' ') ? `#[[${cat}]]` : `#${cat}`;
1135
+ }).join(' ') || '';
1136
+ // Create block with memory, memories tag, and optional categories
1137
+ const blockContent = `${memoriesTag} ${memory} ${categoryTags}`.trim();
1138
+ const success = await createBlock(this.graph, {
1139
+ action: 'create-block',
1140
+ location: {
1141
+ "parent-uid": pageUid,
1142
+ "order": "last"
1143
+ },
1144
+ block: { string: blockContent }
1145
+ });
1146
+ if (!success) {
1147
+ throw new McpError(ErrorCode.InternalError, 'Failed to create memory block');
1148
+ }
1149
+ return { success: true };
1150
+ }
1099
1151
  async addTodos(todos) {
1100
1152
  if (!Array.isArray(todos) || todos.length === 0) {
1101
1153
  throw new McpError(ErrorCode.InvalidRequest, 'todos must be a non-empty array');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roam-research-mcp",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "A Model Context Protocol (MCP) server for Roam Research API integration",
5
5
  "private": false,
6
6
  "repository": {