roam-research-mcp 0.23.1 → 0.24.1

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.
@@ -1,6 +1,7 @@
1
1
  import { q } from '@roam-research/roam-api-sdk';
2
2
  import { BaseSearchHandler } from './types.js';
3
3
  import { SearchUtils } from './utils.js';
4
+ import { resolveRefs } from '../tools/helpers/refs.js';
4
5
  export class BlockRefSearchHandler extends BaseSearchHandler {
5
6
  params;
6
7
  constructor(graph, params) {
@@ -63,10 +64,15 @@ export class BlockRefSearchHandler extends BaseSearchHandler {
63
64
  queryParams = [];
64
65
  }
65
66
  }
66
- const results = await q(this.graph, queryStr, queryParams);
67
+ const rawResults = await q(this.graph, queryStr, queryParams);
68
+ // Resolve block references in content
69
+ const resolvedResults = await Promise.all(rawResults.map(async ([uid, content, pageTitle]) => {
70
+ const resolvedContent = await resolveRefs(this.graph, content);
71
+ return [uid, resolvedContent, pageTitle];
72
+ }));
67
73
  const searchDescription = block_uid
68
74
  ? `referencing block ((${block_uid}))`
69
75
  : 'containing block references';
70
- return SearchUtils.formatSearchResults(results, searchDescription, !targetPageUid);
76
+ return SearchUtils.formatSearchResults(resolvedResults, searchDescription, !targetPageUid);
71
77
  }
72
78
  }
@@ -1,6 +1,7 @@
1
1
  import { q } from '@roam-research/roam-api-sdk';
2
2
  import { BaseSearchHandler } from './types.js';
3
3
  import { SearchUtils } from './utils.js';
