@mauricio.wolff/mcp-obsidian 0.4.1 → 0.5.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 +29 -28
- package/dist/server.js +489 -0
- package/dist/src/filesystem.js +525 -0
- package/dist/src/frontmatter.js +104 -0
- package/dist/src/pathfilter.js +57 -0
- package/dist/src/search.js +105 -0
- package/dist/src/types.js +1 -0
- package/package.json +18 -13
- package/server.ts +3 -3
- package/src/filesystem.ts +0 -602
- package/src/frontmatter.ts +0 -124
- package/src/pathfilter.ts +0 -72
- package/src/search.ts +0 -97
- package/src/types.ts +0 -119
package/README.md
CHANGED
|
@@ -6,16 +6,17 @@ A lightweight Model Context Protocol (MCP) server for safe Obsidian vault access
|
|
|
6
6
|
|
|
7
7
|
## Quick Start (5 minutes)
|
|
8
8
|
|
|
9
|
-
1. **Install
|
|
9
|
+
1. **Install Node.js runtime:**
|
|
10
10
|
```bash
|
|
11
|
-
|
|
11
|
+
# Download from https://nodejs.org (v18.0.0 or later)
|
|
12
|
+
# or use a package manager like nvm, brew, apt, etc.
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
2. **Test the server:**
|
|
15
16
|
|
|
16
17
|
If using the published package:
|
|
17
18
|
```bash
|
|
18
|
-
|
|
19
|
+
npx @modelcontextprotocol/inspector npx @mauricio.wolff/mcp-obsidian /path/to/your/vault
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
3. **Configure your AI client:**
|
|
@@ -25,7 +26,7 @@ A lightweight Model Context Protocol (MCP) server for safe Obsidian vault access
|
|
|
25
26
|
{
|
|
26
27
|
"mcpServers": {
|
|
27
28
|
"obsidian": {
|
|
28
|
-
"command": "
|
|
29
|
+
"command": "npx",
|
|
29
30
|
"args": ["@mauricio.wolff/mcp-obsidian", "/path/to/your/vault"]
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -37,7 +38,7 @@ A lightweight Model Context Protocol (MCP) server for safe Obsidian vault access
|
|
|
37
38
|
{
|
|
38
39
|
"mcpServers": {
|
|
39
40
|
"obsidian": {
|
|
40
|
-
"command": "
|
|
41
|
+
"command": "npx",
|
|
41
42
|
"args": ["@mauricio.wolff/mcp-obsidian", "/path/to/your/vault"],
|
|
42
43
|
"env": {}
|
|
43
44
|
}
|
|
@@ -71,12 +72,12 @@ A lightweight Model Context Protocol (MCP) server for safe Obsidian vault access
|
|
|
71
72
|
- ✅ **NEW:** Tag management: add, remove, and list tags in notes
|
|
72
73
|
- ✅ Safe deletion with confirmation requirement to prevent accidents
|
|
73
74
|
- ✅ Automatic path trimming to handle whitespace in inputs
|
|
74
|
-
- ✅ TypeScript support with
|
|
75
|
+
- ✅ TypeScript support with Node.js runtime (using tsx for execution)
|
|
75
76
|
- ✅ Comprehensive error handling and validation
|
|
76
77
|
|
|
77
78
|
## Prerequisites
|
|
78
79
|
|
|
79
|
-
- [
|
|
80
|
+
- [Node.js](https://nodejs.org) runtime (v18.0.0 or later)
|
|
80
81
|
- An Obsidian vault (local directory with `.md` files)
|
|
81
82
|
- MCP-compatible AI client (Claude Desktop, ChatGPT Desktop, Claude Code, etc.)
|
|
82
83
|
|
|
@@ -84,23 +85,23 @@ A lightweight Model Context Protocol (MCP) server for safe Obsidian vault access
|
|
|
84
85
|
|
|
85
86
|
### For End Users (Recommended)
|
|
86
87
|
|
|
87
|
-
No installation needed! Use `
|
|
88
|
+
No installation needed! Use `npx` to run directly:
|
|
88
89
|
|
|
89
90
|
```bash
|
|
90
|
-
|
|
91
|
+
npx @mauricio.wolff/mcp-obsidian /path/to/your/obsidian/vault
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
### For Developers
|
|
94
95
|
|
|
95
96
|
1. Clone this repository
|
|
96
|
-
2. Install dependencies with
|
|
97
|
+
2. Install dependencies with npm:
|
|
97
98
|
```bash
|
|
98
|
-
|
|
99
|
+
npm install
|
|
99
100
|
```
|
|
100
101
|
|
|
101
102
|
3. Test locally with MCP inspector:
|
|
102
103
|
```bash
|
|
103
|
-
|
|
104
|
+
npx @modelcontextprotocol/inspector npm start /path/to/your/vault
|
|
104
105
|
```
|
|
105
106
|
|
|
106
107
|
## Usage
|
|
@@ -109,12 +110,12 @@ bunx @modelcontextprotocol/inspector bun server.ts /path/to/your/vault
|
|
|
109
110
|
|
|
110
111
|
**End users:**
|
|
111
112
|
```bash
|
|
112
|
-
|
|
113
|
+
npx @mauricio.wolff/mcp-obsidian /path/to/your/obsidian/vault
|
|
113
114
|
```
|
|
114
115
|
|
|
115
116
|
**Developers:**
|
|
116
117
|
```bash
|
|
117
|
-
|
|
118
|
+
npm start /path/to/your/obsidian/vault
|
|
118
119
|
```
|
|
119
120
|
|
|
120
121
|
### AI Client Configuration
|
|
@@ -128,7 +129,7 @@ Add to your Claude Desktop configuration file:
|
|
|
128
129
|
{
|
|
129
130
|
"mcpServers": {
|
|
130
131
|
"obsidian": {
|
|
131
|
-
"command": "
|
|
132
|
+
"command": "npx",
|
|
132
133
|
"args": ["@mauricio.wolff/mcp-obsidian", "/Users/yourname/Documents/MyVault"]
|
|
133
134
|
}
|
|
134
135
|
}
|
|
@@ -140,11 +141,11 @@ Add to your Claude Desktop configuration file:
|
|
|
140
141
|
{
|
|
141
142
|
"mcpServers": {
|
|
142
143
|
"obsidian-personal": {
|
|
143
|
-
"command": "
|
|
144
|
+
"command": "npx",
|
|
144
145
|
"args": ["@mauricio.wolff/mcp-obsidian", "/Users/yourname/Documents/PersonalVault"]
|
|
145
146
|
},
|
|
146
147
|
"obsidian-work": {
|
|
147
|
-
"command": "
|
|
148
|
+
"command": "npx",
|
|
148
149
|
"args": ["@mauricio.wolff/mcp-obsidian", "/Users/yourname/Documents/WorkVault"]
|
|
149
150
|
}
|
|
150
151
|
}
|
|
@@ -180,7 +181,7 @@ Edit `~/.claude.json`:
|
|
|
180
181
|
{
|
|
181
182
|
"mcpServers": {
|
|
182
183
|
"obsidian": {
|
|
183
|
-
"command": "
|
|
184
|
+
"command": "npx",
|
|
184
185
|
"args": ["@mauricio.wolff/mcp-obsidian", "/path/to/your/vault"],
|
|
185
186
|
"env": {}
|
|
186
187
|
}
|
|
@@ -196,7 +197,7 @@ Edit `.claude.json` in your project or add to the projects section:
|
|
|
196
197
|
"/path/to/your/project": {
|
|
197
198
|
"mcpServers": {
|
|
198
199
|
"obsidian": {
|
|
199
|
-
"command": "
|
|
200
|
+
"command": "npx",
|
|
200
201
|
"args": ["@mauricio.wolff/mcp-obsidian", "/path/to/your/vault"]
|
|
201
202
|
}
|
|
202
203
|
}
|
|
@@ -207,7 +208,7 @@ Edit `.claude.json` in your project or add to the projects section:
|
|
|
207
208
|
|
|
208
209
|
**Using Claude Code CLI:**
|
|
209
210
|
```bash
|
|
210
|
-
claude mcp add obsidian --scope user
|
|
211
|
+
claude mcp add obsidian --scope user npx @mauricio.wolff/mcp-obsidian /path/to/your/vault
|
|
211
212
|
```
|
|
212
213
|
|
|
213
214
|
#### Other MCP-Compatible Clients (2025)
|
|
@@ -241,11 +242,11 @@ Most modern MCP clients use similar JSON configuration patterns. Refer to your s
|
|
|
241
242
|
|
|
242
243
|
### Common Issues
|
|
243
244
|
|
|
244
|
-
#### "command not found:
|
|
245
|
-
- **Solution:** Install
|
|
246
|
-
- **Alternative:** Use
|
|
245
|
+
#### "command not found: npx"
|
|
246
|
+
- **Solution:** Install Node.js runtime from [nodejs.org](https://nodejs.org)
|
|
247
|
+
- **Alternative:** Use global install: `npm install -g @mauricio.wolff/mcp-obsidian`
|
|
247
248
|
|
|
248
|
-
#### "Usage:
|
|
249
|
+
#### "Usage: node server.ts /path/to/vault"
|
|
249
250
|
- **Cause:** No vault path provided
|
|
250
251
|
- **Solution:** Specify the full path to your Obsidian vault directory
|
|
251
252
|
|
|
@@ -272,20 +273,20 @@ Most modern MCP clients use similar JSON configuration patterns. Refer to your s
|
|
|
272
273
|
|
|
273
274
|
Run with error logging:
|
|
274
275
|
```bash
|
|
275
|
-
|
|
276
|
+
npx @mauricio.wolff/mcp-obsidian /path/to/vault 2>debug.log
|
|
276
277
|
```
|
|
277
278
|
|
|
278
279
|
### Getting Help
|
|
279
280
|
|
|
280
281
|
- [Open an issue](https://github.com/bitbonsai/mcp-obsidian/issues) on GitHub
|
|
281
|
-
- Include your OS,
|
|
282
|
+
- Include your OS, Node.js version, and error messages
|
|
282
283
|
- Provide the vault directory structure (without sensitive content)
|
|
283
284
|
|
|
284
285
|
## Testing
|
|
285
286
|
|
|
286
287
|
Run the test suite:
|
|
287
288
|
```bash
|
|
288
|
-
|
|
289
|
+
npm test
|
|
289
290
|
```
|
|
290
291
|
|
|
291
292
|
## API Methods
|
|
@@ -691,7 +692,7 @@ This MCP server implements several security measures to protect your Obsidian va
|
|
|
691
692
|
1. Fork the repository
|
|
692
693
|
2. Create a feature branch: `git checkout -b feature-name`
|
|
693
694
|
3. Make your changes and add tests
|
|
694
|
-
4. Ensure all tests pass: `
|
|
695
|
+
4. Ensure all tests pass: `npm test`
|
|
695
696
|
5. Submit a pull request
|
|
696
697
|
|
|
697
698
|
## License
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { FileSystemService } from "./src/filesystem.js";
|
|
6
|
+
import { FrontmatterHandler } from "./src/frontmatter.js";
|
|
7
|
+
import { PathFilter } from "./src/pathfilter.js";
|
|
8
|
+
import { SearchService } from "./src/search.js";
|
|
9
|
+
const vaultPath = process.argv[2];
|
|
10
|
+
if (!vaultPath) {
|
|
11
|
+
console.error("Usage: npx @mauricio.wolff/mcp-obsidian /path/to/vault");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
// Initialize services
|
|
15
|
+
const pathFilter = new PathFilter();
|
|
16
|
+
const frontmatterHandler = new FrontmatterHandler();
|
|
17
|
+
const fileSystem = new FileSystemService(vaultPath, pathFilter, frontmatterHandler);
|
|
18
|
+
const searchService = new SearchService(vaultPath, pathFilter);
|
|
19
|
+
const server = new Server({
|
|
20
|
+
name: "mcp-obsidian",
|
|
21
|
+
version: "0.5.1"
|
|
22
|
+
}, {
|
|
23
|
+
capabilities: {
|
|
24
|
+
tools: {},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
28
|
+
return {
|
|
29
|
+
tools: [
|
|
30
|
+
{
|
|
31
|
+
name: "read_note",
|
|
32
|
+
description: "Read a note from the Obsidian vault",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
path: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "Path to the note relative to vault root"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
required: ["path"]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "write_note",
|
|
46
|
+
description: "Write a note to the Obsidian vault",
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
path: {
|
|
51
|
+
type: "string",
|
|
52
|
+
description: "Path to the note relative to vault root"
|
|
53
|
+
},
|
|
54
|
+
content: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Content of the note"
|
|
57
|
+
},
|
|
58
|
+
frontmatter: {
|
|
59
|
+
type: "object",
|
|
60
|
+
description: "Frontmatter object (optional)"
|
|
61
|
+
},
|
|
62
|
+
mode: {
|
|
63
|
+
type: "string",
|
|
64
|
+
enum: ["overwrite", "append", "prepend"],
|
|
65
|
+
description: "Write mode: 'overwrite' (default), 'append', or 'prepend'",
|
|
66
|
+
default: "overwrite"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
required: ["path", "content"]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "list_directory",
|
|
74
|
+
description: "List files and directories in the vault",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
path: {
|
|
79
|
+
type: "string",
|
|
80
|
+
description: "Path relative to vault root (default: '/')",
|
|
81
|
+
default: "/"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "delete_note",
|
|
88
|
+
description: "Delete a note from the Obsidian vault (requires confirmation)",
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
path: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: "Path to the note relative to vault root"
|
|
95
|
+
},
|
|
96
|
+
confirmPath: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Confirmation: must exactly match the path parameter to proceed with deletion"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
required: ["path", "confirmPath"]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "search_notes",
|
|
106
|
+
description: "Search for notes in the vault by content or frontmatter",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: "object",
|
|
109
|
+
properties: {
|
|
110
|
+
query: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "Search query text"
|
|
113
|
+
},
|
|
114
|
+
limit: {
|
|
115
|
+
type: "number",
|
|
116
|
+
description: "Maximum number of results (default: 5, max: 20)",
|
|
117
|
+
default: 5
|
|
118
|
+
},
|
|
119
|
+
searchContent: {
|
|
120
|
+
type: "boolean",
|
|
121
|
+
description: "Search in note content (default: true)",
|
|
122
|
+
default: true
|
|
123
|
+
},
|
|
124
|
+
searchFrontmatter: {
|
|
125
|
+
type: "boolean",
|
|
126
|
+
description: "Search in frontmatter (default: false)",
|
|
127
|
+
default: false
|
|
128
|
+
},
|
|
129
|
+
caseSensitive: {
|
|
130
|
+
type: "boolean",
|
|
131
|
+
description: "Case sensitive search (default: false)",
|
|
132
|
+
default: false
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
required: ["query"]
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "move_note",
|
|
140
|
+
description: "Move or rename a note in the vault",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: "object",
|
|
143
|
+
properties: {
|
|
144
|
+
oldPath: {
|
|
145
|
+
type: "string",
|
|
146
|
+
description: "Current path of the note"
|
|
147
|
+
},
|
|
148
|
+
newPath: {
|
|
149
|
+
type: "string",
|
|
150
|
+
description: "New path for the note"
|
|
151
|
+
},
|
|
152
|
+
overwrite: {
|
|
153
|
+
type: "boolean",
|
|
154
|
+
description: "Allow overwriting existing file (default: false)",
|
|
155
|
+
default: false
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
required: ["oldPath", "newPath"]
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "read_multiple_notes",
|
|
163
|
+
description: "Read multiple notes in a batch (max 10 files)",
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: "object",
|
|
166
|
+
properties: {
|
|
167
|
+
paths: {
|
|
168
|
+
type: "array",
|
|
169
|
+
items: { type: "string" },
|
|
170
|
+
description: "Array of note paths to read",
|
|
171
|
+
maxItems: 10
|
|
172
|
+
},
|
|
173
|
+
includeContent: {
|
|
174
|
+
type: "boolean",
|
|
175
|
+
description: "Include note content (default: true)",
|
|
176
|
+
default: true
|
|
177
|
+
},
|
|
178
|
+
includeFrontmatter: {
|
|
179
|
+
type: "boolean",
|
|
180
|
+
description: "Include frontmatter (default: true)",
|
|
181
|
+
default: true
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
required: ["paths"]
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: "update_frontmatter",
|
|
189
|
+
description: "Update frontmatter of a note without changing content",
|
|
190
|
+
inputSchema: {
|
|
191
|
+
type: "object",
|
|
192
|
+
properties: {
|
|
193
|
+
path: {
|
|
194
|
+
type: "string",
|
|
195
|
+
description: "Path to the note"
|
|
196
|
+
},
|
|
197
|
+
frontmatter: {
|
|
198
|
+
type: "object",
|
|
199
|
+
description: "Frontmatter object to update"
|
|
200
|
+
},
|
|
201
|
+
merge: {
|
|
202
|
+
type: "boolean",
|
|
203
|
+
description: "Merge with existing frontmatter (default: true)",
|
|
204
|
+
default: true
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
required: ["path", "frontmatter"]
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: "get_notes_info",
|
|
212
|
+
description: "Get metadata for notes without reading full content",
|
|
213
|
+
inputSchema: {
|
|
214
|
+
type: "object",
|
|
215
|
+
properties: {
|
|
216
|
+
paths: {
|
|
217
|
+
type: "array",
|
|
218
|
+
items: { type: "string" },
|
|
219
|
+
description: "Array of note paths to get info for"
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
required: ["paths"]
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "get_frontmatter",
|
|
227
|
+
description: "Extract frontmatter from a note without reading the content",
|
|
228
|
+
inputSchema: {
|
|
229
|
+
type: "object",
|
|
230
|
+
properties: {
|
|
231
|
+
path: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: "Path to the note relative to vault root"
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
required: ["path"]
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "manage_tags",
|
|
241
|
+
description: "Add, remove, or list tags in a note",
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object",
|
|
244
|
+
properties: {
|
|
245
|
+
path: {
|
|
246
|
+
type: "string",
|
|
247
|
+
description: "Path to the note relative to vault root"
|
|
248
|
+
},
|
|
249
|
+
operation: {
|
|
250
|
+
type: "string",
|
|
251
|
+
enum: ["add", "remove", "list"],
|
|
252
|
+
description: "Operation to perform: 'add', 'remove', or 'list'"
|
|
253
|
+
},
|
|
254
|
+
tags: {
|
|
255
|
+
type: "array",
|
|
256
|
+
items: { type: "string" },
|
|
257
|
+
description: "Array of tags (required for 'add' and 'remove' operations)"
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
required: ["path", "operation"]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
// Helper function to trim path arguments
|
|
267
|
+
function trimPaths(args) {
|
|
268
|
+
const trimmed = { ...args };
|
|
269
|
+
// Trim single path properties
|
|
270
|
+
if (trimmed.path && typeof trimmed.path === 'string') {
|
|
271
|
+
trimmed.path = trimmed.path.trim();
|
|
272
|
+
}
|
|
273
|
+
if (trimmed.oldPath && typeof trimmed.oldPath === 'string') {
|
|
274
|
+
trimmed.oldPath = trimmed.oldPath.trim();
|
|
275
|
+
}
|
|
276
|
+
if (trimmed.newPath && typeof trimmed.newPath === 'string') {
|
|
277
|
+
trimmed.newPath = trimmed.newPath.trim();
|
|
278
|
+
}
|
|
279
|
+
if (trimmed.confirmPath && typeof trimmed.confirmPath === 'string') {
|
|
280
|
+
trimmed.confirmPath = trimmed.confirmPath.trim();
|
|
281
|
+
}
|
|
282
|
+
// Trim path arrays
|
|
283
|
+
if (trimmed.paths && Array.isArray(trimmed.paths)) {
|
|
284
|
+
trimmed.paths = trimmed.paths.map((p) => typeof p === 'string' ? p.trim() : p);
|
|
285
|
+
}
|
|
286
|
+
return trimmed;
|
|
287
|
+
}
|
|
288
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
289
|
+
const { name, arguments: args } = request.params;
|
|
290
|
+
const trimmedArgs = trimPaths(args);
|
|
291
|
+
try {
|
|
292
|
+
switch (name) {
|
|
293
|
+
case "read_note": {
|
|
294
|
+
const note = await fileSystem.readNote(trimmedArgs.path);
|
|
295
|
+
return {
|
|
296
|
+
content: [
|
|
297
|
+
{
|
|
298
|
+
type: "text",
|
|
299
|
+
text: JSON.stringify({
|
|
300
|
+
path: trimmedArgs.path,
|
|
301
|
+
frontmatter: note.frontmatter,
|
|
302
|
+
content: note.content
|
|
303
|
+
}, null, 2)
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
case "write_note": {
|
|
309
|
+
await fileSystem.writeNote({
|
|
310
|
+
path: trimmedArgs.path,
|
|
311
|
+
content: trimmedArgs.content,
|
|
312
|
+
frontmatter: trimmedArgs.frontmatter,
|
|
313
|
+
mode: trimmedArgs.mode || 'overwrite'
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
content: [
|
|
317
|
+
{
|
|
318
|
+
type: "text",
|
|
319
|
+
text: `Successfully wrote note: ${trimmedArgs.path} (mode: ${trimmedArgs.mode || 'overwrite'})`
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
case "list_directory": {
|
|
325
|
+
const listing = await fileSystem.listDirectory(trimmedArgs.path || '');
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: JSON.stringify({
|
|
331
|
+
path: trimmedArgs.path || '/',
|
|
332
|
+
directories: listing.directories,
|
|
333
|
+
files: listing.files
|
|
334
|
+
}, null, 2)
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
case "delete_note": {
|
|
340
|
+
const result = await fileSystem.deleteNote({
|
|
341
|
+
path: trimmedArgs.path,
|
|
342
|
+
confirmPath: trimmedArgs.confirmPath
|
|
343
|
+
});
|
|
344
|
+
return {
|
|
345
|
+
content: [
|
|
346
|
+
{
|
|
347
|
+
type: "text",
|
|
348
|
+
text: JSON.stringify(result, null, 2)
|
|
349
|
+
}
|
|
350
|
+
],
|
|
351
|
+
isError: !result.success
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
case "search_notes": {
|
|
355
|
+
const results = await searchService.search({
|
|
356
|
+
query: trimmedArgs.query,
|
|
357
|
+
limit: trimmedArgs.limit,
|
|
358
|
+
searchContent: trimmedArgs.searchContent,
|
|
359
|
+
searchFrontmatter: trimmedArgs.searchFrontmatter,
|
|
360
|
+
caseSensitive: trimmedArgs.caseSensitive
|
|
361
|
+
});
|
|
362
|
+
return {
|
|
363
|
+
content: [
|
|
364
|
+
{
|
|
365
|
+
type: "text",
|
|
366
|
+
text: JSON.stringify({
|
|
367
|
+
query: trimmedArgs.query,
|
|
368
|
+
resultCount: results.length,
|
|
369
|
+
results: results
|
|
370
|
+
}, null, 2)
|
|
371
|
+
}
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
case "move_note": {
|
|
376
|
+
const result = await fileSystem.moveNote({
|
|
377
|
+
oldPath: trimmedArgs.oldPath,
|
|
378
|
+
newPath: trimmedArgs.newPath,
|
|
379
|
+
overwrite: trimmedArgs.overwrite
|
|
380
|
+
});
|
|
381
|
+
return {
|
|
382
|
+
content: [
|
|
383
|
+
{
|
|
384
|
+
type: "text",
|
|
385
|
+
text: JSON.stringify(result, null, 2)
|
|
386
|
+
}
|
|
387
|
+
],
|
|
388
|
+
isError: !result.success
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
case "read_multiple_notes": {
|
|
392
|
+
const result = await fileSystem.readMultipleNotes({
|
|
393
|
+
paths: trimmedArgs.paths,
|
|
394
|
+
includeContent: trimmedArgs.includeContent,
|
|
395
|
+
includeFrontmatter: trimmedArgs.includeFrontmatter
|
|
396
|
+
});
|
|
397
|
+
return {
|
|
398
|
+
content: [
|
|
399
|
+
{
|
|
400
|
+
type: "text",
|
|
401
|
+
text: JSON.stringify({
|
|
402
|
+
successful: result.successful,
|
|
403
|
+
failed: result.failed,
|
|
404
|
+
summary: {
|
|
405
|
+
successCount: result.successful.length,
|
|
406
|
+
failureCount: result.failed.length
|
|
407
|
+
}
|
|
408
|
+
}, null, 2)
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
case "update_frontmatter": {
|
|
414
|
+
await fileSystem.updateFrontmatter({
|
|
415
|
+
path: trimmedArgs.path,
|
|
416
|
+
frontmatter: trimmedArgs.frontmatter,
|
|
417
|
+
merge: trimmedArgs.merge
|
|
418
|
+
});
|
|
419
|
+
return {
|
|
420
|
+
content: [
|
|
421
|
+
{
|
|
422
|
+
type: "text",
|
|
423
|
+
text: `Successfully updated frontmatter for: ${trimmedArgs.path}`
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
case "get_notes_info": {
|
|
429
|
+
const result = await fileSystem.getNotesInfo(trimmedArgs.paths);
|
|
430
|
+
return {
|
|
431
|
+
content: [
|
|
432
|
+
{
|
|
433
|
+
type: "text",
|
|
434
|
+
text: JSON.stringify({
|
|
435
|
+
notes: result,
|
|
436
|
+
count: result.length
|
|
437
|
+
}, null, 2)
|
|
438
|
+
}
|
|
439
|
+
]
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
case "get_frontmatter": {
|
|
443
|
+
const note = await fileSystem.readNote(trimmedArgs.path);
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "text",
|
|
448
|
+
text: JSON.stringify({
|
|
449
|
+
path: trimmedArgs.path,
|
|
450
|
+
frontmatter: note.frontmatter
|
|
451
|
+
}, null, 2)
|
|
452
|
+
}
|
|
453
|
+
]
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
case "manage_tags": {
|
|
457
|
+
const result = await fileSystem.manageTags({
|
|
458
|
+
path: trimmedArgs.path,
|
|
459
|
+
operation: trimmedArgs.operation,
|
|
460
|
+
tags: trimmedArgs.tags
|
|
461
|
+
});
|
|
462
|
+
return {
|
|
463
|
+
content: [
|
|
464
|
+
{
|
|
465
|
+
type: "text",
|
|
466
|
+
text: JSON.stringify(result, null, 2)
|
|
467
|
+
}
|
|
468
|
+
],
|
|
469
|
+
isError: !result.success
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
default:
|
|
473
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
return {
|
|
478
|
+
content: [
|
|
479
|
+
{
|
|
480
|
+
type: "text",
|
|
481
|
+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
482
|
+
}
|
|
483
|
+
],
|
|
484
|
+
isError: true
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
const transport = new StdioServerTransport();
|
|
489
|
+
await server.connect(transport);
|