roam-research-mcp 0.24.2 → 0.24.5

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.
@@ -17,7 +17,7 @@ export class RoamServer {
17
17
  this.toolHandlers = new ToolHandlers(this.graph);
18
18
  this.server = new Server({
19
19
  name: 'roam-research',
20
- version: '0.24.2',
20
+ version: '0.24.5',
21
21
  }, {
22
22
  capabilities: {
23
23
  tools: {
@@ -36,7 +36,7 @@ export class RoamServer {
36
36
  roam_find_pages_modified_today: {},
37
37
  roam_search_by_text: {},
38
38
  roam_update_block: {},
39
- roam_update_blocks: {},
39
+ roam_update_multiple_blocks: {},
40
40
  roam_search_by_date: {},
41
41
  roam_datomic_query: {}
42
42
  },
@@ -137,7 +137,8 @@ export class RoamServer {
137
137
  };
138
138
  }
139
139
  case 'roam_find_pages_modified_today': {
140
- const result = await this.toolHandlers.findPagesModifiedToday();
140
+ const { max_num_pages } = request.params.arguments;
141
+ const result = await this.toolHandlers.findPagesModifiedToday(max_num_pages || 50);
141
142
  return {
142
143
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
143
144
  };
@@ -179,7 +180,7 @@ export class RoamServer {
179
180
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
180
181
  };
181
182
  }
182
- case 'roam_update_blocks': {
183
+ case 'roam_update_multiple_blocks': {
183
184
  const { updates } = request.params.arguments;
184
185
  const result = await this.toolHandlers.updateBlocks(updates);
185
186
  return {
@@ -159,6 +159,10 @@ export class OutlineOperations {
159
159
  return createAndVerifyBlock(content, parentUid, maxRetries, initialDelay, true);
160
160
  }
161
161
  };
162
+ // Helper function to check if string is a valid Roam UID (9 characters)
163
+ const isValidUid = (str) => {
164
+ return typeof str === 'string' && str.length === 9;
165
+ };
162
166
  // Get or create the parent block
163
167
  let targetParentUid;
164
168
  if (!block_text_uid) {
@@ -166,12 +170,28 @@ export class OutlineOperations {
166
170
  }
167
171
  else {
168
172
  try {
169
- // Create header block and get its UID
170
- targetParentUid = await createAndVerifyBlock(block_text_uid, targetPageUid);
173
+ if (isValidUid(block_text_uid)) {
174
+ // First try to find block by UID
175
+ const uidQuery = `[:find ?uid
176
+ :where [?e :block/uid "${block_text_uid}"]
177
+ [?e :block/uid ?uid]]`;
178
+ const uidResult = await q(this.graph, uidQuery, []);
179
+ if (uidResult && uidResult.length > 0) {
180
+ // Use existing block if found
181
+ targetParentUid = uidResult[0][0];
182
+ }
183
+ else {
184
+ throw new McpError(ErrorCode.InvalidRequest, `Block with UID "${block_text_uid}" not found`);
185
+ }
186
+ }
187
+ else {
188
+ // Create header block and get its UID if not a valid UID
189
+ targetParentUid = await createAndVerifyBlock(block_text_uid, targetPageUid);
190
+ }
171
191
  }
172
192
  catch (error) {
173
193
  const errorMessage = error instanceof Error ? error.message : String(error);
174
- throw new McpError(ErrorCode.InternalError, `Failed to create header block "${block_text_uid}": ${errorMessage}`);
194
+ throw new McpError(ErrorCode.InternalError, `Failed to ${isValidUid(block_text_uid) ? 'find' : 'create'} block "${block_text_uid}": ${errorMessage}`);
175
195
  }
176
196
  }
177
197
  // Initialize result variable
@@ -8,7 +8,7 @@ export class PageOperations {
8
8
  constructor(graph) {
9
9
  this.graph = graph;
10
10
  }
11
- async findPagesModifiedToday(num_pages = 10) {
11
+ async findPagesModifiedToday(max_num_pages = 50) {
12
12
  // Define ancestor rule for traversing block hierarchy
13
13
  const ancestorRule = `[
14
14
  [ (ancestor ?b ?a)
@@ -29,7 +29,7 @@ export class PageOperations {
29
29
  (ancestor ?block ?page)
30
30
  [?block :edit/time ?time]
31
31
  [(> ?time ?start_of_day)]]
32
- :limit ${num_pages}`, [startOfDay.getTime(), ancestorRule]);
32
+ :limit ${max_num_pages}`, [startOfDay.getTime(), ancestorRule]);
33
33
  if (!results || results.length === 0) {
34
34
  return {
35
35
  success: true,
@@ -20,13 +20,13 @@ export const toolSchemas = {
20
20
  },
21
21
  roam_fetch_page_by_title: {
22
22
  name: 'roam_fetch_page_by_title',
23
- description: 'Retrieve complete page contents by exact title, including all nested blocks and resolved block references. Use for reading and analyzing existing Roam pages.',
23
+ description: 'Retrieve complete page contents by exact title, including all nested blocks and resolved block references. Use for accessing daily pages, reading and analyzing existing Roam pages.',
24
24
  inputSchema: {
25
25
  type: 'object',
26
26
  properties: {
27
27
  title: {
28
28
  type: 'string',
29
- description: 'Title of the page to fetch and read',
29
+ description: 'Title of the page. For date pages, use ordinal date formats such as January 2nd, 2025',
30
30
  },
31
31
  },
32
32
  required: ['title'],
@@ -249,13 +249,12 @@ export const toolSchemas = {
249
249
  inputSchema: {
250
250
  type: 'object',
251
251
  properties: {
252
- num_pages: {
252
+ max_num_pages: {
253
253
  type: 'integer',
254
- description: 'Number of result pages to retrieve (default: 10)',
255
- default: 10
254
+ description: 'Max number of pages to retrieve (default: 50)',
255
+ default: 50
256
256
  },
257
- },
258
- required: ['num_pages']
257
+ }
259
258
  }
260
259
  },
261
260
  roam_search_by_text: {
@@ -278,7 +277,7 @@ export const toolSchemas = {
278
277
  },
279
278
  roam_update_block: {
280
279
  name: 'roam_update_block',
281
- description: 'Update the content of an existing block identified by its UID. Can either provide new content directly or use a transform pattern to modify existing content.\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).',
280
+ description: 'Update a single block identified by its UID. Use this for individual block updates when you need to either replace the entire content or apply a transform pattern to modify specific parts of the content.\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).',
282
281
  inputSchema: {
283
282
  type: 'object',
284
283
  properties: {
@@ -318,9 +317,9 @@ export const toolSchemas = {
318
317
  ]
319
318
  }
320
319
  },
321
- roam_update_blocks: {
322
- name: 'roam_update_blocks',
323
- description: 'Update multiple blocks in a single batch operation. Each update can provide either new content directly or a transform pattern.\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).',
320
+ roam_update_multiple_blocks: {
321
+ name: 'roam_update_multiple_blocks',
322
+ description: 'Efficiently update multiple blocks in a single batch operation. Use this when you need to update several blocks at once to avoid making multiple separate API calls. Each block in the batch can independently either have its content replaced or transformed using a pattern.\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).',
324
323
  inputSchema: {
325
324
  type: 'object',
326
325
  properties: {
@@ -372,7 +371,7 @@ export const toolSchemas = {
372
371
  },
373
372
  roam_search_by_date: {
374
373
  name: 'roam_search_by_date',
375
- description: 'Search for blocks or pages based on creation or modification dates',
374
+ description: 'Search for blocks or pages based on creation or modification dates. Not for daily pages with ordinal date titles.',
376
375
  inputSchema: {
377
376
  type: 'object',
378
377
  properties: {
@@ -23,8 +23,8 @@ export class ToolHandlers {
23
23
  this.outlineOps = new OutlineOperations(graph);
24
24
  }
25
25
  // Page Operations
26
- async findPagesModifiedToday(num_pages = 10) {
27
- return this.pageOps.findPagesModifiedToday(num_pages);
26
+ async findPagesModifiedToday(max_num_pages = 50) {
27
+ return this.pageOps.findPagesModifiedToday(max_num_pages);
28
28
  }
29
29
  async createPage(title, content) {
30
30
  return this.pageOps.createPage(title, content);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roam-research-mcp",
3
- "version": "0.24.2",
3
+ "version": "0.24.5",
4
4
  "description": "A Model Context Protocol (MCP) server for Roam Research API integration",
5
5
  "private": false,
6
6
  "repository": {