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 +88 -18
- package/build/server/roam-server.js +8 -0
- package/build/tools/schemas.js +23 -2
- package/build/tools/tool-handlers.js +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ npm run build
|
|
|
28
28
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
31
|
-
The server provides
|
|
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);
|
package/build/tools/schemas.js
CHANGED
|
@@ -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');
|