roam-research-mcp 0.22.0 → 0.23.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
@@ -5,7 +5,7 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![GitHub](https://img.shields.io/github/license/2b3pro/roam-research-mcp)](https://github.com/2b3pro/roam-research-mcp/blob/main/LICENSE)
7
7
 
8
- A Model Context Protocol (MCP) server that provides comprehensive access to Roam Research's API functionality. This server enables AI assistants like Claude to interact with your Roam Research graph through a standardized interface. (A WORK-IN-PROGRESS)
8
+ A Model Context Protocol (MCP) server that provides comprehensive access to Roam Research's API functionality. This server enables AI assistants like Claude to interact with your Roam Research graph through a standardized interface. (A WORK-IN-PROGRESS, personal project not officially endorsed by Roam Research)
9
9
 
10
10
  <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>
11
11
 
@@ -28,7 +28,7 @@ npm run build
28
28
 
29
29
  ## Features
30
30
 
31
- The server provides fourteen powerful tools for interacting with Roam Research:
31
+ The server provides 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
@@ -44,7 +44,8 @@ The server provides fourteen powerful tools for interacting with Roam Research:
44
44
  12. `roam_search_by_date`: Search for blocks and pages based on creation or modification dates
45
45
  13. `roam_search_for_tag`: Search for blocks containing specific tags with optional filtering by nearby tags
46
46
  14. `roam_remember`: Store and categorize memories or information with automatic tagging
47
- 15. `roam_recall`: Recall memories of blocks marked with tag MEMORIES_TAG (see below) or blocks on page title of the same name.
47
+ 15. `roam_recall`: Recall memories of blocks marked with tag MEMORIES_TAG (see below) or blocks on page title of the same name
48
+ 16. `roam_datomic_query`: Execute custom Datalog queries on the Roam graph for advanced data retrieval and analysis
48
49
 
49
50
  ## Setup
50
51
 
@@ -64,7 +65,6 @@ The server provides fourteen powerful tools for interacting with Roam Research:
64
65
  ROAM_API_TOKEN=your-api-token
65
66
  ROAM_GRAPH_NAME=your-graph-name
66
67
  MEMORIES_TAG='#[[LLM/Memories]]'
67
- PROFILE_PAGE='LLM/Profile' (not yet implemented)
68
68
  ```
69
69
 
70
70
  Option 2: Using MCP settings (Alternative method)
@@ -82,8 +82,7 @@ The server provides fourteen powerful tools for interacting with Roam Research:
82
82
  "env": {
83
83
  "ROAM_API_TOKEN": "your-api-token",
84
84
  "ROAM_GRAPH_NAME": "your-graph-name",
85
- "MEMORIES_TAG": "#[[LLM/Memories]]",
86
- "PROFILE_PAGE": "LLM/Profile"
85
+ "MEMORIES_TAG": "#[[LLM/Memories]]"
87
86
  }
88
87
  }
89
88
  }
@@ -567,6 +566,85 @@ Returns:
567
566
  }
568
567
  ```
569
568
 
569
+ ### Execute Datomic Queries
570
+
571
+ Execute custom Datalog queries on your Roam graph for advanced data retrieval and analysis:
572
+
573
+ ```typescript
574
+ use_mcp_tool roam-research roam_datomic_query {
575
+ "query": "[:find (count ?p)\n :where [?p :node/title]]",
576
+ "inputs": []
577
+ }
578
+ ```
579
+
580
+ Features:
581
+
582
+ - Direct access to Roam's query engine
583
+ - Support for all Datalog query features:
584
+ - Complex pattern matching
585
+ - Aggregation functions (count, sum, max, min, avg, distinct)
586
+ - String operations (includes?, starts-with?, ends-with?)
587
+ - Logical operations (<, >, <=, >=, =, not=)
588
+ - Rules for recursive queries
589
+ - Case-sensitive and case-insensitive search capabilities
590
+ - Efficient querying across the entire graph
591
+
592
+ Parameters:
593
+
594
+ - `query`: The Datalog query to execute (required)
595
+ - `inputs`: Optional array of input parameters for the query
596
+
597
+ Returns:
598
+
599
+ ```json
600
+ {
601
+ "success": true,
602
+ "matches": [
603
+ {
604
+ "content": "[result data]",
605
+ "block_uid": "",
606
+ "page_title": ""
607
+ }
608
+ ],
609
+ "message": "Query executed successfully. Found N results."
610
+ }
611
+ ```
612
+
613
+ Example Queries:
614
+
615
+ 1. Count all pages:
616
+
617
+ ```clojure
618
+ [:find (count ?p)
619
+ :where [?p :node/title]]
620
+ ```
621
+
622
+ 2. Case-insensitive text search:
623
+
624
+ ```clojure
625
+ [:find ?string ?title
626
+ :where
627
+ [?b :block/string ?string]
628
+ [(clojure.string/lower-case ?string) ?lower]
629
+ [(clojure.string/includes? ?lower "search term")]
630
+ [?b :block/page ?p]
631
+ [?p :node/title ?title]]
632
+ ```
633
+
634
+ 3. Find blocks modified after a date:
635
+
636
+ ```clojure
637
+ [:find ?block_ref ?string
638
+ :in $ ?start_of_day
639
+ :where
640
+ [?b :edit/time ?time]
641
+ [(> ?time ?start_of_day)]
642
+ [?b :block/uid ?block_ref]
643
+ [?b :block/string ?string]]
644
+ ```
645
+
646
+ See Roam_Research_Datalog_Cheatsheet.md for more query examples and syntax documentation.
647
+
570
648
  ### Search Block Hierarchy
571
649
 
572
650
  Navigate and search through block parent-child relationships:
@@ -0,0 +1,31 @@
1
+ import { q } from '@roam-research/roam-api-sdk';
2
+ import { BaseSearchHandler } from './types.js';
3
+ export class DatomicSearchHandler extends BaseSearchHandler {
4
+ params;
5
+ constructor(graph, params) {
6
+ super(graph);
7
+ this.params = params;
8
+ }
9
+ async execute() {
10
+ try {
11
+ // Execute the datomic query using the Roam API
12
+ const results = await q(this.graph, this.params.query, this.params.inputs || []);
13
+ return {
14
+ success: true,
15
+ matches: results.map(result => ({
16
+ content: JSON.stringify(result),
17
+ block_uid: '', // Datomic queries may not always return block UIDs
18
+ page_title: '' // Datomic queries may not always return page titles
19
+ })),
20
+ message: `Query executed successfully. Found ${results.length} results.`
21
+ };
22
+ }
23
+ catch (error) {
24
+ return {
25
+ success: false,
26
+ matches: [],
27
+ message: `Failed to execute query: ${error instanceof Error ? error.message : String(error)}`
28
+ };
29
+ }
30
+ }
31
+ }
@@ -5,3 +5,4 @@ export * from './status-search.js';
5
5
  export * from './block-ref-search.js';
6
6
  export * from './hierarchy-search.js';
7
7
  export * from './text-search.js';
8
+ export * from './datomic-search.js';
@@ -37,7 +37,8 @@ export class RoamServer {
37
37
  roam_search_by_text: {},
38
38
  roam_update_block: {},
39
39
  roam_update_blocks: {},
40
- roam_search_by_date: {}
40
+ roam_search_by_date: {},
41
+ roam_datomic_query: {}
41
42
  },
42
43
  },
