triliumnext-mcp 0.3.10-beta.1 → 0.3.10
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 +188 -150
- package/build/index.js +2 -4
- package/build/modules/noteHandler.js +8 -52
- package/build/modules/noteManager.js +30 -226
- package/build/modules/searchQueryBuilder.js +9 -26
- package/build/modules/toolDefinitions.js +10 -57
- package/build/utils/contentProcessor.js +29 -3
- package/build/utils/hashUtils.js +95 -0
- package/build/utils/validationUtils.js +10 -4
- package/package.json +1 -1
- package/build/utils/contentIntegrity.js +0 -179
- package/build/utils/contentRules.js +0 -266
package/README.md
CHANGED
|
@@ -1,150 +1,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
|
|
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
|
-
|
|
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
|
-
|
|
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 let this MCP has permissions to perform `search_notes` and `get_note` operation and WRITE let this MCP has permissions to perform `create_note`, `update_note` and `delete_note` operations)
|
|
13
|
+
- `VERBOSE` (optional, default='false', where if true it will print out some logging response and pass the logs into LLM (such as API call) which is useful for developers to debug this MCP)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### 1. Using with Claude Desktop
|
|
18
|
+
|
|
19
|
+
Add the server config to your Claude Desktop configuration file:
|
|
20
|
+
|
|
21
|
+
Add the following configuration to the `mcpServers` object in your Claude configuration file:
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
#### For Local Installation (on Windows)
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
"triliumnext-mcp": {
|
|
28
|
+
"command": "cmd",
|
|
29
|
+
"args": [
|
|
30
|
+
"/k",
|
|
31
|
+
"npx",
|
|
32
|
+
"-y",
|
|
33
|
+
"triliumnext-mcp"
|
|
34
|
+
],
|
|
35
|
+
"env": {
|
|
36
|
+
"TRILIUM_API_URL": "http://localhost:8080/etapi",
|
|
37
|
+
"TRILIUM_API_TOKEN": "<YOUR_TRILIUM_API_TOKEN>",
|
|
38
|
+
"PERMISSIONS": "READ;WRITE"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### For Local installation (on Linux)
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
"triliumnext-mcp": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": [
|
|
49
|
+
"-y",
|
|
50
|
+
"triliumnext-mcp"
|
|
51
|
+
],
|
|
52
|
+
"env": {
|
|
53
|
+
"TRILIUM_API_URL": "http://localhost:8080/etapi",
|
|
54
|
+
"TRILIUM_API_TOKEN": "<YOUR_TRILIUM_API_TOKEN>",
|
|
55
|
+
"PERMISSIONS": "READ;WRITE"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### For Development (on Windows / Linux)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd /path/to/triliumnext-mcp
|
|
64
|
+
npm run build
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
"triliumnext-mcp": {
|
|
69
|
+
"command": "node",
|
|
70
|
+
"args": [
|
|
71
|
+
"/path/to/triliumnext-mcp/build/index.js"
|
|
72
|
+
],
|
|
73
|
+
"env": {
|
|
74
|
+
"TRILIUM_API_URL": "http://localhost:8080/etapi",
|
|
75
|
+
"TRILIUM_API_TOKEN": "<YOUR_TRILIUM_API_TOKEN>",
|
|
76
|
+
"PERMISSIONS": "READ;WRITE",
|
|
77
|
+
"VERBOSE": "true"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
Location of the configuration file:
|
|
84
|
+
- Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
85
|
+
- MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
**Feedback**: Please report issues and test results at [GitHub Issues](https://github.com/TriliumNext/Notes/issues)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## Available Tools
|
|
92
|
+
|
|
93
|
+
The server provides the following tools for note management:
|
|
94
|
+
|
|
95
|
+
### Search Tools
|
|
96
|
+
|
|
97
|
+
- `search_notes` - Unified search with comprehensive filtering capabilities
|
|
98
|
+
- **Unified Architecture**: Uses `searchCriteria` parameter for complete boolean logic expressiveness
|
|
99
|
+
- **Cross-type OR logic**: Combine labels, relations, note properties, and hierarchy navigation with OR/AND logic
|
|
100
|
+
- **Parameters**: `text` (keyword search), `searchCriteria` (unified array structure), `limit`
|
|
101
|
+
- **Smart optimization**: Automatically uses fastSearch when only text parameter is provided
|
|
102
|
+
- **Complete filtering**: Supports labels (#book), relations (~author.title), note properties (title, content, dateCreated), hierarchy navigation (parents.*, children.*, ancestors.*)
|
|
103
|
+
- **Navigation support**: Use hierarchy properties like `parents.noteId` for direct children, `ancestors.noteId` for all descendants
|
|
104
|
+
|
|
105
|
+
- `resolve_note_id` - Find note ID by name/title for LLM-friendly workflows
|
|
106
|
+
- **Simple title-based search**: Uses fuzzy search to find notes by title/name
|
|
107
|
+
- **Smart prioritization**: Exact matches → folder-type notes → most recent
|
|
108
|
+
- **User choice workflow**: When multiple matches found, presents options for user selection
|
|
109
|
+
- **Configurable results**: `maxResults` parameter (default: 3, range: 1-10)
|
|
110
|
+
- **JSON response format**: Returns structured data with selectedNote, totalMatches, and nextSteps guidance
|
|
111
|
+
- **Essential workflow**: resolve name → get ID → use with other tools
|
|
112
|
+
|
|
113
|
+
### Note Discovery Tools
|
|
114
|
+
|
|
115
|
+
- `search_notes` - Advanced search with unified filtering capabilities including hierarchy navigation
|
|
116
|
+
- **Complex queries**: Use for sophisticated filtering with multiple criteria
|
|
117
|
+
- **Boolean logic**: Cross-type OR/AND operations between all search criteria types
|
|
118
|
+
- **Unified structure**: Single `searchCriteria` parameter handles labels, relations, properties, and hierarchy
|
|
119
|
+
- **Navigation support**: Use hierarchy properties like `parents.noteId` for direct children, `ancestors.noteId` for all descendants
|
|
120
|
+
- **Performance optimized**: Automatic fastSearch when appropriate
|
|
121
|
+
|
|
122
|
+
### Note Management Tools
|
|
123
|
+
|
|
124
|
+
- `get_note` - Retrieve a note content by ID
|
|
125
|
+
- `create_note` - Create a new note (supports various types: text, code, file, image, etc.)
|
|
126
|
+
- `update_note` - Replace entire note content (⚠️ creates backup by default)
|
|
127
|
+
- `append_note` - Add content while preserving existing content (📝 optimized for logs/journals)
|
|
128
|
+
- `delete_note` - Permanently delete a note (⚠️ cannot be undone)
|
|
129
|
+
|
|
130
|
+
> 📖 **Detailed Usage**: See [Content Modification Guide](docs/content-modification-guide.md) for revision control strategy and best practices.
|
|
131
|
+
|
|
132
|
+
## Example Queries
|
|
133
|
+
|
|
134
|
+
### Search & Discovery
|
|
135
|
+
- "Find my most recent 10 notes about 'n8n' since the beginning of 2020" → Uses `search_notes` with unified searchCriteria
|
|
136
|
+
- "Show me notes I've edited in the last 7 days" → Uses `search_notes` with date properties
|
|
137
|
+
- "Find notes with 'machine learning' in the title created this year" → Uses `search_notes` with cross-type criteria
|
|
138
|
+
- "Search for 'kubernetes' in notes created between January and June" → Uses `search_notes` with boolean logic
|
|
139
|
+
|
|
140
|
+
### Navigation & Browsing
|
|
141
|
+
- "List all notes including subfolders" → Uses `search_notes` with `ancestors.noteId` hierarchy property
|
|
142
|
+
- "Show me everything I have" → Uses `search_notes` with `ancestors.noteId` for complete inventory
|
|
143
|
+
- "List all notes" → Uses `search_notes` with `parents.noteId` hierarchy property
|
|
144
|
+
- "List all notes under 'n8n Template' folder" → Uses `search_notes` with `parents.noteId` hierarchy property
|
|
145
|
+
- "List all notes under 'n8n Template' folder, including subfolders" → Uses `search_notes` with `ancestors.noteId` hierarchy property
|
|
146
|
+
- "Find notes by author Tolkien OR created this week" → Uses `search_notes` with unified `searchCriteria` for cross-type OR logic
|
|
147
|
+
|
|
148
|
+
### Content Management
|
|
149
|
+
- "Add today's update to my work log" (uses `append_note`)
|
|
150
|
+
- "Replace this draft with the final version" (uses `update_note`)
|
|
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
|
+
|
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, handleDeleteNoteRequest, handleGetNoteRequest
|
|
8
|
+
import { handleCreateNoteRequest, handleUpdateNoteRequest, handleDeleteNoteRequest, handleGetNoteRequest } 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.1.0",
|
|
27
27
|
}, {
|
|
28
28
|
capabilities: {
|
|
29
29
|
tools: {},
|
|
@@ -66,8 +66,6 @@ class TriliumServer {
|
|
|
66
66
|
return await handleDeleteNoteRequest(request.params.arguments, this.axiosInstance, this);
|
|
67
67
|
case "get_note":
|
|
68
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
69
|
// Search and listing operations
|
|
72
70
|
case "search_notes":
|
|
73
71
|
return await handleSearchNotesRequest(request.params.arguments, this.axiosInstance, this);
|
|
@@ -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, handleDeleteNote, handleGetNote
|
|
6
|
+
import { handleCreateNote, handleUpdateNote, handleDeleteNote, handleGetNote } from "./noteManager.js";
|
|
7
7
|
/**
|
|
8
8
|
* Handle create_note tool requests
|
|
9
9
|
*/
|
|
@@ -117,22 +117,21 @@ export async function handleGetNoteRequest(args, axiosInstance, permissionChecke
|
|
|
117
117
|
const noteOperation = {
|
|
118
118
|
noteId: args.noteId,
|
|
119
119
|
includeContent: args.includeContent !== false,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
searchFlags: args.searchFlags || 'g'
|
|
120
|
+
regexPattern: args.regexPattern,
|
|
121
|
+
regexFlags: args.regexFlags || 'g'
|
|
123
122
|
};
|
|
124
123
|
const result = await handleGetNote(noteOperation, axiosInstance);
|
|
125
|
-
// Build response data based on whether search was performed
|
|
124
|
+
// Build response data based on whether regex search was performed
|
|
126
125
|
let responseData = {
|
|
127
126
|
...result.note,
|
|
128
127
|
contentHash: result.contentHash
|
|
129
128
|
};
|
|
130
|
-
if (result.
|
|
131
|
-
//
|
|
132
|
-
responseData.
|
|
129
|
+
if (result.regexSearch) {
|
|
130
|
+
// Regex search was performed, include regexSearch object but not content
|
|
131
|
+
responseData.regexSearch = result.regexSearch;
|
|
133
132
|
}
|
|
134
133
|
else if (result.content) {
|
|
135
|
-
// Standard response, include content but not
|
|
134
|
+
// Standard response, include content but not regexSearch
|
|
136
135
|
responseData.content = result.content;
|
|
137
136
|
}
|
|
138
137
|
return {
|
|
@@ -149,46 +148,3 @@ export async function handleGetNoteRequest(args, axiosInstance, permissionChecke
|
|
|
149
148
|
throw new McpError(ErrorCode.InvalidParams, error instanceof Error ? error.message : String(error));
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Handle search_and_replace_note tool requests
|
|
154
|
-
*/
|
|
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.");
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
const noteOperation = {
|
|
172
|
-
noteId: args.noteId,
|
|
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
|
|
179
|
-
};
|
|
180
|
-
const result = await handleSearchReplaceNote(noteOperation, axiosInstance);
|
|
181
|
-
return {
|
|
182
|
-
content: [{
|
|
183
|
-
type: "text",
|
|
184
|
-
text: result.message
|
|
185
|
-
}]
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
catch (error) {
|
|
189
|
-
if (error instanceof McpError) {
|
|
190
|
-
throw error;
|
|
191
|
-
}
|
|
192
|
-
throw new McpError(ErrorCode.InvalidParams, error instanceof Error ? error.message : String(error));
|
|
193
|
-
}
|
|
194
|
-
}
|