4
+ import { resolveRefs } from '../tools/helpers/refs.js';
4
5
  export class HierarchySearchHandler extends BaseSearchHandler {
5
6
  params;
6
7
  constructor(graph, params) {
@@ -85,13 +86,16 @@ export class HierarchySearchHandler extends BaseSearchHandler {
85
86
  queryParams = [ancestorRule, child_uid];
86
87
  }
87
88
  }
88
- const results = await q(this.graph, queryStr, queryParams);
89
- // Format results to include depth information
90
- const matches = results.map(([uid, content, pageTitle, depth]) => ({
91
- block_uid: uid,
92
- content,
93
- depth: depth || 1,
94
- ...(pageTitle && { page_title: pageTitle })
89
+ const rawResults = await q(this.graph, queryStr, queryParams);
90
+ // Resolve block references and format results to include depth information
91
+ const matches = await Promise.all(rawResults.map(async ([uid, content, pageTitle, depth]) => {
92
+ const resolvedContent = await resolveRefs(this.graph, content);
93
+ return {
94
+ block_uid: uid,
95
+ content: resolvedContent,
96
+ depth: depth || 1,
97
+ ...(pageTitle && { page_title: pageTitle })
98
+ };
95
99
  }));
96
100
  const searchDescription = parent_uid
97
101
  ? `descendants of block ${parent_uid}`
@@ -1,6 +1,7 @@
1
1
  import { q } from '@roam-research/roam-api-sdk';
2
2
  import { BaseSearchHandler } from './types.js';
3
3
  import { SearchUtils } from './utils.js';
4
+ import { resolveRefs } from '../tools/helpers/refs.js';
4
5
  export class StatusSearchHandler extends BaseSearchHandler {
5
6
  params;
6
7
  constructor(graph, params) {
@@ -37,7 +38,12 @@ export class StatusSearchHandler extends BaseSearchHandler {
37
38
  [(clojure.string/includes? ?block-str (str "{{[[" ?status "]]}}"))]]`;
38
39
  queryParams = [status];
39
40
  }
40
- const results = await q(this.graph, queryStr, queryParams);
41
- return SearchUtils.formatSearchResults(results, `with status ${status}`, !targetPageUid);
41
+ const rawResults = await q(this.graph, queryStr, queryParams);
42
+ // Resolve block references in content
43
+ const resolvedResults = await Promise.all(rawResults.map(async ([uid, content, pageTitle]) => {
44
+ const resolvedContent = await resolveRefs(this.graph, content);
45
+ return [uid, resolvedContent, pageTitle];
46
+ }));
47
+ return SearchUtils.formatSearchResults(resolvedResults, `with status ${status}`, !targetPageUid);
42
48
  }
43
49
  }
@@ -1,6 +1,7 @@
1
1
  import { q } from '@roam-research/roam-api-sdk';
2
2
  import { BaseSearchHandler } from './types.js';
3
3
  import { SearchUtils } from './utils.js';
4
+ import { resolveRefs } from '../tools/helpers/refs.js';
4
5
  export class TagSearchHandler extends BaseSearchHandler {
5
6
  params;
6
7
  constructor(graph, params) {
@@ -28,8 +29,13 @@ export class TagSearchHandler extends BaseSearchHandler {
28
29
  [?b :block/page ?p]
29
30
  [?p :node/title ?page-title]]`;
30
31
  const queryParams = [primary_tag];
31
- const results = await q(this.graph, queryStr, queryParams);
32
+ const rawResults = await q(this.graph, queryStr, queryParams);
33
+ // Resolve block references in content
34
+ const resolvedResults = await Promise.all(rawResults.map(async ([uid, content, pageTitle]) => {
35
+ const resolvedContent = await resolveRefs(this.graph, content);
36
+ return [uid, resolvedContent, pageTitle];
37
+ }));
32
38
  const searchDescription = `referencing "${primary_tag}"`;
33
- return SearchUtils.formatSearchResults(results, searchDescription, !targetPageUid);
39
+ return SearchUtils.formatSearchResults(resolvedResults, searchDescription, !targetPageUid);
34
40
  }
35
41
  }
@@ -1,6 +1,7 @@
1
1
  import { q } from '@roam-research/roam-api-sdk';
2
2
  import { BaseSearchHandler } from './types.js';
3
3
  import { SearchUtils } from './utils.js';
4
+ import { resolveRefs } from '../tools/helpers/refs.js';
4
5
  export class TextSearchHandler extends BaseSearchHandler {
5
6
  params;
6
7
  constructor(graph, params) {
@@ -24,8 +25,13 @@ export class TextSearchHandler extends BaseSearchHandler {
24
25
  [?b :block/page ?p]
25
26
  [?p :node/title ?page-title]]`;
26
27
  const queryParams = [text];
27
- const results = await q(this.graph, queryStr, queryParams);
28
+ const rawResults = await q(this.graph, queryStr, queryParams);
29
+ // Resolve block references in content
30
+ const resolvedResults = await Promise.all(rawResults.map(async ([uid, content, pageTitle]) => {
31
+ const resolvedContent = await resolveRefs(this.graph, content);
32
+ return [uid, resolvedContent, pageTitle];
33
+ }));
28
34
  const searchDescription = `containing "${text}"`;
29
- return SearchUtils.formatSearchResults(results, searchDescription, !targetPageUid);
35
+ return SearchUtils.formatSearchResults(resolvedResults, searchDescription, !targetPageUid);
30
36
  }
31
37
  }
@@ -87,7 +87,7 @@ export class PageOperations {
87
87
  // Use import_nested_markdown functionality
88
88
  const convertedContent = convertToRoamMarkdown(content);
89
89
  const nodes = parseMarkdown(convertedContent);
90
- const actions = convertToRoamActions(nodes, pageUid, 'last');
90
+ const actions = convertToRoamActions(nodes, pageUid, 'first');
91
91
  const result = await batchActions(this.graph, {
92
92
  action: 'batch-actions',
93
93
  actions
@@ -1,4 +1,4 @@
1
- import { TagSearchHandler, BlockRefSearchHandler, HierarchySearchHandler, TextSearchHandler, DatomicSearchHandler } from '../../../search/index.js';
1
+ import { TagSearchHandler, BlockRefSearchHandler, HierarchySearchHandler, TextSearchHandler, DatomicSearchHandler, StatusSearchHandler } from '../../../search/index.js';
2
2
  // Base class for all search handlers
3
3
  export class BaseSearchHandler {
4
4
  graph;
@@ -54,6 +54,18 @@ export class TextSearchHandlerImpl extends BaseSearchHandler {
54
54
  return handler.execute();
55
55
  }
56
56
  }
57
+ // Status search handler
58
+ export class StatusSearchHandlerImpl extends BaseSearchHandler {
59
+ params;
60
+ constructor(graph, params) {
61
+ super(graph);
62
+ this.params = params;
63
+ }
64
+ async execute() {
65
+ const handler = new StatusSearchHandler(this.graph, this.params);
66
+ return handler.execute();
67
+ }
68
+ }
57
69
  // Datomic query handler
58
70
  export class DatomicSearchHandlerImpl extends BaseSearchHandler {
59
71
  params;
@@ -1,12 +1,12 @@
1
- import { TagSearchHandlerImpl, BlockRefSearchHandlerImpl, HierarchySearchHandlerImpl, TextSearchHandlerImpl } from './handlers.js';
1
+ import { TagSearchHandlerImpl, BlockRefSearchHandlerImpl, HierarchySearchHandlerImpl, TextSearchHandlerImpl, StatusSearchHandlerImpl } from './handlers.js';
2
2
  export class SearchOperations {
3
3
  graph;
4
4
  constructor(graph) {
5
5
  this.graph = graph;
6
6
  }
7
7
  async searchByStatus(status, page_title_uid, include, exclude) {
8
- const handler = new TagSearchHandlerImpl(this.graph, {
9
- primary_tag: `{{[[${status}]]}}`,
8
+ const handler = new StatusSearchHandlerImpl(this.graph, {
9
+ status,
10
10
  page_title_uid,
11
11
  });
12
12
  const result = await handler.execute();
@@ -14,7 +14,7 @@ export class SearchOperations {
14
14
  let matches = result.matches;
15
15
  if (include) {
16
16
  const includeTerms = include.split(',').map(term => term.trim());
17
- matches = matches.filter(match => {
17
+ matches = matches.filter((match) => {
18
18
  const matchContent = match.content;
19
19
  const matchTitle = match.page_title;
20
20
  const terms = includeTerms;
@@ -24,7 +24,7 @@ export class SearchOperations {
24
24
  }
25
25
  if (exclude) {
26
26
  const excludeTerms = exclude.split(',').map(term => term.trim());
27
- matches = matches.filter(match => {
27
+ matches = matches.filter((match) => {
28
28
  const matchContent = match.content;
29
29
  const matchTitle = match.page_title;
30
30
  const terms = excludeTerms;
@@ -34,7 +34,7 @@ export const toolSchemas = {
34
34
  },
35
35
  roam_create_page: {
36
36
  name: 'roam_create_page',
37
- description: 'Create a new standalone page in Roam from markdown with given title. Best for hierarchical content, reference materials, markdown tables, and topics that deserve their own namespace. Optional initial content will be properly nested as blocks.',
37
+ description: 'Create a new standalone page in Roam with optional content using markdown-style formatting. The nesting structure is inferred from markdown indentation (spaces). Best for:\n- Creating foundational concept pages that other pages will link to/from\n- Establishing new topic areas that need their own namespace\n- Setting up reference materials or documentation\n- Making permanent collections of information.',
38
38
  inputSchema: {
39
39
  type: 'object',
40
40
  properties: {
@@ -44,7 +44,7 @@ export const toolSchemas = {
44
44
  },
45
45
  content: {
46
46
  type: 'string',
47
- description: 'Initial content for the page (optional)',
47
+ description: 'Initial content for the page (optional). Each line becomes a separate block. Indentation (using spaces or tabs) determines the nesting level of each block.',
48
48
  },
49
49
  },
50
50
  required: ['title'],
@@ -74,7 +74,7 @@ export const toolSchemas = {
74
74
  },
75
75
  roam_create_outline: {
76
76
  name: 'roam_create_outline',
77
- description: 'Create a structured outline 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.',
77
+ description: 'Add a structured outline to an existing page or block (by title text or uid), with customizable nesting levels. Best for:\n- Adding supplementary structured content to existing pages\n- Creating temporary or working outlines (meeting notes, brainstorms)\n- Organizing thoughts or research under a specific topic\n- Breaking down subtopics or components of a larger concept',
78
78
  inputSchema: {
79
79
  type: 'object',
80
80
  properties: {
@@ -88,7 +88,7 @@ export const toolSchemas = {
88
88
  },
89
89
  outline: {
90
90
  type: 'array',
91
- description: 'Array of outline items with block text and level',
91
+ description: 'Array of outline items with block text and explicit nesting level',
92
92
  items: {
93
93
  type: 'object',
94
94
  properties: {
@@ -429,7 +429,7 @@ export const toolSchemas = {
429
429
  },
430
430
  roam_datomic_query: {
431
431
  name: 'roam_datomic_query',
432
- description: 'Execute a custom Datomic query on the Roam graph beyond the available search tools. 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.',
432
+ description: 'Execute a custom Datomic query on the Roam graph beyond the available search tools. This provides direct access to Roam\'s query engine for advanced data retrieval. Note: Roam graph is case-sensitive.\nList 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, limit).\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
433
  inputSchema: {
434
434
  type: 'object',
435
435
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roam-research-mcp",
3
- "version": "0.23.1",
3
+ "version": "0.24.1",
4
4
  "description": "A Model Context Protocol (MCP) server for Roam Research API integration",
5
5
  "private": false,
6
6
  "repository": {