triliumnext-mcp 0.3.9-beta.1 → 0.3.10-beta.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.
- package/README.md +150 -188
- package/build/index.js +5 -5
- package/build/modules/noteHandler.js +73 -29
- package/build/modules/noteManager.js +464 -93
- package/build/modules/searchQueryBuilder.js +26 -9
- package/build/modules/toolDefinitions.js +92 -103
- package/build/types/noteBuilderTypes.js +1 -1
- package/build/utils/contentIntegrity.js +179 -0
- package/build/utils/contentProcessor.js +28 -166
- package/build/utils/contentRules.js +266 -0
- package/build/utils/noteBuilder.js +4 -49
- package/build/utils/validationUtils.js +40 -4
- package/package.json +6 -2
- package/build/index_refactored.js +0 -109
- package/build/modules/attachmentManager.js +0 -244
- package/build/modules/contentProcessor.js +0 -27
- package/build/modules/htmlToMarkdownConverter.js +0 -71
- package/build/modules/listAllNotesHelper.js +0 -36
- package/build/modules/listChildHelper.js +0 -41
- package/build/modules/listChildrenHelper.js +0 -41
- package/build/modules/listDescendantNotesHelper.js +0 -48
- package/build/modules/listHandler.js +0 -37
- package/build/modules/noteFormatter.js +0 -58
- package/build/modules/responseUtils.js +0 -21
- package/build/utils/responseUtils.js +0 -21
package/README.md
CHANGED
|
@@ -1,188 +1,150 @@
|
|
|
1
|
-
# TriliumNext Notes' MCP Server
|
|
2
|
-
|
|
3
|
-
⚠️ **DISCLAIMER: This is a prototype for https://github.com/TriliumNext/Notes/issues/705. Suggested only for developer use. Please backup your Trilium notes before using this tool.** ⚠️
|
|
4
|
-
|
|
5
|
-
A model context protocol server for TriliumNext Notes. This server provides tools to interact with your Trilium Notes instance through MCP.
|
|
6
|
-
|
|
7
|
-
## Quick Start
|
|
8
|
-
|
|
9
|
-
Make sure to set up your environment variables first:
|
|
10
|
-
- `TRILIUM_API_URL` (default: http://localhost:8080/etapi)
|
|
11
|
-
- `TRILIUM_API_TOKEN` (required, get this from your Trilium Notes settings)
|
|
12
|
-
- `PERMISSIONS` (optional, default='READ;WRITE', where READ
|
|
13
|
-
- `VERBOSE` (optional, default='false',
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
### 1. Using with Claude Desktop
|
|
18
|
-
|
|
19
|
-
Add the server config to your Claude Desktop configuration file:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"triliumnext-mcp"
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
- `
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- "Create a new note called 'Weekly Review' in my journal folder"
|
|
152
|
-
|
|
153
|
-
> 📖 **More Examples**: See [User Query Examples](docs/user-query-examples.md) for comprehensive usage scenarios.
|
|
154
|
-
|
|
155
|
-
## Documentation
|
|
156
|
-
|
|
157
|
-
- [Content Modification Guide](docs/content-modification-guide.md) - Safe content editing with revision control
|
|
158
|
-
- [User Query Examples](docs/user-query-examples.md) - Natural language query examples
|
|
159
|
-
- [Search Query Examples](docs/search-examples/) - Advanced search syntax and filters
|
|
160
|
-
|
|
161
|
-
## Development
|
|
162
|
-
|
|
163
|
-
If you want to contribute or modify the server:
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
# Clone the repository
|
|
167
|
-
git clone https://github.com/tan-yong-sheng/triliumnext-mcp.git
|
|
168
|
-
|
|
169
|
-
# Install dependencies
|
|
170
|
-
npm install
|
|
171
|
-
|
|
172
|
-
# Build the server
|
|
173
|
-
npm run build
|
|
174
|
-
|
|
175
|
-
# For development with auto-rebuild
|
|
176
|
-
npm run watch
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Contributing
|
|
180
|
-
|
|
181
|
-
Contributions are welcome! If you are looking to improve the server, especially the search functionality, please familiarize yourself with the following resources:
|
|
182
|
-
|
|
183
|
-
- **Trilium Search DSL**: The [official documentation](https://triliumnext.github.io/Docs/Wiki/search.html) provides the foundation for all search queries.
|
|
184
|
-
- **Internal Search Implementation**: Our [Search Query Examples](docs/search-examples/) document details how `search_notes` parameters are translated into Trilium search strings. This is crucial for understanding and extending the current implementation.
|
|
185
|
-
|
|
186
|
-
Please feel free to open an issue or submit a pull request.
|
|
187
|
-
|
|
188
|
-
|
|
1
|
+
# TriliumNext Notes' MCP Server
|
|
2
|
+
|
|
3
|
+
⚠️ **DISCLAIMER: This is a prototype for https://github.com/TriliumNext/Notes/issues/705. Suggested only for developer use. Please backup your Trilium notes before using this tool.** ⚠️
|
|
4
|
+
|
|
5
|
+
A model context protocol server for TriliumNext Notes. This server provides tools to interact with your Trilium Notes instance through MCP.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
Make sure to set up your environment variables first:
|
|
10
|
+
- `TRILIUM_API_URL` (default: http://localhost:8080/etapi)
|
|
11
|
+
- `TRILIUM_API_TOKEN` (required, get this from your Trilium Notes settings)
|
|
12
|
+
- `PERMISSIONS` (optional, default='READ;WRITE', where READ grants access to `search_notes`, `get_note`, `resolve_note_id`, and `read_attributes`, and WRITE grants access to `create_note`, `update_note`, `delete_note`, and `manage_attributes`)
|
|
13
|
+
- `VERBOSE` (optional, default='false', which if true will print verbose debugging logs)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### 1. Using with Claude Desktop
|
|
18
|
+
|
|
19
|
+
Add the server config to your Claude Desktop configuration file:
|
|
20
|
+
|
|
21
|
+
#### For Local Installation (on Windows)
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
"triliumnext-mcp": {
|
|
25
|
+
"command": "cmd",
|
|
26
|
+
"args": [
|
|
27
|
+
"/k",
|
|
28
|
+
"npx",
|
|
29
|
+
"-y",
|
|
30
|
+
"triliumnext-mcp"
|
|
31
|
+
],
|
|
32
|
+
"env": {
|
|
33
|
+
"TRILIUM_API_URL": "http://localhost:8080/etapi",
|
|
34
|
+
"TRILIUM_API_TOKEN": "<YOUR_TRILIUM_API_TOKEN>",
|
|
35
|
+
"PERMISSIONS": "READ;WRITE"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### For Local installation (on Linux)
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
"triliumnext-mcp": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": [
|
|
46
|
+
"-y",
|
|
47
|
+
"triliumnext-mcp"
|
|
48
|
+
],
|
|
49
|
+
"env": {
|
|
50
|
+
"TRILIUM_API_URL": "http://localhost:8080/etapi",
|
|
51
|
+
"TRILIUM_API_TOKEN": "<YOUR_TRILIUM_API_TOKEN>",
|
|
52
|
+
"PERMISSIONS": "READ;WRITE"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### For Development (on Windows / Linux)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cd /path/to/triliumnext-mcp
|
|
61
|
+
npm run build
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
"triliumnext-mcp": {
|
|
66
|
+
"command": "node",
|
|
67
|
+
"args": [
|
|
68
|
+
"/path/to/triliumnext-mcp/build/index.js"
|
|
69
|
+
],
|
|
70
|
+
"env": {
|
|
71
|
+
"TRILIUM_API_URL": "http://localhost:8080/etapi",
|
|
72
|
+
"TRILIUM_API_TOKEN": "<YOUR_TRILIUM_API_TOKEN>",
|
|
73
|
+
"PERMISSIONS": "READ;WRITE",
|
|
74
|
+
"VERBOSE": "true"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Location of the configuration file:
|
|
80
|
+
- Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
81
|
+
- MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
82
|
+
|
|
83
|
+
**Feedback**: Please report issues and test results at [GitHub Issues](https://github.com/TriliumNext/Notes/issues)
|
|
84
|
+
|
|
85
|
+
## Available Tools
|
|
86
|
+
|
|
87
|
+
The server provides the following tools for note management:
|
|
88
|
+
|
|
89
|
+
### Search & Discovery Tools
|
|
90
|
+
|
|
91
|
+
- `search_notes` - Unified search with comprehensive filtering capabilities including keyword search, date ranges, field-specific searches, attribute searches, note properties, template-based searches, note type filtering, MIME type filtering, and hierarchy navigation.
|
|
92
|
+
- `resolve_note_id` - Find a note's ID by its title. Essential for getting a note's ID to use with other tools.
|
|
93
|
+
|
|
94
|
+
### Note Management Tools
|
|
95
|
+
|
|
96
|
+
- `get_note` - Retrieve a note and its content by ID. Can also be used with regex to extract specific patterns from the content.
|
|
97
|
+
- `create_note` - Create a new note. Supports 9 note types and allows creating attributes (labels and relations) in the same step.
|
|
98
|
+
- `update_note` - Updates a note's title or content. Requires a `mode` (`'overwrite'` or `'append'`) to specify the update type and an `expectedHash` to prevent conflicts.
|
|
99
|
+
- `delete_note` - Permanently delete a note (⚠️ cannot be undone).
|
|
100
|
+
|
|
101
|
+
### Attribute Management Tools
|
|
102
|
+
|
|
103
|
+
- `read_attributes` - Read all attributes (labels and relations) for a given note.
|
|
104
|
+
- `manage_attributes` - Create, update, or delete attributes on a note. Supports batch creation.
|
|
105
|
+
|
|
106
|
+
> 📖 **Detailed Usage**: See [Note Management Guide](docs/manage-notes-examples/index.md) for revision control strategy and best practices.
|
|
107
|
+
|
|
108
|
+
## Example Queries
|
|
109
|
+
|
|
110
|
+
### Search & Discovery
|
|
111
|
+
- "Find my most recent 10 notes about 'n8n' since the beginning of 2024"
|
|
112
|
+
- "Show me notes I've edited in the last 7 days"
|
|
113
|
+
- "List all notes under 'n8n Template' folder, including subfolders"
|
|
114
|
+
|
|
115
|
+
### Content Management
|
|
116
|
+
- "Add today's update to my work log" (uses `update_note` with `mode: 'append'`)
|
|
117
|
+
- "Replace this draft with the final version" (uses `update_note` with `mode: 'overwrite'`)
|
|
118
|
+
- "Create a new note called 'Weekly Review' in my journal folder"
|
|
119
|
+
|
|
120
|
+
> 📖 **More Examples**: See [User Query Examples](docs/user-query-examples.md) for comprehensive usage scenarios.
|
|
121
|
+
|
|
122
|
+
## Documentation
|
|
123
|
+
|
|
124
|
+
- [Note Management Guide](docs/manage-notes-examples/index.md) - Safe content editing with revision control
|
|
125
|
+
- [User Query Examples](docs/user-query-examples.md) - Natural language query examples
|
|
126
|
+
- [Search Query Examples](docs/search-examples/) - Advanced search syntax and filters
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
If you want to contribute or modify the server:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Clone the repository
|
|
134
|
+
git clone https://github.com/tan-yong-sheng/triliumnext-mcp.git
|
|
135
|
+
|
|
136
|
+
# Install dependencies
|
|
137
|
+
npm install
|
|
138
|
+
|
|
139
|
+
# Build the server
|
|
140
|
+
npm run build
|
|
141
|
+
|
|
142
|
+
# For development with auto-rebuild
|
|
143
|
+
npm run watch
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Contributing
|
|
147
|
+
|
|
148
|
+
Contributions are welcome! If you are looking to improve the server, please familiarize yourself with the official [Trilium Search DSL documentation](https://triliumnext.github.io/Docs/Wiki/search.html) and our internal [Search Query Examples](docs/search-examples/) to understand how search queries are constructed.
|
|
149
|
+
|
|
150
|
+
Please feel free to open an issue or submit a pull request.
|
package/build/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } f
|
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
// Import modular components
|
|
7
7
|
import { generateTools } from "./modules/toolDefinitions.js";
|
|
8
|
-
import { handleCreateNoteRequest, handleUpdateNoteRequest,
|
|
8
|
+
import { handleCreateNoteRequest, handleUpdateNoteRequest, handleDeleteNoteRequest, handleGetNoteRequest, handleSearchReplaceNoteRequest } from "./modules/noteHandler.js";
|
|
9
9
|
import { handleSearchNotesRequest } from "./modules/searchHandler.js";
|
|
10
10
|
import { handleResolveNoteRequest } from "./modules/resolveHandler.js";
|
|
11
11
|
import { handleManageAttributes, handleReadAttributes } from "./modules/attributeHandler.js";
|
|
@@ -23,7 +23,7 @@ class TriliumServer {
|
|
|
23
23
|
this.allowedPermissions = PERMISSIONS.split(';');
|
|
24
24
|
this.server = new Server({
|
|
25
25
|
name: "triliumnext-mcp",
|
|
26
|
-
version: "0.
|
|
26
|
+
version: "0.3.10",
|
|
27
27
|
}, {
|
|
28
28
|
capabilities: {
|
|
29
29
|
tools: {},
|
|
@@ -62,12 +62,12 @@ class TriliumServer {
|
|
|
62
62
|
return await handleCreateNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
63
63
|
case "update_note":
|
|
64
64
|
return await handleUpdateNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
65
|
-
case "append_note":
|
|
66
|
-
return await handleAppendNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
67
65
|
case "delete_note":
|
|
68
66
|
return await handleDeleteNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
69
67
|
case "get_note":
|
|
70
68
|
return await handleGetNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
69
|
+
case "search_and_replace_note":
|
|
70
|
+
return await handleSearchReplaceNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
71
71
|
// Search and listing operations
|
|
72
72
|
case "search_notes":
|
|
73
73
|
return await handleSearchNotesRequest(request.params.arguments, this.axiosInstance, this);
|
|
@@ -101,4 +101,4 @@ server.run().catch((error) => {
|
|
|
101
101
|
process.exit(1);
|
|
102
102
|
});
|
|
103
103
|
// Export helper functions for external use
|
|
104
|
-
export { buildNoteParams
|
|
104
|
+
export { buildNoteParams } from './utils/noteBuilder.js';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Centralized request handling for note operations
|
|
4
4
|
*/
|
|
5
5
|
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
-
import { handleCreateNote, handleUpdateNote,
|
|
6
|
+
import { handleCreateNote, handleUpdateNote, handleDeleteNote, handleGetNote, handleSearchReplaceNote } from "./noteManager.js";
|
|
7
7
|
/**
|
|
8
8
|
* Handle create_note tool requests
|
|
9
9
|
*/
|
|
@@ -13,12 +13,12 @@ export async function handleCreateNoteRequest(args, axiosInstance, permissionChe
|
|
|
13
13
|
}
|
|
14
14
|
try {
|
|
15
15
|
const noteOperation = {
|
|
16
|
-
parentNoteId: args.parentNoteId,
|
|
16
|
+
parentNoteId: args.parentNoteId || "root", // Use default value if not provided
|
|
17
17
|
title: args.title,
|
|
18
18
|
type: args.type,
|
|
19
19
|
content: args.content,
|
|
20
20
|
mime: args.mime,
|
|
21
|
-
attributes: args.attributes
|
|
21
|
+
attributes: args.attributes
|
|
22
22
|
};
|
|
23
23
|
const result = await handleCreateNote(noteOperation, axiosInstance);
|
|
24
24
|
return {
|
|
@@ -42,11 +42,28 @@ export async function handleUpdateNoteRequest(args, axiosInstance, permissionChe
|
|
|
42
42
|
if (!permissionChecker.hasPermission("WRITE")) {
|
|
43
43
|
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to update notes.");
|
|
44
44
|
}
|
|
45
|
+
// Validate that expectedHash is provided (required for data integrity)
|
|
46
|
+
if (!args.expectedHash) {
|
|
47
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing required parameter 'expectedHash'. You must call get_note first to retrieve the current blobId (content hash) before updating. This ensures data integrity by preventing overwriting changes made by other users.");
|
|
48
|
+
}
|
|
49
|
+
// Validate that either title or content is provided
|
|
50
|
+
if (!args.title && !args.content) {
|
|
51
|
+
throw new McpError(ErrorCode.InvalidParams, "Either 'title' or 'content' (or both) must be provided for update operation.");
|
|
52
|
+
}
|
|
53
|
+
// Validate that type is provided when content is being updated
|
|
54
|
+
if (args.content && !args.type) {
|
|
55
|
+
throw new McpError(ErrorCode.InvalidParams, "Parameter 'type' is required when updating content.");
|
|
56
|
+
}
|
|
45
57
|
try {
|
|
46
58
|
const noteOperation = {
|
|
47
59
|
noteId: args.noteId,
|
|
60
|
+
title: args.title,
|
|
61
|
+
type: args.type,
|
|
48
62
|
content: args.content,
|
|
49
|
-
|
|
63
|
+
mime: args.mime,
|
|
64
|
+
revision: args.revision !== false, // Default to true (safe behavior)
|
|
65
|
+
expectedHash: args.expectedHash,
|
|
66
|
+
mode: args.mode
|
|
50
67
|
};
|
|
51
68
|
const result = await handleUpdateNote(noteOperation, axiosInstance);
|
|
52
69
|
return {
|
|
@@ -64,19 +81,17 @@ export async function handleUpdateNoteRequest(args, axiosInstance, permissionChe
|
|
|
64
81
|
}
|
|
65
82
|
}
|
|
66
83
|
/**
|
|
67
|
-
* Handle
|
|
84
|
+
* Handle delete_note tool requests
|
|
68
85
|
*/
|
|
69
|
-
export async function
|
|
86
|
+
export async function handleDeleteNoteRequest(args, axiosInstance, permissionChecker) {
|
|
70
87
|
if (!permissionChecker.hasPermission("WRITE")) {
|
|
71
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to
|
|
88
|
+
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to delete notes.");
|
|
72
89
|
}
|
|
73
90
|
try {
|
|
74
91
|
const noteOperation = {
|
|
75
|
-
noteId: args.noteId
|
|
76
|
-
content: args.content,
|
|
77
|
-
revision: args.revision === true // Default to false (performance behavior)
|
|
92
|
+
noteId: args.noteId
|
|
78
93
|
};
|
|
79
|
-
const result = await
|
|
94
|
+
const result = await handleDeleteNote(noteOperation, axiosInstance);
|
|
80
95
|
return {
|
|
81
96
|
content: [{
|
|
82
97
|
type: "text",
|
|
@@ -92,21 +107,38 @@ export async function handleAppendNoteRequest(args, axiosInstance, permissionChe
|
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
/**
|
|
95
|
-
* Handle
|
|
110
|
+
* Handle get_note tool requests
|
|
96
111
|
*/
|
|
97
|
-
export async function
|
|
98
|
-
if (!permissionChecker.hasPermission("
|
|
99
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to
|
|
112
|
+
export async function handleGetNoteRequest(args, axiosInstance, permissionChecker) {
|
|
113
|
+
if (!permissionChecker.hasPermission("READ")) {
|
|
114
|
+
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to get notes.");
|
|
100
115
|
}
|
|
101
116
|
try {
|
|
102
117
|
const noteOperation = {
|
|
103
|
-
noteId: args.noteId
|
|
118
|
+
noteId: args.noteId,
|
|
119
|
+
includeContent: args.includeContent !== false,
|
|
120
|
+
searchPattern: args.searchPattern,
|
|
121
|
+
useRegex: args.useRegex !== false, // Default to true
|
|
122
|
+
searchFlags: args.searchFlags || 'g'
|
|
104
123
|
};
|
|
105
|
-
const result = await
|
|
124
|
+
const result = await handleGetNote(noteOperation, axiosInstance);
|
|
125
|
+
// Build response data based on whether search was performed
|
|
126
|
+
let responseData = {
|
|
127
|
+
...result.note,
|
|
128
|
+
contentHash: result.contentHash
|
|
129
|
+
};
|
|
130
|
+
if (result.search) {
|
|
131
|
+
// Search was performed, include search object but not content
|
|
132
|
+
responseData.search = result.search;
|
|
133
|
+
}
|
|
134
|
+
else if (result.content) {
|
|
135
|
+
// Standard response, include content but not search
|
|
136
|
+
responseData.content = result.content;
|
|
137
|
+
}
|
|
106
138
|
return {
|
|
107
139
|
content: [{
|
|
108
140
|
type: "text",
|
|
109
|
-
text:
|
|
141
|
+
text: JSON.stringify(responseData, null, 2)
|
|
110
142
|
}]
|
|
111
143
|
};
|
|
112
144
|
}
|
|
@@ -118,26 +150,38 @@ export async function handleDeleteNoteRequest(args, axiosInstance, permissionChe
|
|
|
118
150
|
}
|
|
119
151
|
}
|
|
120
152
|
/**
|
|
121
|
-
* Handle
|
|
153
|
+
* Handle search_and_replace_note tool requests
|
|
122
154
|
*/
|
|
123
|
-
export async function
|
|
124
|
-
if (!permissionChecker.hasPermission("
|
|
125
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to
|
|
155
|
+
export async function handleSearchReplaceNoteRequest(args, axiosInstance, permissionChecker) {
|
|
156
|
+
if (!permissionChecker.hasPermission("WRITE")) {
|
|
157
|
+
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to modify notes.");
|
|
158
|
+
}
|
|
159
|
+
// Validate that expectedHash is provided (required for data integrity)
|
|
160
|
+
if (!args.expectedHash) {
|
|
161
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing required parameter 'expectedHash'. You must call get_note first to retrieve the current blobId (content hash) before performing search and replace. This ensures data integrity by preventing overwriting changes made by other users.");
|
|
162
|
+
}
|
|
163
|
+
// Validate required parameters
|
|
164
|
+
if (!args.searchPattern) {
|
|
165
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing required parameter 'searchPattern'. The pattern to search for is required.");
|
|
166
|
+
}
|
|
167
|
+
if (!args.replacePattern) {
|
|
168
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing required parameter 'replacePattern'. The replacement pattern is required.");
|
|
126
169
|
}
|
|
127
170
|
try {
|
|
128
171
|
const noteOperation = {
|
|
129
172
|
noteId: args.noteId,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
173
|
+
searchPattern: args.searchPattern,
|
|
174
|
+
replacePattern: args.replacePattern,
|
|
175
|
+
useRegex: args.useRegex !== false, // Default to true
|
|
176
|
+
searchFlags: args.searchFlags || 'g',
|
|
177
|
+
revision: args.revision !== false, // Default to true for safety
|
|
178
|
+
expectedHash: args.expectedHash
|
|
136
179
|
};
|
|
180
|
+
const result = await handleSearchReplaceNote(noteOperation, axiosInstance);
|
|
137
181
|
return {
|
|
138
182
|
content: [{
|
|
139
183
|
type: "text",
|
|
140
|
-
text:
|
|
184
|
+
text: result.message
|
|
141
185
|
}]
|
|
142
186
|
};
|
|
143
187
|
}
|