@bitbonsai/mcpvault 0.16.0 → 0.17.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 +63 -62
- package/dist/server.js +6 -3
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +19 -0
- package/dist/src/createServer.d.ts +2 -0
- package/dist/src/createServer.d.ts.map +1 -1
- package/dist/src/createServer.js +36 -5
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://mcptoplist.com/server/glama%2Fbitbonsai%2Fmcpvault)
|
|
6
6
|
|
|
7
|
-
A
|
|
7
|
+
A local MCP server that lets compatible clients read, search, and edit notes in an Obsidian vault. MCPVault works directly with vault files, restricts file operations to the configured vault root, and preserves formatting for unchanged frontmatter fields.
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
|
|
@@ -20,18 +20,18 @@ A universal AI bridge for Obsidian vaults using the Model Context Protocol (MCP)
|
|
|
20
20
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Supported clients
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Configuration examples are available for Claude Desktop, Claude Code, ChatGPT Desktop (Enterprise+), OpenCode, Gemini CLI, OpenAI Codex, IntelliJ IDEA 2025.1+, Cursor, Windsurf, and Ontheia. Other clients can use MCPVault if they support local stdio MCP servers.
|
|
26
26
|
|
|
27
27
|
https://github.com/user-attachments/assets/657ac4c6-1cd2-4cc3-829f-fd095a32f71c
|
|
28
28
|
|
|
29
|
-
## Quick
|
|
29
|
+
## Quick start
|
|
30
30
|
|
|
31
31
|
1. **Install Node.js runtime:**
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
# Download from https://nodejs.org (
|
|
34
|
+
# Download from https://nodejs.org (v20.0.0 or later)
|
|
35
35
|
# or use a package manager like nvm, brew, apt, etc.
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -99,57 +99,41 @@ https://github.com/user-attachments/assets/657ac4c6-1cd2-4cc3-829f-fd095a32f71c
|
|
|
99
99
|
- "Read my note called 'project-ideas.md'"
|
|
100
100
|
- "Create a new note with today's date"
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
To verify the connection, ask your client to list MCPVault tools or read a known note.
|
|
103
103
|
|
|
104
|
-
##
|
|
104
|
+
## How it connects
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Built on the open Model Context Protocol standard, MCPVault is not locked to any single AI provider. As more AI assistants adopt MCP, your investment in this tool grows more valuable. Today it works with Claude and ChatGPT - tomorrow it will work with whatever AI tools emerge.
|
|
109
|
-
|
|
110
|
-
### Future-Proof Your Knowledge Base
|
|
111
|
-
|
|
112
|
-
Instead of waiting for each AI company to build Obsidian integrations, MCPVault provides a universal adapter that works with any MCP-compatible assistant. One tool, endless possibilities.
|
|
113
|
-
|
|
114
|
-
### Open Standard, No Lock-in
|
|
115
|
-
|
|
116
|
-
MCP is an open protocol. You're not tied to any specific vendor or platform. Your notes remain yours, accessible through any compatible AI assistant.
|
|
106
|
+
An MCP client starts MCPVault as a local stdio process and passes the vault path. MCPVault exposes the same tools to each supported client, so the server is not tied to one AI provider. Obsidian does not need to be running, and no Obsidian plugin is required.
|
|
117
107
|
|
|
118
108
|
## Features
|
|
119
109
|
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
110
|
+
- AST-aware frontmatter updates preserve formatting for unchanged YAML fields.
|
|
111
|
+
- Path checks block traversal, symlink escapes, dotfiles, `.obsidian`, `.git`, and `node_modules`.
|
|
112
|
+
- Eighteen MCP tools cover note and file operations:
|
|
123
113
|
- File operations: `read_note`, `write_note`, `patch_note`, `delete_note`, `move_note`, `move_file`
|
|
124
|
-
- Partial reads: `get_note_outline
|
|
125
|
-
- Directory
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
- ✅ TypeScript support with Node.js runtime (using tsx for execution)
|
|
136
|
-
- ✅ Comprehensive error handling and validation
|
|
137
|
-
- ✅ **Token-optimized responses**: 40-60% smaller responses with minified field names and compact JSON (v0.6.3+)
|
|
138
|
-
- ✅ **Optional pretty-printing**: Set `prettyPrint: true` for human-readable debugging
|
|
139
|
-
- ✅ **Performance optimized**: No unnecessary token consumption, efficient for large vaults
|
|
140
|
-
- ✅ **Zero dependencies**: No Obsidian plugins required, works with any vault structure
|
|
114
|
+
- Partial reads: `get_note_outline`, `read_note_lines`
|
|
115
|
+
- Directory and batch reads: `list_directory`, `read_multiple_notes`
|
|
116
|
+
- Search: `search_notes` with multi-word matching and BM25 reranking
|
|
117
|
+
- Metadata and tags: `get_frontmatter`, `update_frontmatter`, `get_notes_info`, `get_vault_stats`, `manage_tags`, `list_all_tags`
|
|
118
|
+
- Wiki links: `wiki_link` resolves names and returns alternative paths when a name is ambiguous
|
|
119
|
+
- `write_note` supports overwrite, append, and prepend modes.
|
|
120
|
+
- `delete_note` and `move_file` require matching confirmation paths.
|
|
121
|
+
- Path arguments are trimmed before validation.
|
|
122
|
+
- Search and batch tools return compact fields by default; set `prettyPrint: true` for expanded output.
|
|
123
|
+
- The package exports TypeScript declarations and public types.
|
|
124
|
+
- MCPVault requires no Obsidian plugin.
|
|
141
125
|
|
|
142
126
|
## Prerequisites
|
|
143
127
|
|
|
144
|
-
- [Node.js](https://nodejs.org) runtime (
|
|
128
|
+
- [Node.js](https://nodejs.org) runtime (v20.0.0 or later)
|
|
145
129
|
- An Obsidian vault (local directory with `.md`, `.markdown`, `.txt`, `.base`, or `.canvas` files)
|
|
146
130
|
- MCP-compatible AI client (Claude Desktop, ChatGPT Desktop, Claude Code, etc.)
|
|
147
131
|
|
|
148
132
|
## Installation
|
|
149
133
|
|
|
150
|
-
### For
|
|
134
|
+
### For end users
|
|
151
135
|
|
|
152
|
-
|
|
136
|
+
`npx` downloads and runs the package:
|
|
153
137
|
|
|
154
138
|
```bash
|
|
155
139
|
npx @bitbonsai/mcpvault@latest /path/to/your/obsidian/vault
|
|
@@ -157,7 +141,7 @@ npx @bitbonsai/mcpvault@latest /path/to/your/obsidian/vault
|
|
|
157
141
|
|
|
158
142
|
If you omit the vault path, the server uses your current working directory as the vault root.
|
|
159
143
|
|
|
160
|
-
### For
|
|
144
|
+
### For developers
|
|
161
145
|
|
|
162
146
|
1. Clone this repository
|
|
163
147
|
2. Use the correct Node.js version:
|
|
@@ -178,7 +162,7 @@ npm install # Corepack automatically uses npm 10.9.0
|
|
|
178
162
|
npx @modelcontextprotocol/inspector npm start /path/to/your/vault
|
|
179
163
|
```
|
|
180
164
|
|
|
181
|
-
|
|
165
|
+
Use MCP Inspector to test the server before adding it to a client:
|
|
182
166
|
|
|
183
167
|
```bash
|
|
184
168
|
# Install globally for easier access
|
|
@@ -274,6 +258,21 @@ Add `--read-only` after the vault path to expose only read tools. Mutating tools
|
|
|
274
258
|
|
|
275
259
|
The CLI also accepts `--read-only true` and `--read-only=true` for configuration systems that require explicit values. Omit the option, or set it to `false`, to keep normal read/write access.
|
|
276
260
|
|
|
261
|
+
**Default search and listing exclusions:**
|
|
262
|
+
|
|
263
|
+
Repeat `--exclude <subtree>` to hide vault-relative folders from `search_notes` and `list_directory` in every session. Per-call `excludePaths` add to these defaults. Direct read and write tools remain available for excluded paths.
|
|
264
|
+
|
|
265
|
+
```json
|
|
266
|
+
"args": [
|
|
267
|
+
"@bitbonsai/mcpvault@latest",
|
|
268
|
+
"/Users/yourname/Documents/ResearchVault",
|
|
269
|
+
"--exclude", "Archive",
|
|
270
|
+
"--exclude", "Snapshots/Old"
|
|
271
|
+
]
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
MCPVault does not read Obsidian's `.obsidian/app.json`; exclusions stay explicit in the MCP server configuration.
|
|
275
|
+
|
|
277
276
|
**Configuration File Locations:**
|
|
278
277
|
|
|
279
278
|
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
@@ -375,12 +374,12 @@ Most modern MCP clients use similar JSON configuration patterns. Refer to your s
|
|
|
375
374
|
- "List all markdown files in my 'Projects' folder"
|
|
376
375
|
- "Delete the old draft note 'draft-ideas.md' (with confirmation)"
|
|
377
376
|
|
|
378
|
-
####
|
|
377
|
+
#### Example workflows
|
|
379
378
|
|
|
380
|
-
-
|
|
381
|
-
-
|
|
382
|
-
-
|
|
383
|
-
-
|
|
379
|
+
- "Summarize my research notes tagged with 'machine-learning' from the last month"
|
|
380
|
+
- "Update the status in my project notes to 'completed' and add today's date"
|
|
381
|
+
- "Find notes that mention 'API design' and draft a guide from them"
|
|
382
|
+
- "Review my untagged notes and suggest tags based on their content"
|
|
384
383
|
|
|
385
384
|
## Troubleshooting
|
|
386
385
|
|
|
@@ -459,7 +458,7 @@ Read a note from the vault with parsed frontmatter.
|
|
|
459
458
|
}
|
|
460
459
|
```
|
|
461
460
|
|
|
462
|
-
**
|
|
461
|
+
**Compact response:**
|
|
463
462
|
|
|
464
463
|
```json
|
|
465
464
|
{
|
|
@@ -536,7 +535,7 @@ Write a note to the vault with optional frontmatter and write mode.
|
|
|
536
535
|
|
|
537
536
|
### `patch_note`
|
|
538
537
|
|
|
539
|
-
|
|
538
|
+
Replace an exact string inside an existing note without rewriting the full file.
|
|
540
539
|
|
|
541
540
|
**Request:**
|
|
542
541
|
|
|
@@ -592,7 +591,7 @@ Note: this includes non-note filenames (for example `pdf`, `png`, `jpg`) so AI a
|
|
|
592
591
|
}
|
|
593
592
|
```
|
|
594
593
|
|
|
595
|
-
**
|
|
594
|
+
**Compact response:**
|
|
596
595
|
|
|
597
596
|
```json
|
|
598
597
|
{
|
|
@@ -643,7 +642,7 @@ Delete a note from the vault (requires confirmation for safety).
|
|
|
643
642
|
}
|
|
644
643
|
```
|
|
645
644
|
|
|
646
|
-
|
|
645
|
+
**Confirmation:** `confirmPath` must exactly match `path` before deletion proceeds.
|
|
647
646
|
|
|
648
647
|
### `get_frontmatter`
|
|
649
648
|
|
|
@@ -661,7 +660,7 @@ Extract only the frontmatter from a note without reading the full content.
|
|
|
661
660
|
}
|
|
662
661
|
```
|
|
663
662
|
|
|
664
|
-
**
|
|
663
|
+
**Compact response, returning frontmatter directly:**
|
|
665
664
|
|
|
666
665
|
```json
|
|
667
666
|
{
|
|
@@ -745,7 +744,7 @@ Search for notes in the vault by content or frontmatter with multi-word matching
|
|
|
745
744
|
}
|
|
746
745
|
```
|
|
747
746
|
|
|
748
|
-
**
|
|
747
|
+
**Compact response:**
|
|
749
748
|
|
|
750
749
|
```json
|
|
751
750
|
[
|
|
@@ -827,7 +826,7 @@ Move or rename any file in the vault with binary-safe file operations (file-only
|
|
|
827
826
|
}
|
|
828
827
|
```
|
|
829
828
|
|
|
830
|
-
**
|
|
829
|
+
**Confirmation:** `confirmOldPath` must match `oldPath`, and `confirmNewPath` must match `newPath`.
|
|
831
830
|
|
|
832
831
|
### `read_multiple_notes`
|
|
833
832
|
|
|
@@ -847,7 +846,7 @@ Read multiple notes in a batch (maximum 10 files).
|
|
|
847
846
|
}
|
|
848
847
|
```
|
|
849
848
|
|
|
850
|
-
**
|
|
849
|
+
**Compact response:**
|
|
851
850
|
|
|
852
851
|
```json
|
|
853
852
|
{
|
|
@@ -911,7 +910,7 @@ Get metadata for notes without reading full content.
|
|
|
911
910
|
}
|
|
912
911
|
```
|
|
913
912
|
|
|
914
|
-
**
|
|
913
|
+
**Compact response, returning an array directly:**
|
|
915
914
|
|
|
916
915
|
```json
|
|
917
916
|
[
|
|
@@ -940,7 +939,7 @@ Get high-level vault statistics without reading note contents.
|
|
|
940
939
|
}
|
|
941
940
|
```
|
|
942
941
|
|
|
943
|
-
**
|
|
942
|
+
**Compact response:**
|
|
944
943
|
|
|
945
944
|
```json
|
|
946
945
|
{
|
|
@@ -957,9 +956,9 @@ Get high-level vault statistics without reading note contents.
|
|
|
957
956
|
}
|
|
958
957
|
```
|
|
959
958
|
|
|
960
|
-
## Security
|
|
959
|
+
## Security boundaries
|
|
961
960
|
|
|
962
|
-
|
|
961
|
+
MCPVault applies these checks before file operations:
|
|
963
962
|
|
|
964
963
|
### Path Security
|
|
965
964
|
|
|
@@ -992,13 +991,15 @@ This MCP server implements several security measures to protect your Obsidian va
|
|
|
992
991
|
- **Vault Structure:** Directory structure is visible to AI assistants
|
|
993
992
|
- **File Metadata:** Creation times, file sizes, etc. are accessible
|
|
994
993
|
|
|
995
|
-
|
|
994
|
+
Only grant write access to clients and conversations you trust. Use `--read-only` when the client does not need to modify notes.
|
|
996
995
|
|
|
997
996
|
## Architecture
|
|
998
997
|
|
|
999
998
|
- `server.ts` - MCP server entry point
|
|
999
|
+
- `src/cli.ts` - CLI option parsing for vault path, read-only mode, and default exclusions
|
|
1000
|
+
- `src/createServer.ts` - MCP tool registration, request routing, and discovery defaults
|
|
1000
1001
|
- `src/frontmatter.ts` - YAML frontmatter handling with gray-matter
|
|
1001
|
-
- `src/filesystem.ts` -
|
|
1002
|
+
- `src/filesystem.ts` - File operations with path validation
|
|
1002
1003
|
- `src/pathfilter.ts` - Directory and file filtering
|
|
1003
1004
|
- `src/search.ts` - Note search functionality with content and frontmatter support
|
|
1004
1005
|
- `src/uri.ts` - Obsidian URI generation for deep links
|
package/dist/server.js
CHANGED
|
@@ -24,7 +24,7 @@ mcpvault v${VERSION}
|
|
|
24
24
|
Universal AI bridge for Obsidian vaults - connect any MCP-compatible assistant
|
|
25
25
|
|
|
26
26
|
Usage:
|
|
27
|
-
npx @bitbonsai/mcpvault [vault-path] [--read-only[=true|false]]
|
|
27
|
+
npx @bitbonsai/mcpvault [vault-path] [--read-only[=true|false]] [--exclude <subtree>]...
|
|
28
28
|
|
|
29
29
|
Arguments:
|
|
30
30
|
[vault-path] Optional path to your Obsidian vault directory
|
|
@@ -35,12 +35,15 @@ Options:
|
|
|
35
35
|
--help, -h Show this help message
|
|
36
36
|
--read-only Expose read tools only and reject all vault mutations
|
|
37
37
|
May be passed alone, with true/false, or as --read-only=true
|
|
38
|
+
--exclude PATH Hide a vault-relative subtree from search and directory listings
|
|
39
|
+
Repeat for multiple subtrees; direct read/write tools are unaffected
|
|
38
40
|
|
|
39
41
|
Examples:
|
|
40
42
|
npx @bitbonsai/mcpvault
|
|
41
43
|
npx @bitbonsai/mcpvault ~/Documents/MyVault
|
|
42
44
|
npx @bitbonsai/mcpvault ./Vault
|
|
43
45
|
npx @bitbonsai/mcpvault ./Vault --read-only
|
|
46
|
+
npx @bitbonsai/mcpvault ./Vault --exclude Archive --exclude "Snapshots/Old"
|
|
44
47
|
npx @bitbonsai/mcpvault /path/to/obsidian/vault
|
|
45
48
|
npx @bitbonsai/mcpvault "/path/with spaces/Obsidian Vault"
|
|
46
49
|
`);
|
|
@@ -48,11 +51,11 @@ Examples:
|
|
|
48
51
|
}
|
|
49
52
|
// Remove runtime options before joining trailing args, preserving support for
|
|
50
53
|
// unquoted vault paths with spaces. When omitted, use the current directory.
|
|
51
|
-
const { vaultPathArg, readOnly } = parseCliArgs(cliArgs);
|
|
54
|
+
const { vaultPathArg, readOnly, excludePaths } = parseCliArgs(cliArgs);
|
|
52
55
|
const vaultPath = resolve(vaultPathArg || process.cwd());
|
|
53
56
|
// Serve both the legacy handshake-based protocol and MCP 2026-07-28 from the
|
|
54
57
|
// same process. The opening exchange selects the era for this connection.
|
|
55
|
-
const serverHandle = serveStdio(() => createServer(vaultPath, { version: VERSION, readOnly }), { onerror: (error) => console.error(error) });
|
|
58
|
+
const serverHandle = serveStdio(() => createServer(vaultPath, { version: VERSION, readOnly, excludePaths }), { onerror: (error) => console.error(error) });
|
|
56
59
|
// Exit when the client disconnects (stdin EOF) or the process is asked to
|
|
57
60
|
// terminate. Hosts that don't send an MCP shutdown request otherwise leave
|
|
58
61
|
// this process running forever, orphaned once stdin closes (#159).
|
package/dist/src/cli.d.ts
CHANGED
package/dist/src/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D"}
|
package/dist/src/cli.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export function parseCliArgs(args) {
|
|
7
7
|
const pathArgs = [];
|
|
8
|
+
const excludePaths = [];
|
|
8
9
|
let readOnly = false;
|
|
9
10
|
for (let index = 0; index < args.length; index += 1) {
|
|
10
11
|
const arg = args[index];
|
|
@@ -27,10 +28,28 @@ export function parseCliArgs(args) {
|
|
|
27
28
|
readOnly = value === "true";
|
|
28
29
|
continue;
|
|
29
30
|
}
|
|
31
|
+
if (arg === "--exclude") {
|
|
32
|
+
const value = args[index + 1]?.trim();
|
|
33
|
+
if (!value || value.startsWith("--")) {
|
|
34
|
+
throw new Error("--exclude requires a subtree path");
|
|
35
|
+
}
|
|
36
|
+
excludePaths.push(value);
|
|
37
|
+
index += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (arg.startsWith("--exclude=")) {
|
|
41
|
+
const value = arg.slice("--exclude=".length).trim();
|
|
42
|
+
if (!value) {
|
|
43
|
+
throw new Error("--exclude requires a subtree path");
|
|
44
|
+
}
|
|
45
|
+
excludePaths.push(value);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
30
48
|
pathArgs.push(arg);
|
|
31
49
|
}
|
|
32
50
|
return {
|
|
33
51
|
vaultPathArg: pathArgs.join(" ").trim(),
|
|
34
52
|
readOnly,
|
|
53
|
+
excludePaths,
|
|
35
54
|
};
|
|
36
55
|
}
|
|
@@ -8,6 +8,8 @@ export interface CreateServerOptions {
|
|
|
8
8
|
frontmatterHandler?: FrontmatterHandler;
|
|
9
9
|
/** Expose read tools only and reject direct calls to mutating tools. */
|
|
10
10
|
readOnly?: boolean;
|
|
11
|
+
/** Vault-relative subtrees hidden from search and directory listings. */
|
|
12
|
+
excludePaths?: string[];
|
|
11
13
|
}
|
|
12
14
|
export declare function createServer(vaultPath: string, options?: CreateServerOptions): Server;
|
|
13
15
|
//# sourceMappingURL=createServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAoB,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAK7C,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAoB,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAK7C,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAoCD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAofzF"}
|
package/dist/src/createServer.js
CHANGED
|
@@ -4,7 +4,7 @@ import { FrontmatterHandler, parseFrontmatter } from "./frontmatter.js";
|
|
|
4
4
|
import { PathFilter } from "./pathfilter.js";
|
|
5
5
|
import { SearchService } from "./search.js";
|
|
6
6
|
import { handleWikiLinkTool } from "./wikilink/index.js";
|
|
7
|
-
import { resolve } from "path";
|
|
7
|
+
import { posix, resolve } from "path";
|
|
8
8
|
const MUTATING_TOOLS = new Set([
|
|
9
9
|
"write_note",
|
|
10
10
|
"patch_note",
|
|
@@ -14,8 +14,29 @@ const MUTATING_TOOLS = new Set([
|
|
|
14
14
|
"update_frontmatter",
|
|
15
15
|
"manage_tags",
|
|
16
16
|
]);
|
|
17
|
+
function normalizeSubtree(path) {
|
|
18
|
+
let relativePath = path.trim().replace(/\\/g, "/");
|
|
19
|
+
while (relativePath.startsWith("/"))
|
|
20
|
+
relativePath = relativePath.slice(1);
|
|
21
|
+
const normalizedPath = posix.normalize(relativePath);
|
|
22
|
+
if (normalizedPath === ".")
|
|
23
|
+
return "";
|
|
24
|
+
return normalizedPath.endsWith("/") ? normalizedPath.slice(0, -1) : normalizedPath;
|
|
25
|
+
}
|
|
26
|
+
function mergeExcludePaths(defaults, requested) {
|
|
27
|
+
const provided = Array.isArray(requested) ? requested : [];
|
|
28
|
+
return [...new Set([...defaults, ...provided]
|
|
29
|
+
.filter((path) => typeof path === "string")
|
|
30
|
+
.map(normalizeSubtree)
|
|
31
|
+
.filter(Boolean))];
|
|
32
|
+
}
|
|
33
|
+
function isExcluded(path, excludePaths) {
|
|
34
|
+
const normalizedPath = normalizeSubtree(path);
|
|
35
|
+
return excludePaths.some((excluded) => normalizedPath === excluded || normalizedPath.startsWith(`${excluded}/`));
|
|
36
|
+
}
|
|
17
37
|
export function createServer(vaultPath, options = {}) {
|
|
18
|
-
const { name = "mcpvault", version = "0.0.0", pathFilter = new PathFilter(), frontmatterHandler = new FrontmatterHandler(), readOnly = false, } = options;
|
|
38
|
+
const { name = "mcpvault", version = "0.0.0", pathFilter = new PathFilter(), frontmatterHandler = new FrontmatterHandler(), readOnly = false, excludePaths = [], } = options;
|
|
39
|
+
const defaultExcludePaths = mergeExcludePaths([], excludePaths);
|
|
19
40
|
const resolvedVaultPath = resolve(vaultPath);
|
|
20
41
|
const fileSystem = new FileSystemService(resolvedVaultPath, pathFilter, frontmatterHandler);
|
|
21
42
|
const searchService = new SearchService(resolvedVaultPath, pathFilter);
|
|
@@ -317,10 +338,17 @@ export function createServer(vaultPath, options = {}) {
|
|
|
317
338
|
};
|
|
318
339
|
}
|
|
319
340
|
case "list_directory": {
|
|
320
|
-
const
|
|
341
|
+
const requestedPath = normalizeSubtree(trimmedArgs.path || "");
|
|
342
|
+
if (isExcluded(requestedPath, defaultExcludePaths)) {
|
|
343
|
+
throw new Error(`Directory excluded by server configuration: ${requestedPath}`);
|
|
344
|
+
}
|
|
345
|
+
const listing = await fileSystem.listDirectory(trimmedArgs.path || "");
|
|
346
|
+
const prefix = requestedPath ? `${requestedPath}/` : "";
|
|
347
|
+
const directories = listing.directories.filter((name) => !isExcluded(`${prefix}${name}`, defaultExcludePaths));
|
|
348
|
+
const files = listing.files.filter((name) => !isExcluded(`${prefix}${name}`, defaultExcludePaths));
|
|
321
349
|
const indent = trimmedArgs.prettyPrint ? 2 : undefined;
|
|
322
350
|
return {
|
|
323
|
-
content: [{ type: "text", text: JSON.stringify({ dirs:
|
|
351
|
+
content: [{ type: "text", text: JSON.stringify({ dirs: directories, files }, null, indent) }]
|
|
324
352
|
};
|
|
325
353
|
}
|
|
326
354
|
case "delete_note": {
|
|
@@ -342,7 +370,7 @@ export function createServer(vaultPath, options = {}) {
|
|
|
342
370
|
searchFrontmatter: trimmedArgs.searchFrontmatter,
|
|
343
371
|
caseSensitive: trimmedArgs.caseSensitive,
|
|
344
372
|
pathPrefix: trimmedArgs.pathPrefix,
|
|
345
|
-
excludePaths: trimmedArgs.excludePaths
|
|
373
|
+
excludePaths: mergeExcludePaths(defaultExcludePaths, trimmedArgs.excludePaths)
|
|
346
374
|
});
|
|
347
375
|
const indent = trimmedArgs.prettyPrint ? 2 : undefined;
|
|
348
376
|
return {
|
|
@@ -487,5 +515,8 @@ function trimPaths(args) {
|
|
|
487
515
|
if (trimmed.paths && Array.isArray(trimmed.paths)) {
|
|
488
516
|
trimmed.paths = trimmed.paths.map((p) => typeof p === 'string' ? p.trim() : p);
|
|
489
517
|
}
|
|
518
|
+
if (trimmed.excludePaths && Array.isArray(trimmed.excludePaths)) {
|
|
519
|
+
trimmed.excludePaths = trimmed.excludePaths.map((p) => typeof p === 'string' ? p.trim() : p);
|
|
520
|
+
}
|
|
490
521
|
return trimmed;
|
|
491
522
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbonsai/mcpvault",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Universal AI bridge for Obsidian vaults - connect any MCP-compatible assistant",
|
|
5
5
|
"homepage": "https://mcpvault.org",
|
|
6
6
|
"author": "bitbonsai",
|
|
@@ -39,18 +39,18 @@
|
|
|
39
39
|
"ship:update": "bun scripts/ship.ts --update"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@modelcontextprotocol/server": "^2.
|
|
42
|
+
"@modelcontextprotocol/server": "^2.3.1",
|
|
43
43
|
"gray-matter": "^4.0.3",
|
|
44
|
-
"trash": "
|
|
44
|
+
"trash": "9.0.0",
|
|
45
45
|
"yaml": "^2.9.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@clack/prompts": "^
|
|
49
|
-
"@modelcontextprotocol/client": "^2.
|
|
48
|
+
"@clack/prompts": "^1.7.0",
|
|
49
|
+
"@modelcontextprotocol/client": "^2.3.1",
|
|
50
50
|
"@types/node": "^26.1.2",
|
|
51
51
|
"tsx": "^4.23.9",
|
|
52
52
|
"typescript": "^7.0.2",
|
|
53
|
-
"vitest": "^4.1.
|
|
53
|
+
"vitest": "^4.1.12"
|
|
54
54
|
},
|
|
55
55
|
"engines": {
|
|
56
56
|
"node": ">=20.0.0"
|