roam-research-mcp 0.17.0 → 0.18.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
@@ -1,10 +1,11 @@
1
1
  # Roam Research MCP Server
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/roam-research-mcp.svg)](https://badge.fury.io/js/roam-research-mcp)
4
+ [![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
4
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
6
  [![GitHub](https://img.shields.io/github/license/2b3pro/roam-research-mcp)](https://github.com/2b3pro/roam-research-mcp/blob/main/LICENSE)
6
7
 
7
- 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.
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
9
 
9
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>
10
11
 
@@ -27,7 +28,7 @@ npm run build
27
28
 
28
29
  ## Features
29
30
 
30
- The server provides eleven powerful tools for interacting with Roam Research:
31
+ The server provides twelve powerful tools for interacting with Roam Research:
31
32
 
32
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
33
34
  2. `roam_create_page`: Create new pages with optional content
@@ -40,6 +41,7 @@ The server provides eleven powerful tools for interacting with Roam Research:
40
41
  9. `find_pages_modified_today`: Find all pages that have been modified since midnight today
41
42
  10. `roam_search_by_text`: Search for blocks containing specific text across all pages or within a specific page
42
43
  11. `roam_update_block`: Update block content with direct text or pattern-based transformations
44
+ 12. `roam_search_by_date`: Search for blocks and pages based on creation or modification dates
43
45
 
44
46
  ## Setup
45
47
 
@@ -419,6 +421,55 @@ Returns:
419
421
  }
420
422
  ```
421
423
 
424
+ ### Search By Date
425
+
426
+ Search for blocks and pages based on creation or modification dates:
427
+
428
+ ```typescript
429
+ use_mcp_tool roam-research roam_search_by_date {
430
+ "start_date": "2025-01-01",
431
+ "end_date": "2025-01-31",
432
+ "type": "modified",
433
+ "scope": "blocks",
434
+ "include_content": true
435
+ }
436
+ ```
437
+
438
+ Features:
439
+
440
+ - Search by creation date, modification date, or both
441
+ - Filter blocks, pages, or both
442
+ - Optional date range with start and end dates
443
+ - Include or exclude block/page content in results
444
+ - Sort results by timestamp
445
+ - Efficient date-based filtering using Datalog queries
446
+
447
+ Parameters:
448
+
449
+ - `start_date`: Start date in ISO format (YYYY-MM-DD) (required)
450
+ - `end_date`: End date in ISO format (YYYY-MM-DD) (optional)
451
+ - `type`: Whether to search by 'created', 'modified', or 'both' (required)
452
+ - `scope`: Whether to search 'blocks', 'pages', or 'both' (required)
453
+ - `include_content`: Whether to include the content of matching blocks/pages (optional, default: true)
454
+
455
+ Returns:
456
+
457
+ ```json
458
+ {
459
+ "success": true,
460
+ "matches": [
461
+ {
462
+ "uid": "block-or-page-uid",
463
+ "type": "block",
464
+ "time": 1704067200000,
465
+ "content": "Block or page content",
466
+ "page_title": "Page title (for blocks)"
467
+ }
468
+ ],
469
+ "message": "Found N matches for the given date range and criteria"
470
+ }
471
+ ```
472
+
422
473
  ### Find Pages Modified Today
423
474
 
424
475
  Find all pages that have been modified since midnight today:
@@ -18,7 +18,7 @@ export class RoamServer {
18
18
  this.toolHandlers = new ToolHandlers(this.graph);
19
19
  this.server = new Server({
20
20
  name: 'roam-research',
21
- version: '0.16.0',
21
+ version: '0.17.0',
22
22
  }, {
23
23
  capabilities: {
24
24
  tools: {
@@ -34,7 +34,8 @@ export class RoamServer {
34
34
  roam_search_hierarchy: {},
35
35
  find_pages_modified_today: {},
36
36
  roam_search_by_text: {},
37
- roam_update_block: {}
37
+ roam_update_block: {},
38
+ roam_search_by_date: {}
38
39
  },
39
40
  },
40
41
  });
@@ -142,6 +143,13 @@ export class RoamServer {
142
143
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
143
144
  };
144
145
  }
146
+ case 'roam_search_by_date': {
147
+ const params = request.params.arguments;
148
+ const result = await this.toolHandlers.searchByDate(params);
149
+ return {
150
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
151
+ };
152
+ }
145
153
  case 'roam_update_block': {
146
154
  const { block_uid, content, transform_pattern } = request.params.arguments;
147
155
  let result;
@@ -313,5 +313,38 @@ export const toolSchemas = {
313
313
  { required: ['transform_pattern'] }
314
314
  ]
315
315
  }
316
+ },
317
+ roam_search_by_date: {
318
+ name: 'roam_search_by_date',
319
+ description: 'Search for blocks or pages based on creation or modification dates',
320
+ inputSchema: {
321
+ type: 'object',
322
+ properties: {
323
+ start_date: {
324
+ type: 'string',
325
+ description: 'Start date in ISO format (YYYY-MM-DD)',
326
+ },
327
+ end_date: {
328
+ type: 'string',
329
+ description: 'Optional: End date in ISO format (YYYY-MM-DD)',
330
+ },
331
+ type: {
332
+ type: 'string',
333
+ enum: ['created', 'modified', 'both'],
334
+ description: 'Whether to search by creation date, modification date, or both',
335
+ },
336
+ scope: {
337
+ type: 'string',
338
+ enum: ['blocks', 'pages', 'both'],
339
+ description: 'Whether to search blocks, pages, or both',
340
+ },
341
+ include_content: {
342
+ type: 'boolean',
343
+ description: 'Whether to include the content of matching blocks/pages',
344
+ default: true,
345
+ }
346
+ },
347
+ required: ['start_date', 'type', 'scope']
348
+ }
316
349
  }
317
350
  };
@@ -900,6 +900,59 @@ export class ToolHandlers {
900
900
  message: `Found ${matches.length} block(s) containing ${primaryTagFormatted}${nearTagFormatted ? ` near ${nearTagFormatted}` : ''}`
901
901
  };
902
902
  }
903
+ async searchByDate(params) {
904
+ // Convert dates to timestamps
905
+ const startTimestamp = new Date(`${params.start_date}T00:00:00`).getTime();
906
+ const endTimestamp = params.end_date ? new Date(`${params.end_date}T23:59:59`).getTime() : undefined;
907
+ // Define rule for entity type
908
+ const entityRule = `[
909
+ [(block? ?e)
910
+ [?e :block/string]
911
+ [?e :block/page ?p]
912
+ [?p :node/title]]
913
+ [(page? ?e)
914
+ [?e :node/title]]
915
+ ]`;
916
+ // Build query based on cheatsheet pattern
917
+ const timeAttr = params.type === 'created' ? ':create/time' : ':edit/time';
918
+ let queryStr = `[:find ?block-uid ?string ?time ?page-title
919
+ :in $ ?start-ts ${endTimestamp ? '?end-ts' : ''}
920
+ :where
921
+ [?b ${timeAttr} ?time]
922
+ [(>= ?time ?start-ts)]
923
+ ${endTimestamp ? '[(<= ?time ?end-ts)]' : ''}
924
+ [?b :block/uid ?block-uid]
925
+ [?b :block/string ?string]
926
+ [?b :block/page ?p]
927
+ [?p :node/title ?page-title]]`;
928
+ // Execute query
929
+ const queryParams = endTimestamp ?
930
+ [startTimestamp, endTimestamp] :
931
+ [startTimestamp];
932
+ const results = await q(this.graph, queryStr, queryParams);
933
+ if (!results || results.length === 0) {
934
+ return {
935
+ success: true,
936
+ matches: [],
937
+ message: 'No matches found for the given date range and criteria'
938
+ };
939
+ }
940
+ // Process results - now we get [block-uid, string, time, page-title]
941
+ const matches = results.map(([uid, content, time, pageTitle]) => ({
942
+ uid,
943
+ type: 'block',
944
+ time,
945
+ ...(params.include_content && { content }),
946
+ page_title: pageTitle
947
+ }));
948
+ // Sort by time
949
+ const sortedMatches = matches.sort((a, b) => b.time - a.time);
950
+ return {
951
+ success: true,
952
+ matches: sortedMatches,
953
+ message: `Found ${sortedMatches.length} matches for the given date range and criteria`
954
+ };
955
+ }
903
956
  async addTodos(todos) {
904
957
  if (!Array.isArray(todos) || todos.length === 0) {
905
958
  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.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "A Model Context Protocol (MCP) server for Roam Research API integration",
5
5
  "private": false,
6
6
  "repository": {