roam-research-mcp 0.24.1 → 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.
- package/README.md +42 -18
- package/build/server/roam-server.js +9 -7
- package/build/tools/operations/memory.js +69 -23
- package/build/tools/operations/outline.js +23 -3
- package/build/tools/operations/pages.js +3 -2
- package/build/tools/schemas.js +32 -17
- package/build/tools/tool-handlers.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,16 @@ npm run build
|
|
|
30
30
|
|
|
31
31
|
The server provides powerful tools for interacting with Roam Research:
|
|
32
32
|
|
|
33
|
+
- Environment variable handling with .env support
|
|
34
|
+
- Comprehensive input validation
|
|
35
|
+
- Case-insensitive page title matching
|
|
36
|
+
- Recursive block reference resolution
|
|
37
|
+
- Markdown parsing and conversion
|
|
38
|
+
- Daily page integration
|
|
39
|
+
- Detailed debug logging
|
|
40
|
+
- Efficient batch operations
|
|
41
|
+
- Hierarchical outline creation
|
|
42
|
+
|
|
33
43
|
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
44
|
2. `roam_create_page`: Create new pages with optional content
|
|
35
45
|
3. `roam_create_block`: Create new blocks in a page (defaults to today's daily page)
|
|
@@ -38,7 +48,7 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
38
48
|
6. `roam_create_outline`: Create hierarchical outlines with proper nesting and structure
|
|
39
49
|
7. `roam_search_block_refs`: Search for block references within pages or across the graph
|
|
40
50
|
8. `roam_search_hierarchy`: Navigate and search through block parent-child relationships
|
|
41
|
-
9. `
|
|
51
|
+
9. `roam_find_pages_modified_today`: Find all pages that have been modified since midnight today
|
|
42
52
|
10. `roam_search_by_text`: Search for blocks containing specific text across all pages or within a specific page
|
|
43
53
|
11. `roam_update_block`: Update block content with direct text or pattern-based transformations
|
|
44
54
|
12. `roam_search_by_date`: Search for blocks and pages based on creation or modification dates
|
|
@@ -545,7 +555,7 @@ Returns:
|
|
|
545
555
|
Find all pages that have been modified since midnight today:
|
|
546
556
|
|
|
547
557
|
```typescript
|
|
548
|
-
use_mcp_tool roam-research
|
|
558
|
+
use_mcp_tool roam-research roam_find_pages_modified_today {}
|
|
549
559
|
```
|
|
550
560
|
|
|
551
561
|
Features:
|
|
@@ -717,25 +727,39 @@ Each error response includes:
|
|
|
717
727
|
|
|
718
728
|
## Development
|
|
719
729
|
|
|
720
|
-
|
|
730
|
+
### Building
|
|
721
731
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
732
|
+
To build the server:
|
|
733
|
+
|
|
734
|
+
```bash
|
|
735
|
+
npm install
|
|
736
|
+
npm run build
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
This will:
|
|
740
|
+
|
|
741
|
+
1. Install all required dependencies
|
|
742
|
+
2. Compile TypeScript to JavaScript
|
|
743
|
+
3. Make the output file executable
|
|
744
|
+
|
|
745
|
+
You can also use `npm run watch` during development to automatically recompile when files change.
|
|
746
|
+
|
|
747
|
+
### Testing with MCP Inspector
|
|
748
|
+
|
|
749
|
+
The MCP Inspector is a tool that helps test and debug MCP servers. To test the server:
|
|
750
|
+
|
|
751
|
+
```bash
|
|
752
|
+
# Inspect with npx:
|
|
753
|
+
npx @modelcontextprotocol/inspector node build/index.js
|
|
754
|
+
```
|
|
731
755
|
|
|
732
|
-
|
|
756
|
+
This will:
|
|
733
757
|
|
|
734
|
-
1.
|
|
735
|
-
2.
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
758
|
+
1. Start the server in inspector mode
|
|
759
|
+
2. Provide an interactive interface to:
|
|
760
|
+
- List available tools and resources
|
|
761
|
+
- Execute tools with custom parameters
|
|
762
|
+
- View tool responses and error handling
|
|
739
763
|
|
|
740
764
|
## License
|
|
741
765
|
|
|
@@ -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.
|
|
20
|
+
version: '0.24.5',
|
|
21
21
|
}, {
|
|
22
22
|
capabilities: {
|
|
23
23
|
tools: {
|
|
@@ -33,10 +33,10 @@ export class RoamServer {
|
|
|
33
33
|
roam_search_by_status: {},
|
|
34
34
|
roam_search_block_refs: {},
|
|
35
35
|
roam_search_hierarchy: {},
|
|
36
|
-
|
|
36
|
+
roam_find_pages_modified_today: {},
|
|
37
37
|
roam_search_by_text: {},
|
|
38
38
|
roam_update_block: {},
|
|
39
|
-
|
|
39
|
+
roam_update_multiple_blocks: {},
|
|
40
40
|
roam_search_by_date: {},
|
|
41
41
|
roam_datomic_query: {}
|
|
42
42
|
},
|
|
@@ -136,8 +136,9 @@ export class RoamServer {
|
|
|
136
136
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
case '
|
|
140
|
-
const
|
|
139
|
+
case 'roam_find_pages_modified_today': {
|
|
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
|
};
|
|
@@ -173,12 +174,13 @@ export class RoamServer {
|
|
|
173
174
|
};
|
|
174
175
|
}
|
|
175
176
|
case 'roam_recall': {
|
|
176
|
-
const
|
|
177
|
+
const { sort_by = 'newest', filter_tag } = request.params.arguments;
|
|
178
|
+
const result = await this.toolHandlers.recall(sort_by, filter_tag);
|
|
177
179
|
return {
|
|
178
180
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
179
181
|
};
|
|
180
182
|
}
|
|
181
|
-
case '
|
|
183
|
+
case 'roam_update_multiple_blocks': {
|
|
182
184
|
const { updates } = request.params.arguments;
|
|
183
185
|
const result = await this.toolHandlers.updateBlocks(updates);
|
|
184
186
|
return {
|
|
@@ -66,7 +66,7 @@ export class MemoryOperations {
|
|
|
66
66
|
}
|
|
67
67
|
return { success: true };
|
|
68
68
|
}
|
|
69
|
-
async recall() {
|
|
69
|
+
async recall(sort_by = 'newest', filter_tag) {
|
|
70
70
|
// Get memories tag from environment
|
|
71
71
|
var memoriesTag = process.env.MEMORIES_TAG;
|
|
72
72
|
if (!memoriesTag) {
|
|
@@ -76,27 +76,73 @@ export class MemoryOperations {
|
|
|
76
76
|
const tagText = memoriesTag
|
|
77
77
|
.replace(/^#/, '') // Remove leading #
|
|
78
78
|
.replace(/^\[\[/, '').replace(/\]\]$/, ''); // Remove [[ and ]]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
79
|
+
try {
|
|
80
|
+
// Get page blocks using query to access actual block content
|
|
81
|
+
const ancestorRule = `[
|
|
82
|
+
[ (ancestor ?b ?a)
|
|
83
|
+
[?a :block/children ?b] ]
|
|
84
|
+
[ (ancestor ?b ?a)
|
|
85
|
+
[?parent :block/children ?b]
|
|
86
|
+
(ancestor ?parent ?a) ]
|
|
87
|
+
]`;
|
|
88
|
+
// Query to find all blocks on the page
|
|
89
|
+
const pageQuery = `[:find ?string ?time
|
|
90
|
+
:in $ % ?title
|
|
91
|
+
:where
|
|
92
|
+
[?page :node/title ?title]
|
|
93
|
+
[?block :block/string ?string]
|
|
94
|
+
[?block :create/time ?time]
|
|
95
|
+
(ancestor ?block ?page)]`;
|
|
96
|
+
// Execute query
|
|
97
|
+
const pageResults = await q(this.graph, pageQuery, [ancestorRule, tagText]);
|
|
98
|
+
// Process page blocks with sorting
|
|
99
|
+
let pageMemories = pageResults
|
|
100
|
+
.sort(([_, aTime], [__, bTime]) => sort_by === 'newest' ? bTime - aTime : aTime - bTime)
|
|
101
|
+
.map(([content]) => content);
|
|
102
|
+
// Get tagged blocks from across the graph
|
|
103
|
+
const tagResults = await this.searchOps.searchForTag(tagText);
|
|
104
|
+
// Process tagged blocks with sorting
|
|
105
|
+
let taggedMemories = tagResults.matches
|
|
106
|
+
.sort((a, b) => {
|
|
107
|
+
const aTime = a.block_uid ? parseInt(a.block_uid.split('-')[0], 16) : 0;
|
|
108
|
+
const bTime = b.block_uid ? parseInt(b.block_uid.split('-')[0], 16) : 0;
|
|
109
|
+
return sort_by === 'newest' ? bTime - aTime : aTime - bTime;
|
|
110
|
+
})
|
|
111
|
+
.map(match => match.content);
|
|
112
|
+
// Resolve any block references in both sets
|
|
113
|
+
const resolvedPageMemories = await Promise.all(pageMemories.map(async (content) => resolveRefs(this.graph, content)));
|
|
114
|
+
const resolvedTaggedMemories = await Promise.all(taggedMemories.map(async (content) => resolveRefs(this.graph, content)));
|
|
115
|
+
// Combine both sets and remove duplicates while preserving order
|
|
116
|
+
let uniqueMemories = [
|
|
117
|
+
...resolvedPageMemories,
|
|
118
|
+
...resolvedTaggedMemories
|
|
119
|
+
].filter((memory, index, self) => self.indexOf(memory) === index);
|
|
120
|
+
// Format filter tag with exact Roam tag syntax
|
|
121
|
+
const filterTagFormatted = filter_tag ?
|
|
122
|
+
(filter_tag.includes(' ') ? `#[[${filter_tag}]]` : `#${filter_tag}`) : null;
|
|
123
|
+
// Filter by exact tag match if provided
|
|
124
|
+
if (filterTagFormatted) {
|
|
125
|
+
uniqueMemories = uniqueMemories.filter(memory => memory.includes(filterTagFormatted));
|
|
126
|
+
}
|
|
127
|
+
// Format memories tag for removal and clean up memories tag
|
|
128
|
+
const memoriesTagFormatted = tagText.includes(' ') || tagText.includes('/') ? `#[[${tagText}]]` : `#${tagText}`;
|
|
129
|
+
uniqueMemories = uniqueMemories.map(memory => memory.replace(memoriesTagFormatted, '').trim());
|
|
130
|
+
// return {
|
|
131
|
+
// success: true,
|
|
132
|
+
// memories: [
|
|
133
|
+
// `memoriesTag = ${memoriesTag}`,
|
|
134
|
+
// `filter_tag = ${filter_tag}`,
|
|
135
|
+
// `filterTagFormatted = ${filterTagFormatted}`,
|
|
136
|
+
// `memoriesTagFormatted = ${memoriesTagFormatted}`,
|
|
137
|
+
// ]
|
|
138
|
+
// }
|
|
139
|
+
return {
|
|
140
|
+
success: true,
|
|
141
|
+
memories: uniqueMemories
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
throw new McpError(ErrorCode.InternalError, `Failed to recall memories: ${error.message}`);
|
|
146
|
+
}
|
|
101
147
|
}
|
|
102
148
|
}
|
|
@@ -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
|
-
|
|
170
|
-
|
|
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
|
|
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() {
|
|
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)
|
|
@@ -28,7 +28,8 @@ export class PageOperations {
|
|
|
28
28
|
[?page :node/title ?title]
|
|
29
29
|
(ancestor ?block ?page)
|
|
30
30
|
[?block :edit/time ?time]
|
|
31
|
-
[(> ?time ?start_of_day)]]
|
|
31
|
+
[(> ?time ?start_of_day)]]
|
|
32
|
+
:limit ${max_num_pages}`, [startOfDay.getTime(), ancestorRule]);
|
|
32
33
|
if (!results || results.length === 0) {
|
|
33
34
|
return {
|
|
34
35
|
success: true,
|
package/build/tools/schemas.js
CHANGED
|
@@ -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
|
|
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'],
|
|
@@ -243,13 +243,18 @@ export const toolSchemas = {
|
|
|
243
243
|
]
|
|
244
244
|
}
|
|
245
245
|
},
|
|
246
|
-
|
|
247
|
-
name: '
|
|
248
|
-
description: 'Find
|
|
246
|
+
roam_find_pages_modified_today: {
|
|
247
|
+
name: 'roam_find_pages_modified_today',
|
|
248
|
+
description: 'Find pages that have been modified today (since midnight), with limit.',
|
|
249
249
|
inputSchema: {
|
|
250
250
|
type: 'object',
|
|
251
|
-
properties: {
|
|
252
|
-
|
|
251
|
+
properties: {
|
|
252
|
+
max_num_pages: {
|
|
253
|
+
type: 'integer',
|
|
254
|
+
description: 'Max number of pages to retrieve (default: 50)',
|
|
255
|
+
default: 50
|
|
256
|
+
},
|
|
257
|
+
}
|
|
253
258
|
}
|
|
254
259
|
},
|
|
255
260
|
roam_search_by_text: {
|
|
@@ -272,7 +277,7 @@ export const toolSchemas = {
|
|
|
272
277
|
},
|
|
273
278
|
roam_update_block: {
|
|
274
279
|
name: 'roam_update_block',
|
|
275
|
-
description: 'Update
|
|
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).',
|
|
276
281
|
inputSchema: {
|
|
277
282
|
type: 'object',
|
|
278
283
|
properties: {
|
|
@@ -312,9 +317,9 @@ export const toolSchemas = {
|
|
|
312
317
|
]
|
|
313
318
|
}
|
|
314
319
|
},
|
|
315
|
-
|
|
316
|
-
name: '
|
|
317
|
-
description: '
|
|
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).',
|
|
318
323
|
inputSchema: {
|
|
319
324
|
type: 'object',
|
|
320
325
|
properties: {
|
|
@@ -366,7 +371,7 @@ export const toolSchemas = {
|
|
|
366
371
|
},
|
|
367
372
|
roam_search_by_date: {
|
|
368
373
|
name: 'roam_search_by_date',
|
|
369
|
-
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.',
|
|
370
375
|
inputSchema: {
|
|
371
376
|
type: 'object',
|
|
372
377
|
properties: {
|
|
@@ -399,13 +404,13 @@ export const toolSchemas = {
|
|
|
399
404
|
},
|
|
400
405
|
roam_remember: {
|
|
401
406
|
name: 'roam_remember',
|
|
402
|
-
description: 'Add a memory or piece of information to remember, stored on the daily page with
|
|
407
|
+
description: 'Add a memory or piece of information to remember, stored on the daily page with MEMORIES_TAG 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
408
|
inputSchema: {
|
|
404
409
|
type: 'object',
|
|
405
410
|
properties: {
|
|
406
411
|
memory: {
|
|
407
412
|
type: 'string',
|
|
408
|
-
description: 'The memory or information to remember'
|
|
413
|
+
description: 'The memory detail or information to remember'
|
|
409
414
|
},
|
|
410
415
|
categories: {
|
|
411
416
|
type: 'array',
|
|
@@ -420,11 +425,21 @@ export const toolSchemas = {
|
|
|
420
425
|
},
|
|
421
426
|
roam_recall: {
|
|
422
427
|
name: 'roam_recall',
|
|
423
|
-
description: 'Retrieve all stored memories
|
|
428
|
+
description: 'Retrieve all stored memories on page titled MEMORIES_TAG, or tagged block content with the same name. Returns a combined, deduplicated list of memories. Optionally filter blcoks with a specified tag and sort by creation date.',
|
|
424
429
|
inputSchema: {
|
|
425
430
|
type: 'object',
|
|
426
|
-
properties: {
|
|
427
|
-
|
|
431
|
+
properties: {
|
|
432
|
+
sort_by: {
|
|
433
|
+
type: 'string',
|
|
434
|
+
description: 'Sort order for memories based on creation date',
|
|
435
|
+
enum: ['newest', 'oldest'],
|
|
436
|
+
default: 'newest'
|
|
437
|
+
},
|
|
438
|
+
filter_tag: {
|
|
439
|
+
type: 'string',
|
|
440
|
+
description: 'Include only memories with a specific filter tag. For single word tags use format "tag", for multi-word tags use format "tag word" (without brackets)'
|
|
441
|
+
}
|
|
442
|
+
}
|
|
428
443
|
}
|
|
429
444
|
},
|
|
430
445
|
roam_datomic_query: {
|
|
@@ -23,8 +23,8 @@ export class ToolHandlers {
|
|
|
23
23
|
this.outlineOps = new OutlineOperations(graph);
|
|
24
24
|
}
|
|
25
25
|
// Page Operations
|
|
26
|
-
async findPagesModifiedToday() {
|
|
27
|
-
return this.pageOps.findPagesModifiedToday();
|
|
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);
|
|
@@ -70,8 +70,8 @@ export class ToolHandlers {
|
|
|
70
70
|
async remember(memory, categories) {
|
|
71
71
|
return this.memoryOps.remember(memory, categories);
|
|
72
72
|
}
|
|
73
|
-
async recall() {
|
|
74
|
-
return this.memoryOps.recall();
|
|
73
|
+
async recall(sort_by = 'newest', filter_tag) {
|
|
74
|
+
return this.memoryOps.recall(sort_by, filter_tag);
|
|
75
75
|
}
|
|
76
76
|
// Todo Operations
|
|
77
77
|
async addTodos(todos) {
|