roam-research-mcp 0.25.5 → 0.27.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 +85 -617
- package/build/config/environment.js +3 -1
- package/build/server/roam-server.js +149 -45
- package/build/tools/schemas.js +18 -15
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -5,16 +5,25 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](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, personal project not officially endorsed by Roam Research)
|
|
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. It supports standard input/output (stdio), HTTP Stream, and Server-Sent Events (SSE) communication. (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
|
|
|
12
|
-
## Installation
|
|
12
|
+
## Installation and Usage
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
This MCP server supports three primary communication methods:
|
|
15
|
+
|
|
16
|
+
1. **Stdio (Standard Input/Output):** Ideal for local inter-process communication, command-line tools, and direct integration with applications running on the same machine. This is the default communication method when running the server directly.
|
|
17
|
+
2. **HTTP Stream:** Provides network-based communication, suitable for web-based clients, remote applications, or scenarios requiring real-time updates over HTTP. The HTTP Stream endpoint runs on port `8088` by default.
|
|
18
|
+
3. **SSE (Server-Sent Events):** A transport for legacy clients that require SSE. The SSE endpoint runs on port `8087` by default. (NOTE: ⚠️ DEPRECATED: The SSE Transport has been deprecated as of MCP specification version 2025-03-26. HTTP Stream Transport preferred.)
|
|
19
|
+
|
|
20
|
+
### Running with Stdio
|
|
21
|
+
|
|
22
|
+
You can install the package globally and run it:
|
|
15
23
|
|
|
16
24
|
```bash
|
|
17
25
|
npm install -g roam-research-mcp
|
|
26
|
+
roam-research-mcp
|
|
18
27
|
```
|
|
19
28
|
|
|
20
29
|
Or clone the repository and build from source:
|
|
@@ -24,14 +33,60 @@ git clone https://github.com/2b3pro/roam-research-mcp.git
|
|
|
24
33
|
cd roam-research-mcp
|
|
25
34
|
npm install
|
|
26
35
|
npm run build
|
|
36
|
+
npm start
|
|
27
37
|
```
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
### Running with HTTP Stream
|
|
40
|
+
|
|
41
|
+
To run the server with HTTP Stream or SSE support, you can either:
|
|
42
|
+
|
|
43
|
+
1. **Use the default ports:** Run `npm start` after building (as shown above). The server will automatically listen on port `8088` for HTTP Stream and `8087` for SSE.
|
|
44
|
+
2. **Specify custom ports:** Set the `HTTP_STREAM_PORT` and/or `SSE_PORT` environment variables before starting the server.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
HTTP_STREAM_PORT=9000 SSE_PORT=9001 npm start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or, if using a `.env` file, add `HTTP_STREAM_PORT=9000` and/or `SSE_PORT=9001` to it.
|
|
51
|
+
|
|
52
|
+
## Docker
|
|
53
|
+
|
|
54
|
+
This project can be easily containerized using Docker. A `Dockerfile` is provided at the root of the repository.
|
|
30
55
|
|
|
31
|
-
|
|
56
|
+
### Build the Docker Image
|
|
32
57
|
|
|
58
|
+
To build the Docker image, navigate to the project root and run:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
docker build -t roam-research-mcp .
|
|
33
62
|
```
|
|
34
|
-
|
|
63
|
+
|
|
64
|
+
### Run the Docker Container
|
|
65
|
+
|
|
66
|
+
To run the Docker container and map the necessary ports, you must also provide the required environment variables. Use the `-e` flag to pass `ROAM_API_TOKEN`, `ROAM_GRAPH_NAME`, and optionally `MEMORIES_TAG`, `HTTP_STREAM_PORT`, and `SSE_PORT`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
docker run -p 3000:3000 -p 8088:8088 -p 8087:8087 \
|
|
70
|
+
-e ROAM_API_TOKEN="your-api-token" \
|
|
71
|
+
-e ROAM_GRAPH_NAME="your-graph-name" \
|
|
72
|
+
-e MEMORIES_TAG="#[[LLM/Memories]]" \
|
|
73
|
+
-e HTTP_STREAM_PORT="8088" \
|
|
74
|
+
-e SSE_PORT="8087" \
|
|
75
|
+
roam-research-mcp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Alternatively, if you have a `.env` file in the project root (which is copied into the Docker image during build), you can use the `--env-file` flag:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
docker run -p 3000:3000 -p 8088:8088 --env-file .env roam-research-mcp
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## To Test
|
|
85
|
+
|
|
86
|
+
Run [MCP Inspector](https://github.com/modelcontextprotocol/inspector) after build using the provided npm script:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run inspector
|
|
35
90
|
```
|
|
36
91
|
|
|
37
92
|
## Features
|
|
@@ -48,22 +103,24 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
48
103
|
- Efficient batch operations
|
|
49
104
|
- Hierarchical outline creation
|
|
50
105
|
|
|
51
|
-
1. `roam_fetch_page_by_title`: Fetch
|
|
52
|
-
2. `roam_create_page`: Create new pages with optional content
|
|
53
|
-
3. `roam_create_block`:
|
|
54
|
-
4. `roam_import_markdown`: Import nested markdown content under specific
|
|
55
|
-
5. `roam_add_todo`: Add
|
|
56
|
-
6. `roam_create_outline`:
|
|
57
|
-
7. `roam_search_block_refs`: Search for block references within
|
|
58
|
-
8. `roam_search_hierarchy`:
|
|
59
|
-
9. `roam_find_pages_modified_today`: Find
|
|
60
|
-
10. `roam_search_by_text`: Search for blocks containing specific text
|
|
61
|
-
11. `roam_update_block`: Update
|
|
62
|
-
12. `
|
|
63
|
-
13. `
|
|
64
|
-
14. `
|
|
65
|
-
15. `
|
|
66
|
-
16. `
|
|
106
|
+
1. `roam_fetch_page_by_title`: Fetch page content by title.
|
|
107
|
+
2. `roam_create_page`: Create new pages with optional content and headings.
|
|
108
|
+
3. `roam_create_block`: Add new blocks to an existing page or today's daily note.
|
|
109
|
+
4. `roam_import_markdown`: Import nested markdown content under a specific block.
|
|
110
|
+
5. `roam_add_todo`: Add a list of todo items to today's daily page.
|
|
111
|
+
6. `roam_create_outline`: Add a structured outline to an existing page or block.
|
|
112
|
+
7. `roam_search_block_refs`: Search for block references within a page or across the entire graph.
|
|
113
|
+
8. `roam_search_hierarchy`: Search for parent or child blocks in the block hierarchy.
|
|
114
|
+
9. `roam_find_pages_modified_today`: Find pages that have been modified today (since midnight).
|
|
115
|
+
10. `roam_search_by_text`: Search for blocks containing specific text.
|
|
116
|
+
11. `roam_update_block`: Update a single block identified by its UID.
|
|
117
|
+
12. `roam_update_multiple_blocks`: Efficiently update multiple blocks in a single batch operation.
|
|
118
|
+
13. `roam_search_by_status`: Search for blocks with a specific status (TODO/DONE) across all pages or within a specific page.
|
|
119
|
+
14. `roam_search_by_date`: Search for blocks or pages based on creation or modification dates.
|
|
120
|
+
15. `roam_search_for_tag`: Search for blocks containing a specific tag and optionally filter by blocks that also contain another tag nearby.
|
|
121
|
+
16. `roam_remember`: Add a memory or piece of information to remember.
|
|
122
|
+
17. `roam_recall`: Retrieve all stored memories.
|
|
123
|
+
18. `roam_datomic_query`: Execute a custom Datomic query on the Roam graph beyond the available search tools.
|
|
67
124
|
|
|
68
125
|
## Setup
|
|
69
126
|
|
|
@@ -83,10 +140,12 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
83
140
|
ROAM_API_TOKEN=your-api-token
|
|
84
141
|
ROAM_GRAPH_NAME=your-graph-name
|
|
85
142
|
MEMORIES_TAG='#[[LLM/Memories]]'
|
|
143
|
+
HTTP_STREAM_PORT=8088 # Or your desired port for HTTP Stream communication
|
|
144
|
+
SSE_PORT=8087 # Or your desired port for SSE communication
|
|
86
145
|
```
|
|
87
146
|
|
|
88
147
|
Option 2: Using MCP settings (Alternative method)
|
|
89
|
-
Add the configuration to your MCP settings file
|
|
148
|
+
Add the configuration to your MCP settings file. Note that you may need to update the `args` to `["/path/to/roam-research-mcp/build/index.js"]` if you are running the server directly.
|
|
90
149
|
|
|
91
150
|
- For Cline (`~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`):
|
|
92
151
|
- For Claude desktop app (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
@@ -100,7 +159,9 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
100
159
|
"env": {
|
|
101
160
|
"ROAM_API_TOKEN": "your-api-token",
|
|
102
161
|
"ROAM_GRAPH_NAME": "your-graph-name",
|
|
103
|
-
"MEMORIES_TAG": "#[[LLM/Memories]]"
|
|
162
|
+
"MEMORIES_TAG": "#[[LLM/Memories]]",
|
|
163
|
+
"HTTP_STREAM_PORT": "8088",
|
|
164
|
+
"SSE_PORT": "8087"
|
|
104
165
|
}
|
|
105
166
|
}
|
|
106
167
|
}
|
|
@@ -116,599 +177,6 @@ The server provides powerful tools for interacting with Roam Research:
|
|
|
116
177
|
npm run build
|
|
117
178
|
```
|
|
118
179
|
|
|
119
|
-
## Usage
|
|
120
|
-
|
|
121
|
-
### Fetch Page By Title
|
|
122
|
-
|
|
123
|
-
Fetch and read a page's content with resolved block references:
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
use_mcp_tool roam-research roam_fetch_page_by_title {
|
|
127
|
-
"title": "Example Page"
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Returns the page content as markdown with:
|
|
132
|
-
|
|
133
|
-
- Complete hierarchical structure
|
|
134
|
-
- Block references recursively resolved (up to 4 levels deep)
|
|
135
|
-
- Proper indentation for nesting levels
|
|
136
|
-
- Full markdown formatting
|
|
137
|
-
|
|
138
|
-
### Create Page
|
|
139
|
-
|
|
140
|
-
Create a new page with optional content:
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
use_mcp_tool roam-research roam_create_page {
|
|
144
|
-
"title": "New Page",
|
|
145
|
-
"content": "Initial content for the page"
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Returns the created page's UID on success.
|
|
150
|
-
|
|
151
|
-
### Create Block
|
|
152
|
-
|
|
153
|
-
Add a new block to a page (defaults to today's daily page if neither page_uid nor title provided):
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
use_mcp_tool roam-research roam_create_block {
|
|
157
|
-
"content": "Block content",
|
|
158
|
-
"page_uid": "optional-target-page-uid",
|
|
159
|
-
"title": "optional-target-page-title"
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
You can specify either:
|
|
164
|
-
|
|
165
|
-
- `page_uid`: Direct reference to target page
|
|
166
|
-
- `title`: Name of target page (will be created if it doesn't exist)
|
|
167
|
-
- Neither: Block will be added to today's daily page
|
|
168
|
-
|
|
169
|
-
Returns:
|
|
170
|
-
|
|
171
|
-
```json
|
|
172
|
-
{
|
|
173
|
-
"success": true,
|
|
174
|
-
"block_uid": "created-block-uid",
|
|
175
|
-
"parent_uid": "parent-page-uid"
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Create Outline
|
|
180
|
-
|
|
181
|
-
Create a hierarchical outline with proper nesting and structure:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
use_mcp_tool roam-research roam_create_outline {
|
|
185
|
-
"outline": [
|
|
186
|
-
{
|
|
187
|
-
"text": "I. Top Level",
|
|
188
|
-
"level": 1
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
"text": "A. Second Level",
|
|
192
|
-
"level": 2
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
"text": "1. Third Level",
|
|
196
|
-
"level": 3
|
|
197
|
-
}
|
|
198
|
-
],
|
|
199
|
-
"page_title_uid": "optional-target-page",
|
|
200
|
-
"block_text_uid": "optional-header-text"
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Features:
|
|
205
|
-
|
|
206
|
-
- Create complex outlines with up to 10 levels of nesting
|
|
207
|
-
- Validate outline structure and content
|
|
208
|
-
- Maintain proper parent-child relationships
|
|
209
|
-
- Optional header block for the outline
|
|
210
|
-
- Defaults to today's daily page if no page specified
|
|
211
|
-
- Efficient batch operations for creating blocks
|
|
212
|
-
|
|
213
|
-
Parameters:
|
|
214
|
-
|
|
215
|
-
- `outline`: Array of outline items, each with:
|
|
216
|
-
- `text`: Content of the outline item (required)
|
|
217
|
-
- `level`: Nesting level (1-10, required)
|
|
218
|
-
- `page_title_uid`: Target page title or UID (optional, defaults to today's page)
|
|
219
|
-
- `block_text_uid`: Header text for the outline (optional)
|
|
220
|
-
|
|
221
|
-
Returns:
|
|
222
|
-
|
|
223
|
-
```json
|
|
224
|
-
{
|
|
225
|
-
"success": true,
|
|
226
|
-
"page_uid": "target-page-uid",
|
|
227
|
-
"parent_uid": "header-block-uid",
|
|
228
|
-
"created_uids": ["uid1", "uid2", ...]
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Add Todo Items
|
|
233
|
-
|
|
234
|
-
Add one or more todo items to today's daily page:
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
use_mcp_tool roam-research roam_add_todo {
|
|
238
|
-
"todos": [
|
|
239
|
-
"First todo item",
|
|
240
|
-
"Second todo item",
|
|
241
|
-
"Third todo item"
|
|
242
|
-
]
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Features:
|
|
247
|
-
|
|
248
|
-
- Adds todos with Roam checkbox syntax (`{{TODO}} todo text`)
|
|
249
|
-
- Supports adding multiple todos in a single operation
|
|
250
|
-
- Uses batch actions for efficiency when adding >10 todos
|
|
251
|
-
- Automatically creates today's page if it doesn't exist
|
|
252
|
-
- Adds todos as top-level blocks in sequential order
|
|
253
|
-
|
|
254
|
-
### Import Nested Markdown
|
|
255
|
-
|
|
256
|
-
Import nested markdown content under a specific block:
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
use_mcp_tool roam-research roam_import_markdown {
|
|
260
|
-
"content": "- Item 1\n - Subitem A\n - Subitem B\n- Item 2",
|
|
261
|
-
"page_uid": "optional-page-uid",
|
|
262
|
-
"page_title": "optional-page-title",
|
|
263
|
-
"parent_uid": "optional-parent-block-uid",
|
|
264
|
-
"parent_string": "optional-exact-block-content",
|
|
265
|
-
"order": "first"
|
|
266
|
-
}
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Features:
|
|
270
|
-
|
|
271
|
-
- Import content under specific blocks:
|
|
272
|
-
- Find parent block by UID or exact string match
|
|
273
|
-
- Locate blocks within specific pages by title or UID
|
|
274
|
-
- Defaults to today's page if no page specified
|
|
275
|
-
- Control content placement:
|
|
276
|
-
- Add as first or last child of parent block
|
|
277
|
-
- Preserve hierarchical structure
|
|
278
|
-
- Efficient batch operations for nested content
|
|
279
|
-
- Comprehensive return value:
|
|
280
|
-
```json
|
|
281
|
-
{
|
|
282
|
-
"success": true,
|
|
283
|
-
"page_uid": "target-page-uid",
|
|
284
|
-
"parent_uid": "parent-block-uid",
|
|
285
|
-
"created_uids": ["uid1", "uid2", ...]
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
Parameters:
|
|
290
|
-
|
|
291
|
-
- `content`: Nested markdown content to import
|
|
292
|
-
- `page_uid`: UID of the page containing the parent block
|
|
293
|
-
- `page_title`: Title of the page containing the parent block (ignored if page_uid provided)
|
|
294
|
-
- `parent_uid`: UID of the parent block to add content under
|
|
295
|
-
- `parent_string`: Exact string content of the parent block (must provide either page_uid or page_title)
|
|
296
|
-
- `order`: Where to add the content ("first" or "last", defaults to "first")
|
|
297
|
-
|
|
298
|
-
### Search Block References
|
|
299
|
-
|
|
300
|
-
Search for block references within pages or across the entire graph:
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
use_mcp_tool roam-research roam_search_block_refs {
|
|
304
|
-
"block_uid": "optional-block-uid",
|
|
305
|
-
"page_title_uid": "optional-page-title-or-uid"
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
Features:
|
|
310
|
-
|
|
311
|
-
- Find all references to a specific block
|
|
312
|
-
- Search for any block references within a page
|
|
313
|
-
- Search across the entire graph
|
|
314
|
-
- Supports both direct and indirect references
|
|
315
|
-
- Includes block content and location context
|
|
316
|
-
|
|
317
|
-
Parameters:
|
|
318
|
-
|
|
319
|
-
- `block_uid`: UID of the block to find references to (optional)
|
|
320
|
-
- `page_title_uid`: Title or UID of the page to search in (optional)
|
|
321
|
-
|
|
322
|
-
Returns:
|
|
323
|
-
|
|
324
|
-
```json
|
|
325
|
-
{
|
|
326
|
-
"success": true,
|
|
327
|
-
"matches": [
|
|
328
|
-
{
|
|
329
|
-
"block_uid": "referenced-block-uid",
|
|
330
|
-
"content": "Block content with ((reference))",
|
|
331
|
-
"page_title": "Page containing reference"
|
|
332
|
-
}
|
|
333
|
-
],
|
|
334
|
-
"message": "Found N block(s) referencing..."
|
|
335
|
-
}
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Search By Text
|
|
339
|
-
|
|
340
|
-
Search for blocks containing specific text across all pages or within a specific page:
|
|
341
|
-
|
|
342
|
-
```typescript
|
|
343
|
-
use_mcp_tool roam-research roam_search_by_text {
|
|
344
|
-
"text": "search text",
|
|
345
|
-
"page_title_uid": "optional-page-title-or-uid",
|
|
346
|
-
"case_sensitive": true
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
Features:
|
|
351
|
-
|
|
352
|
-
- Search for any text across all blocks in the graph
|
|
353
|
-
- Optional page-scoped search
|
|
354
|
-
- Case-sensitive or case-insensitive search
|
|
355
|
-
- Returns block content with page context
|
|
356
|
-
- Efficient text matching using Datalog queries
|
|
357
|
-
|
|
358
|
-
Parameters:
|
|
359
|
-
|
|
360
|
-
- `text`: The text to search for (required)
|
|
361
|
-
- `page_title_uid`: Title or UID of the page to search in (optional)
|
|
362
|
-
- `case_sensitive`: Whether to perform a case-sensitive search (optional, default: true to match Roam's native behavior)
|
|
363
|
-
|
|
364
|
-
Returns:
|
|
365
|
-
|
|
366
|
-
```json
|
|
367
|
-
{
|
|
368
|
-
"success": true,
|
|
369
|
-
"matches": [
|
|
370
|
-
{
|
|
371
|
-
"block_uid": "matching-block-uid",
|
|
372
|
-
"content": "Block content containing search text",
|
|
373
|
-
"page_title": "Page containing block"
|
|
374
|
-
}
|
|
375
|
-
],
|
|
376
|
-
"message": "Found N block(s) containing \"search text\""
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### Update Block Content
|
|
381
|
-
|
|
382
|
-
Update a block's content using either direct text replacement or pattern-based transformations:
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
use_mcp_tool roam-research roam_update_block {
|
|
386
|
-
"block_uid": "target-block-uid",
|
|
387
|
-
"content": "New block content"
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
Or use pattern-based transformation:
|
|
392
|
-
|
|
393
|
-
```typescript
|
|
394
|
-
use_mcp_tool roam-research roam_update_block {
|
|
395
|
-
"block_uid": "target-block-uid",
|
|
396
|
-
"transform_pattern": {
|
|
397
|
-
"find": "\\bPython\\b",
|
|
398
|
-
"replace": "[[Python]]",
|
|
399
|
-
"global": true
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
Features:
|
|
405
|
-
|
|
406
|
-
- Two update modes:
|
|
407
|
-
- Direct content replacement
|
|
408
|
-
- Pattern-based transformation using regex
|
|
409
|
-
- Verify block existence before updating
|
|
410
|
-
- Return updated content in response
|
|
411
|
-
- Support for global or single-match replacements
|
|
412
|
-
- Preserve block relationships and metadata
|
|
413
|
-
|
|
414
|
-
Parameters:
|
|
415
|
-
|
|
416
|
-
- `block_uid`: UID of the block to update (required)
|
|
417
|
-
- `content`: New content for the block (if using direct replacement)
|
|
418
|
-
- `transform_pattern`: Pattern for transforming existing content:
|
|
419
|
-
- `find`: Text or regex pattern to find
|
|
420
|
-
- `replace`: Text to replace with
|
|
421
|
-
- `global`: Whether to replace all occurrences (default: true)
|
|
422
|
-
|
|
423
|
-
Returns:
|
|
424
|
-
|
|
425
|
-
```json
|
|
426
|
-
{
|
|
427
|
-
"success": true,
|
|
428
|
-
"content": "Updated block content"
|
|
429
|
-
}
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
### Search For Tags
|
|
433
|
-
|
|
434
|
-
Search for blocks containing specific tags with optional filtering by nearby tags:
|
|
435
|
-
|
|
436
|
-
```typescript
|
|
437
|
-
use_mcp_tool roam-research roam_search_for_tag {
|
|
438
|
-
"primary_tag": "Project/Tasks",
|
|
439
|
-
"page_title_uid": "optional-page-title-or-uid",
|
|
440
|
-
"near_tag": "optional-secondary-tag",
|
|
441
|
-
"case_sensitive": true
|
|
442
|
-
}
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
Features:
|
|
446
|
-
|
|
447
|
-
- Search for blocks containing specific tags
|
|
448
|
-
- Optional filtering by presence of another tag
|
|
449
|
-
- Page-scoped or graph-wide search
|
|
450
|
-
- Case-sensitive or case-insensitive search
|
|
451
|
-
- Returns block content with page context
|
|
452
|
-
- Efficient tag matching using Datalog queries
|
|
453
|
-
|
|
454
|
-
Parameters:
|
|
455
|
-
|
|
456
|
-
- `primary_tag`: The main tag to search for (required)
|
|
457
|
-
- `page_title_uid`: Title or UID of the page to search in (optional)
|
|
458
|
-
- `near_tag`: Another tag to filter results by (optional)
|
|
459
|
-
- `case_sensitive`: Whether to perform case-sensitive search (optional, default: true to match Roam's native behavior)
|
|
460
|
-
|
|
461
|
-
Returns:
|
|
462
|
-
|
|
463
|
-
```json
|
|
464
|
-
{
|
|
465
|
-
"success": true,
|
|
466
|
-
"matches": [
|
|
467
|
-
{
|
|
468
|
-
"block_uid": "matching-block-uid",
|
|
469
|
-
"content": "Block content containing #[[primary_tag]]",
|
|
470
|
-
"page_title": "Page containing block"
|
|
471
|
-
}
|
|
472
|
-
],
|
|
473
|
-
"message": "Found N block(s) referencing \"primary_tag\""
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
### Remember Information
|
|
478
|
-
|
|
479
|
-
Store memories or important information with automatic tagging and categorization:
|
|
480
|
-
|
|
481
|
-
```typescript
|
|
482
|
-
use_mcp_tool roam-research roam_remember {
|
|
483
|
-
"memory": "Important information to remember",
|
|
484
|
-
"categories": ["Work", "Project/Alpha"]
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
Features:
|
|
489
|
-
|
|
490
|
-
- Store information with #[[LLM/Memories]] tag
|
|
491
|
-
- Add optional category tags for organization
|
|
492
|
-
- Automatically adds to today's daily page
|
|
493
|
-
- Supports multiple categories per memory
|
|
494
|
-
- Easy retrieval using roam_search_for_tag
|
|
495
|
-
- Maintains chronological order of memories
|
|
496
|
-
|
|
497
|
-
Parameters:
|
|
498
|
-
|
|
499
|
-
- `memory`: The information to remember (required)
|
|
500
|
-
- `categories`: Optional array of categories to tag the memory with
|
|
501
|
-
|
|
502
|
-
Returns:
|
|
503
|
-
|
|
504
|
-
```json
|
|
505
|
-
{
|
|
506
|
-
"success": true,
|
|
507
|
-
"block_uid": "created-block-uid",
|
|
508
|
-
"content": "Memory content with tags"
|
|
509
|
-
}
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
### Search By Date
|
|
513
|
-
|
|
514
|
-
Search for blocks and pages based on creation or modification dates:
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
use_mcp_tool roam-research roam_search_by_date {
|
|
518
|
-
"start_date": "2025-01-01",
|
|
519
|
-
"end_date": "2025-01-31",
|
|
520
|
-
"type": "modified",
|
|
521
|
-
"scope": "blocks",
|
|
522
|
-
"include_content": true
|
|
523
|
-
}
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
Features:
|
|
527
|
-
|
|
528
|
-
- Search by creation date, modification date, or both
|
|
529
|
-
- Filter blocks, pages, or both
|
|
530
|
-
- Optional date range with start and end dates
|
|
531
|
-
- Include or exclude block/page content in results
|
|
532
|
-
- Sort results by timestamp
|
|
533
|
-
- Efficient date-based filtering using Datalog queries
|
|
534
|
-
|
|
535
|
-
Parameters:
|
|
536
|
-
|
|
537
|
-
- `start_date`: Start date in ISO format (YYYY-MM-DD) (required)
|
|
538
|
-
- `end_date`: End date in ISO format (YYYY-MM-DD) (optional)
|
|
539
|
-
- `type`: Whether to search by 'created', 'modified', or 'both' (required)
|
|
540
|
-
- `scope`: Whether to search 'blocks', 'pages', or 'both' (required)
|
|
541
|
-
- `include_content`: Whether to include the content of matching blocks/pages (optional, default: true)
|
|
542
|
-
|
|
543
|
-
Returns:
|
|
544
|
-
|
|
545
|
-
```json
|
|
546
|
-
{
|
|
547
|
-
"success": true,
|
|
548
|
-
"matches": [
|
|
549
|
-
{
|
|
550
|
-
"uid": "block-or-page-uid",
|
|
551
|
-
"type": "block",
|
|
552
|
-
"time": 1704067200000,
|
|
553
|
-
"content": "Block or page content",
|
|
554
|
-
"page_title": "Page title (for blocks)"
|
|
555
|
-
}
|
|
556
|
-
],
|
|
557
|
-
"message": "Found N matches for the given date range and criteria"
|
|
558
|
-
}
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
### Find Pages Modified Today
|
|
562
|
-
|
|
563
|
-
Find all pages that have been modified since midnight today:
|
|
564
|
-
|
|
565
|
-
```typescript
|
|
566
|
-
use_mcp_tool roam-research roam_find_pages_modified_today {}
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
Features:
|
|
570
|
-
|
|
571
|
-
- Tracks all modifications made to pages since midnight
|
|
572
|
-
- Detects changes at any level in the block hierarchy
|
|
573
|
-
- Returns unique list of modified page titles
|
|
574
|
-
- Includes count of modified pages
|
|
575
|
-
- No parameters required
|
|
576
|
-
|
|
577
|
-
Returns:
|
|
578
|
-
|
|
579
|
-
```json
|
|
580
|
-
{
|
|
581
|
-
"success": true,
|
|
582
|
-
"pages": ["Page 1", "Page 2"],
|
|
583
|
-
"message": "Found 2 page(s) modified today"
|
|
584
|
-
}
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### Execute Datomic Queries
|
|
588
|
-
|
|
589
|
-
Execute custom Datalog queries on your Roam graph for advanced data retrieval and analysis:
|
|
590
|
-
|
|
591
|
-
```typescript
|
|
592
|
-
use_mcp_tool roam-research roam_datomic_query {
|
|
593
|
-
"query": "[:find (count ?p)\n :where [?p :node/title]]",
|
|
594
|
-
"inputs": []
|
|
595
|
-
}
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
Features:
|
|
599
|
-
|
|
600
|
-
- Direct access to Roam's query engine
|
|
601
|
-
- Support for all Datalog query features:
|
|
602
|
-
- Complex pattern matching
|
|
603
|
-
- Aggregation functions (count, sum, max, min, avg, distinct)
|
|
604
|
-
- String operations (includes?, starts-with?, ends-with?)
|
|
605
|
-
- Logical operations (<, >, <=, >=, =, not=)
|
|
606
|
-
- Rules for recursive queries
|
|
607
|
-
- Case-sensitive and case-insensitive search capabilities
|
|
608
|
-
- Efficient querying across the entire graph
|
|
609
|
-
|
|
610
|
-
Parameters:
|
|
611
|
-
|
|
612
|
-
- `query`: The Datalog query to execute (required)
|
|
613
|
-
- `inputs`: Optional array of input parameters for the query
|
|
614
|
-
|
|
615
|
-
Returns:
|
|
616
|
-
|
|
617
|
-
```json
|
|
618
|
-
{
|
|
619
|
-
"success": true,
|
|
620
|
-
"matches": [
|
|
621
|
-
{
|
|
622
|
-
"content": "[result data]",
|
|
623
|
-
"block_uid": "",
|
|
624
|
-
"page_title": ""
|
|
625
|
-
}
|
|
626
|
-
],
|
|
627
|
-
"message": "Query executed successfully. Found N results."
|
|
628
|
-
}
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
Example Queries:
|
|
632
|
-
|
|
633
|
-
1. Count all pages:
|
|
634
|
-
|
|
635
|
-
```clojure
|
|
636
|
-
[:find (count ?p)
|
|
637
|
-
:where [?p :node/title]]
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
2. Case-insensitive text search:
|
|
641
|
-
|
|
642
|
-
```clojure
|
|
643
|
-
[:find ?string ?title
|
|
644
|
-
:where
|
|
645
|
-
[?b :block/string ?string]
|
|
646
|
-
[(clojure.string/lower-case ?string) ?lower]
|
|
647
|
-
[(clojure.string/includes? ?lower "search term")]
|
|
648
|
-
[?b :block/page ?p]
|
|
649
|
-
[?p :node/title ?title]]
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
3. Find blocks modified after a date:
|
|
653
|
-
|
|
654
|
-
```clojure
|
|
655
|
-
[:find ?block_ref ?string
|
|
656
|
-
:in $ ?start_of_day
|
|
657
|
-
:where
|
|
658
|
-
[?b :edit/time ?time]
|
|
659
|
-
[(> ?time ?start_of_day)]
|
|
660
|
-
[?b :block/uid ?block_ref]
|
|
661
|
-
[?b :block/string ?string]]
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
See Roam_Research_Datalog_Cheatsheet.md for more query examples and syntax documentation.
|
|
665
|
-
|
|
666
|
-
### Search Block Hierarchy
|
|
667
|
-
|
|
668
|
-
Navigate and search through block parent-child relationships:
|
|
669
|
-
|
|
670
|
-
```typescript
|
|
671
|
-
use_mcp_tool roam-research roam_search_hierarchy {
|
|
672
|
-
"parent_uid": "optional-parent-block-uid",
|
|
673
|
-
"child_uid": "optional-child-block-uid",
|
|
674
|
-
"page_title_uid": "optional-page-title-or-uid",
|
|
675
|
-
"max_depth": 3
|
|
676
|
-
}
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
Features:
|
|
680
|
-
|
|
681
|
-
- Search up or down the block hierarchy
|
|
682
|
-
- Find children of a specific block
|
|
683
|
-
- Find parents of a specific block
|
|
684
|
-
- Configure search depth (1-10 levels)
|
|
685
|
-
- Optional page scope filtering
|
|
686
|
-
- Includes depth information for each result
|
|
687
|
-
|
|
688
|
-
Parameters:
|
|
689
|
-
|
|
690
|
-
- `parent_uid`: UID of the block to find children of (required if searching down)
|
|
691
|
-
- `child_uid`: UID of the block to find parents of (required if searching up)
|
|
692
|
-
- `page_title_uid`: Title or UID of the page to search in (optional)
|
|
693
|
-
- `max_depth`: How many levels deep to search (optional, default: 1, max: 10)
|
|
694
|
-
|
|
695
|
-
Returns:
|
|
696
|
-
|
|
697
|
-
```json
|
|
698
|
-
{
|
|
699
|
-
"success": true,
|
|
700
|
-
"matches": [
|
|
701
|
-
{
|
|
702
|
-
"block_uid": "related-block-uid",
|
|
703
|
-
"content": "Block content",
|
|
704
|
-
"depth": 2,
|
|
705
|
-
"page_title": "Page containing block"
|
|
706
|
-
}
|
|
707
|
-
],
|
|
708
|
-
"message": "Found N block(s) as children/parents..."
|
|
709
|
-
}
|
|
710
|
-
```
|
|
711
|
-
|
|
712
180
|
## Error Handling
|
|
713
181
|
|
|
714
182
|
The server provides comprehensive error handling for common scenarios:
|
|
@@ -41,4 +41,6 @@ if (!API_TOKEN || !GRAPH_NAME) {
|
|
|
41
41
|
' ROAM_API_TOKEN=your-api-token\n' +
|
|
42
42
|
' ROAM_GRAPH_NAME=your-graph-name');
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
const HTTP_STREAM_PORT = process.env.HTTP_STREAM_PORT || '8088'; // Default to 8080
|
|
45
|
+
const SSE_PORT = process.env.SSE_PORT || '8087'; // Default to 8087
|
|
46
|
+
export { API_TOKEN, GRAPH_NAME, HTTP_STREAM_PORT, SSE_PORT };
|
|
@@ -1,59 +1,54 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
3
5
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
4
6
|
import { initializeGraph } from '@roam-research/roam-api-sdk';
|
|
5
|
-
import { API_TOKEN, GRAPH_NAME } from '../config/environment.js';
|
|
7
|
+
import { API_TOKEN, GRAPH_NAME, HTTP_STREAM_PORT, SSE_PORT } from '../config/environment.js';
|
|
6
8
|
import { toolSchemas } from '../tools/schemas.js';
|
|
7
9
|
import { ToolHandlers } from '../tools/tool-handlers.js';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import { join, dirname } from 'node:path';
|
|
12
|
+
import { createServer } from 'node:http';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
// Read package.json to get the version
|
|
17
|
+
const packageJsonPath = join(__dirname, '../../package.json');
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
19
|
+
const serverVersion = packageJson.version;
|
|
8
20
|
export class RoamServer {
|
|
9
21
|
constructor() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
roam_search_block_refs: {},
|
|
32
|
-
roam_search_hierarchy: {},
|
|
33
|
-
roam_find_pages_modified_today: {},
|
|
34
|
-
roam_search_by_text: {},
|
|
35
|
-
roam_update_block: {},
|
|
36
|
-
roam_update_multiple_blocks: {},
|
|
37
|
-
roam_search_by_date: {},
|
|
38
|
-
roam_datomic_query: {}
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
this.setupRequestHandlers();
|
|
43
|
-
// Error handling
|
|
44
|
-
this.server.onerror = (error) => { };
|
|
45
|
-
process.on('SIGINT', async () => {
|
|
46
|
-
await this.server.close();
|
|
47
|
-
process.exit(0);
|
|
48
|
-
});
|
|
22
|
+
try {
|
|
23
|
+
this.graph = initializeGraph({
|
|
24
|
+
token: API_TOKEN,
|
|
25
|
+
graph: GRAPH_NAME,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
30
|
+
throw new McpError(ErrorCode.InternalError, `Failed to initialize Roam graph: ${errorMessage}`);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
this.toolHandlers = new ToolHandlers(this.graph);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
37
|
+
throw new McpError(ErrorCode.InternalError, `Failed to initialize tool handlers: ${errorMessage}`);
|
|
38
|
+
}
|
|
39
|
+
// Ensure toolSchemas is not empty before proceeding
|
|
40
|
+
if (Object.keys(toolSchemas).length === 0) {
|
|
41
|
+
throw new McpError(ErrorCode.InternalError, 'No tool schemas defined in src/tools/schemas.ts');
|
|
42
|
+
}
|
|
49
43
|
}
|
|
50
|
-
|
|
44
|
+
// Refactored to accept a Server instance
|
|
45
|
+
setupRequestHandlers(mcpServer) {
|
|
51
46
|
// List available tools
|
|
52
|
-
|
|
47
|
+
mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
53
48
|
tools: Object.values(toolSchemas),
|
|
54
49
|
}));
|
|
55
50
|
// Handle tool calls
|
|
56
|
-
|
|
51
|
+
mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
57
52
|
try {
|
|
58
53
|
switch (request.params.name) {
|
|
59
54
|
case 'roam_remember': {
|
|
@@ -220,7 +215,116 @@ export class RoamServer {
|
|
|
220
215
|
});
|
|
221
216
|
}
|
|
222
217
|
async run() {
|
|
223
|
-
|
|
224
|
-
|
|
218
|
+
try {
|
|
219
|
+
const stdioMcpServer = new Server({
|
|
220
|
+
name: 'roam-research',
|
|
221
|
+
version: serverVersion,
|
|
222
|
+
}, {
|
|
223
|
+
capabilities: {
|
|
224
|
+
tools: {
|
|
225
|
+
...Object.fromEntries(Object.keys(toolSchemas).map((toolName) => [toolName, {}])),
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
this.setupRequestHandlers(stdioMcpServer);
|
|
230
|
+
const stdioTransport = new StdioServerTransport();
|
|
231
|
+
await stdioMcpServer.connect(stdioTransport);
|
|
232
|
+
const httpMcpServer = new Server({
|
|
233
|
+
name: 'roam-research-http', // A distinct name for the HTTP server
|
|
234
|
+
version: serverVersion,
|
|
235
|
+
}, {
|
|
236
|
+
capabilities: {
|
|
237
|
+
tools: {
|
|
238
|
+
...Object.fromEntries(Object.keys(toolSchemas).map((toolName) => [toolName, {}])),
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
this.setupRequestHandlers(httpMcpServer);
|
|
243
|
+
const httpStreamTransport = new StreamableHTTPServerTransport({
|
|
244
|
+
sessionIdGenerator: () => Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
|
|
245
|
+
});
|
|
246
|
+
await httpMcpServer.connect(httpStreamTransport);
|
|
247
|
+
const httpServer = createServer(async (req, res) => {
|
|
248
|
+
try {
|
|
249
|
+
await httpStreamTransport.handleRequest(req, res);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
// console.error('HTTP Stream Server error:', error);
|
|
253
|
+
if (!res.headersSent) {
|
|
254
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
255
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
httpServer.listen(parseInt(HTTP_STREAM_PORT), () => {
|
|
260
|
+
// console.log(`MCP Roam Research server running HTTP Stream on port ${HTTP_STREAM_PORT}`);
|
|
261
|
+
});
|
|
262
|
+
// SSE Server setup
|
|
263
|
+
const sseMcpServer = new Server({
|
|
264
|
+
name: 'roam-research-sse', // Distinct name for SSE server
|
|
265
|
+
version: serverVersion,
|
|
266
|
+
}, {
|
|
267
|
+
capabilities: {
|
|
268
|
+
tools: {
|
|
269
|
+
...Object.fromEntries(Object.keys(toolSchemas).map((toolName) => [toolName, {}])),
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
this.setupRequestHandlers(sseMcpServer);
|
|
274
|
+
const sseHttpServer = createServer(async (req, res) => {
|
|
275
|
+
const parseBody = (request) => {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
let body = '';
|
|
278
|
+
request.on('data', (chunk) => {
|
|
279
|
+
body += chunk.toString();
|
|
280
|
+
});
|
|
281
|
+
request.on('end', () => {
|
|
282
|
+
try {
|
|
283
|
+
resolve(body ? JSON.parse(body) : {});
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
reject(error);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
request.on('error', reject);
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
try {
|
|
293
|
+
if (req.url === '/sse') {
|
|
294
|
+
const sseTransport = new SSEServerTransport('/sse', res);
|
|
295
|
+
await sseMcpServer.connect(sseTransport);
|
|
296
|
+
if (req.method === 'GET') {
|
|
297
|
+
await sseTransport.start();
|
|
298
|
+
}
|
|
299
|
+
else if (req.method === 'POST') {
|
|
300
|
+
const parsedBody = await parseBody(req);
|
|
301
|
+
await sseTransport.handlePostMessage(req, res, parsedBody);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
res.writeHead(405, { 'Content-Type': 'text/plain' });
|
|
305
|
+
res.end('Method Not Allowed');
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
310
|
+
res.end('Not Found');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
// console.error('SSE HTTP Server error:', error);
|
|
315
|
+
if (!res.headersSent) {
|
|
316
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
317
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
sseHttpServer.listen(parseInt(SSE_PORT), () => {
|
|
322
|
+
// console.log(`MCP Roam Research server running SSE on port ${SSE_PORT}`);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
327
|
+
throw new McpError(ErrorCode.InternalError, `Failed to connect MCP server: ${errorMessage}`);
|
|
328
|
+
}
|
|
225
329
|
}
|
|
226
330
|
}
|
package/build/tools/schemas.js
CHANGED
|
@@ -19,21 +19,24 @@ export const toolSchemas = {
|
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
roam_fetch_page_by_title: {
|
|
22
|
+
name: 'roam_fetch_page_by_title',
|
|
22
23
|
description: 'Fetch page by title, defaults to raw JSON string.',
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
title: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Title of the page. For date pages, use ordinal date formats such as January 2nd, 2025'
|
|
30
|
+
},
|
|
31
|
+
format: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
enum: ['markdown', 'raw'],
|
|
34
|
+
default: 'raw',
|
|
35
|
+
description: "Format output as markdown or JSON. 'markdown' returns as string; 'raw' returns JSON string of the page's blocks"
|
|
36
|
+
}
|
|
28
37
|
},
|
|
29
|
-
|
|
30
|
-
type: 'string',
|
|
31
|
-
enum: ['markdown', 'raw'],
|
|
32
|
-
default: 'raw',
|
|
33
|
-
description: "Format output as markdown or JSON. 'markdown' returns as string; 'raw' returns JSON string of the page's blocks"
|
|
34
|
-
}
|
|
38
|
+
required: ['title']
|
|
35
39
|
},
|
|
36
|
-
required: ['title']
|
|
37
40
|
},
|
|
38
41
|
roam_create_page: {
|
|
39
42
|
name: 'roam_create_page',
|
|
@@ -149,7 +152,7 @@ export const toolSchemas = {
|
|
|
149
152
|
},
|
|
150
153
|
roam_import_markdown: {
|
|
151
154
|
name: 'roam_import_markdown',
|
|
152
|
-
description: 'Import nested markdown content into Roam under a specific block. Can locate the parent block by UID or by exact string match within a specific page.',
|
|
155
|
+
description: 'Import nested markdown content into Roam under a specific block. Can locate the parent block by UID (preferred) or by exact string match within a specific page.',
|
|
153
156
|
inputSchema: {
|
|
154
157
|
type: 'object',
|
|
155
158
|
properties: {
|
|
@@ -171,7 +174,7 @@ export const toolSchemas = {
|
|
|
171
174
|
},
|
|
172
175
|
parent_string: {
|
|
173
176
|
type: 'string',
|
|
174
|
-
description: 'Optional: Exact string content of the parent block to add content under (must provide either page_uid or page_title)'
|
|
177
|
+
description: 'Optional: Exact string content of the parent block to add content under (must provide either page_uid (preferred) or page_title)'
|
|
175
178
|
},
|
|
176
179
|
order: {
|
|
177
180
|
type: 'string',
|
|
@@ -311,7 +314,7 @@ export const toolSchemas = {
|
|
|
311
314
|
},
|
|
312
315
|
roam_update_block: {
|
|
313
316
|
name: 'roam_update_block',
|
|
314
|
-
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
|
|
317
|
+
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 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).',
|
|
315
318
|
inputSchema: {
|
|
316
319
|
type: 'object',
|
|
317
320
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roam-research-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "A Model Context Protocol (MCP) server for Roam Research API integration",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "tsc && chmod 755 build/index.js",
|
|
32
|
-
"prepare": "npm run build",
|
|
33
32
|
"watch": "tsc --watch",
|
|
34
|
-
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
|
|
33
|
+
"inspector": "npx @modelcontextprotocol/inspector build/index.js",
|
|
34
|
+
"start": "node build/index.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@modelcontextprotocol/sdk": "0.
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
38
38
|
"@roam-research/roam-api-sdk": "^0.10.0",
|
|
39
39
|
"dotenv": "^16.4.7"
|
|
40
40
|
},
|