43
44
  });
@@ -184,6 +185,13 @@ export class RoamServer {
184
185
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
185
186
  };
186
187
  }
188
+ case 'roam_datomic_query': {
189
+ const { query, inputs } = request.params.arguments;
190
+ const result = await this.toolHandlers.executeDatomicQuery({ query, inputs });
191
+ return {
192
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
193
+ };
194
+ }
187
195
  default:
188
196
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
189
197
  }
@@ -68,9 +68,9 @@ export class MemoryOperations {
68
68
  }
69
69
  async recall() {
70
70
  // Get memories tag from environment
71
- const memoriesTag = process.env.MEMORIES_TAG;
71
+ var memoriesTag = process.env.MEMORIES_TAG;
72
72
  if (!memoriesTag) {
73
- throw new McpError(ErrorCode.InternalError, 'MEMORIES_TAG environment variable not set');
73
+ memoriesTag = "Memories";
74
74
  }
75
75
  // Extract the tag text, removing any formatting
76
76
  const tagText = memoriesTag
@@ -1,4 +1,4 @@
1
- import { TagSearchHandler, BlockRefSearchHandler, HierarchySearchHandler, TextSearchHandler } from '../../../search/index.js';
1
+ import { TagSearchHandler, BlockRefSearchHandler, HierarchySearchHandler, TextSearchHandler, DatomicSearchHandler } from '../../../search/index.js';
2
2
  // Base class for all search handlers
3
3
  export class BaseSearchHandler {
4
4
  graph;
@@ -54,3 +54,15 @@ export class TextSearchHandlerImpl extends BaseSearchHandler {
54
54
  return handler.execute();
55
55
  }
56
56
  }
57
+ // Datomic query handler
58
+ export class DatomicSearchHandlerImpl extends BaseSearchHandler {
59
+ params;
60
+ constructor(graph, params) {
61
+ super(graph);
62
+ this.params = params;
63
+ }
64
+ async execute() {
65
+ const handler = new DatomicSearchHandler(this.graph, this.params);
66
+ return handler.execute();
67
+ }
68
+ }
@@ -399,7 +399,7 @@ export const toolSchemas = {
399
399
  },
400
400
  roam_remember: {
401
401
  name: 'roam_remember',
402
- 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).',
402
+ description: 'Add a memory or piece of information to remember, stored on the daily page with #[[LLM/Memories]] tag and optional categories. \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).',
403
403
  inputSchema: {
404
404
  type: 'object',
405
405
  properties: {
@@ -426,5 +426,26 @@ export const toolSchemas = {
426
426
  properties: {},
427
427
  required: []
428
428
  }
429
+ },
430
+ roam_datomic_query: {
431
+ name: 'roam_datomic_query',
432
+ description: 'Execute a custom Datomic query on the Roam graph. This provides direct access to Roam\'s query engine for advanced data retrieval. Note: The Roam graph is case-sensitive.\nA list of some of Roam\'s data model Namespaces and Attributes: ancestor (descendants), attrs (lookup), block (children, heading, open, order, page, parents, props, refs, string, text-align, uid), children (view-type), create (email, time), descendant (ancestors), edit (email, seen-by, time), entity (attrs), log (id), node (title), page (uid, title), refs (text).\nPredicates (clojure.string/includes?, clojure.string/starts-with?, clojure.string/ends-with?, <, >, <=, >=, =, not=, !=).\nAggregates (distinct, count, sum, max, min, avg).\nTips: Use :block/parents for all ancestor levels, :block/children for direct descendants only; combine clojure.string for complex matching, use distinct to deduplicate, leverage Pull patterns for hierarchies, handle case-sensitivity carefully, and chain ancestry rules for multi-level queries.',
433
+ inputSchema: {
434
+ type: 'object',
435
+ properties: {
436
+ query: {
437
+ type: 'string',
438
+ description: 'The Datomic query to execute (in Datalog syntax)'
439
+ },
440
+ inputs: {
441
+ type: 'array',
442
+ description: 'Optional array of input parameters for the query',
443
+ items: {
444
+ type: 'string'
445
+ }
446
+ }
447
+ },
448
+ required: ['query']
449
+ }
429
450
  }
430
451
  };
@@ -4,6 +4,7 @@ import { SearchOperations } from './operations/search/index.js';
4
4
  import { MemoryOperations } from './operations/memory.js';
5
5
  import { TodoOperations } from './operations/todos.js';
6
6
  import { OutlineOperations } from './operations/outline.js';
7
+ import { DatomicSearchHandlerImpl } from './operations/search/handlers.js';
7
8
  export class ToolHandlers {
8
9
  graph;
9
10
  pageOps;
@@ -60,6 +61,11 @@ export class ToolHandlers {
60
61
  async searchByDate(params) {
61
62
  return this.searchOps.searchByDate(params);
62
63
  }
64
+ // Datomic query
65
+ async executeDatomicQuery(params) {
66
+ const handler = new DatomicSearchHandlerImpl(this.graph, params);
67
+ return handler.execute();
68
+ }
63
69
  // Memory Operations
64
70
  async remember(memory, categories) {
65
71
  return this.memoryOps.remember(memory, categories);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roam-research-mcp",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "A Model Context Protocol (MCP) server for Roam Research API integration",
5
5
  "private": false,
6
6
  "repository": {