@bitbonsai/mcpvault 0.10.0 → 0.11.3

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 CHANGED
@@ -122,7 +122,7 @@ MCP is an open protocol. You're not tied to any specific vendor or platform. You
122
122
 
123
123
  ## Features
124
124
 
125
- - ✅ Safe frontmatter parsing and validation using gray-matter
125
+ - ✅ Safe frontmatter parsing and validation using gray-matter with AST-aware updates that preserve raw formatting for unmodified fields
126
126
  - ✅ Path filtering to exclude `.obsidian` directory and other system files
127
127
  - ✅ **Complete MCP toolkit**: 14 methods covering all vault operations
128
128
  - File operations: `read_note`, `write_note`, `patch_note`, `delete_note`, `move_note`, `move_file`
@@ -595,7 +595,8 @@ Delete a note from the vault (requires confirmation for safety).
595
595
  "name": "delete_note",
596
596
  "arguments": {
597
597
  "path": "old-draft.md",
598
- "confirmPath": "old-draft.md"
598
+ "confirmPath": "old-draft.md",
599
+ "trashMode": "local"
599
600
  }
600
601
  }
601
602
  ```
@@ -606,10 +607,15 @@ Delete a note from the vault (requires confirmation for safety).
606
607
  {
607
608
  "success": true,
608
609
  "path": "old-draft.md",
609
- "message": "Successfully deleted note: old-draft.md. This action cannot be undone."
610
+ "message": "Successfully moved note to vault trash: old-draft.md"
610
611
  }
611
612
  ```
612
613
 
614
+ **Trash modes:**
615
+ - `none` (default): permanent delete
616
+ - `local`: move to `.trash` inside the vault, preserving folder structure
617
+ - `system`: move to the OS trash/recycle bin
618
+
613
619
  **Response (Confirmation Failed):**
614
620
 
615
621
  ```json
@@ -1 +1 @@
1
- {"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAMnE,OAAO,EAAE,kBAAkB,EAAoB,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,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;CACzC;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA4XzF"}
1
+ {"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAMnE,OAAO,EAAE,kBAAkB,EAAoB,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,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;CACzC;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAgZzF"}
@@ -69,12 +69,13 @@ export function createServer(vaultPath, options = {}) {
69
69
  },
70
70
  {
71
71
  name: "delete_note",
72
- description: "Delete a note from the Obsidian vault (requires confirmation)",
72
+ description: "Delete a note from the Obsidian vault (requires confirmation). Supports permanent delete, vault trash, or system trash.",
73
73
  inputSchema: {
74
74
  type: "object",
75
75
  properties: {
76
76
  path: { type: "string", description: "Path to the note relative to vault root" },
77
- confirmPath: { type: "string", description: "Confirmation: must exactly match the path parameter to proceed with deletion" }
77
+ confirmPath: { type: "string", description: "Confirmation: must exactly match the path parameter to proceed with deletion" },
78
+ trashMode: { type: "string", enum: ["none", "local", "system"], description: "Deletion mode: 'none' = permanent delete (default), 'local' = move to .trash inside vault, 'system' = move to OS trash", default: "none" }
78
79
  },
79
80
  required: ["path", "confirmPath"]
80
81
  }
@@ -197,6 +198,16 @@ export function createServer(vaultPath, options = {}) {
197
198
  prettyPrint: { type: "boolean", description: "Format JSON response with indentation (default: false)", default: false }
198
199
  }
199
200
  }
201
+ },
202
+ {
203
+ name: "list_all_tags",
204
+ description: "List all tags across the vault with occurrence counts. Returns both frontmatter tags and inline #hashtags, deduplicated and sorted by frequency. Useful for discovering existing tags before creating or organizing notes.",
205
+ inputSchema: {
206
+ type: "object",
207
+ properties: {
208
+ prettyPrint: { type: "boolean", description: "Format JSON response with indentation (default: false)", default: false }
209
+ }
210
+ }
200
211
  }
201
212
  ]
202
213
  };
@@ -247,7 +258,8 @@ export function createServer(vaultPath, options = {}) {
247
258
  case "delete_note": {
248
259
  const result = await fileSystem.deleteNote({
249
260
  path: trimmedArgs.path,
250
- confirmPath: trimmedArgs.confirmPath
261
+ confirmPath: trimmedArgs.confirmPath,
262
+ trashMode: trimmedArgs.trashMode
251
263
  });
252
264
  return {
253
265
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
@@ -349,6 +361,13 @@ export function createServer(vaultPath, options = {}) {
349
361
  content: [{ type: "text", text: JSON.stringify({ notes: stats.totalNotes, folders: stats.totalFolders, size: stats.totalSize, recent: stats.recentlyModified }, null, indent) }]
350
362
  };
351
363
  }
364
+ case "list_all_tags": {
365
+ const tags = await fileSystem.listAllTags();
366
+ const indent = trimmedArgs.prettyPrint ? 2 : undefined;
367
+ return {
368
+ content: [{ type: "text", text: JSON.stringify(tags, null, indent) }]
369
+ };
370
+ }
352
371
  default:
353
372
  throw new Error(`Unknown tool: ${toolName}`);
354
373
  }
@@ -22,5 +22,9 @@ export declare class FileSystemService {
22
22
  manageTags(params: TagManagementParams): Promise<TagManagementResult>;
23
23
  getVaultPath(): string;
24
24
  getVaultStats(recentCount?: number): Promise<VaultStats>;
25
+ listAllTags(): Promise<Array<{
26
+ tag: string;
27
+ count: number;
28
+ }>>;
25
29
  }
26
30
  //# sourceMappingURL=filesystem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzT,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,SAAS;IAJnB,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,UAAU,CAAa;gBAGrB,SAAS,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,UAAU,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAazC,OAAO,CAAC,WAAW;IA6Db,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgC3C,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA6EjD,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IA2F5D,aAAa,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8D3D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAetC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAe3C,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAmE3D,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAoFrD,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA6HrD,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAgDpE,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BjE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2ClD,UAAU,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA0F3E,YAAY,IAAI,MAAM;IAIhB,aAAa,CAAC,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,UAAU,CAAC;CAwDlE"}
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzT,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,SAAS;IAJnB,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,UAAU,CAAa;gBAGrB,SAAS,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,UAAU,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAazC,OAAO,CAAC,WAAW;IA6Db,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgC3C,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAmFjD,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IA2F5D,aAAa,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8D3D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAetC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAe3C,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAyG3D,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAoFrD,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA6HrD,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAgDpE,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCjE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2ClD,UAAU,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsG3E,YAAY,IAAI,MAAM;IAIhB,aAAa,CAAC,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAyD3D,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAkDpE"}
@@ -1,6 +1,7 @@
1
1
  import { join, resolve, relative, dirname } from 'path';
2
2
  import { readdir, stat, readFile, writeFile, unlink, mkdir, access, rename, copyFile } from 'node:fs/promises';
3
3
  import { constants, realpathSync } from 'node:fs';
4
+ import trash from 'trash';
4
5
  import { FrontmatterHandler } from './frontmatter.js';
5
6
  import { PathFilter } from './pathfilter.js';
6
7
  import { generateObsidianUri } from './uri.js';
@@ -152,11 +153,15 @@ export class FileSystemService {
152
153
  const mergedFrontmatter = frontmatter
153
154
  ? { ...existingNote.frontmatter, ...frontmatter }
154
155
  : existingNote.frontmatter;
155
- if (mode === 'append') {
156
- finalContent = this.frontmatterHandler.stringify(mergedFrontmatter, existingNote.content + content);
156
+ const mergedContent = mode === 'append'
157
+ ? existingNote.content + content
158
+ : content + existingNote.content;
159
+ if (existingNote.matter && existingNote.matter.trim() !== '') {
160
+ // Preserve raw formatting for unmodified fields by only applying explicit updates
161
+ finalContent = this.frontmatterHandler.preserveStringify(existingNote.matter, frontmatter || {}, mergedContent);
157
162
  }
158
- else if (mode === 'prepend') {
159
- finalContent = this.frontmatterHandler.stringify(mergedFrontmatter, content + existingNote.content);
163
+ else {
164
+ finalContent = this.frontmatterHandler.stringify(mergedFrontmatter, mergedContent);
160
165
  }
161
166
  }
162
167
  }
@@ -193,11 +198,11 @@ export class FileSystemService {
193
198
  message: 'oldString cannot be empty'
194
199
  };
195
200
  }
196
- if (!newString) {
201
+ if (newString === undefined || newString === null) {
197
202
  return {
198
203
  success: false,
199
204
  path,
200
- message: 'newString cannot be empty'
205
+ message: 'newString is required'
201
206
  };
202
207
  }
203
208
  // Validate that oldString and newString are different
@@ -342,7 +347,7 @@ export class FileSystemService {
342
347
  }
343
348
  }
344
349
  async deleteNote(params) {
345
- const { path, confirmPath } = params;
350
+ const { path, confirmPath, trashMode = 'none' } = params;
346
351
  // Confirmation check - paths must match exactly
347
352
  if (path !== confirmPath) {
348
353
  return {
@@ -369,6 +374,39 @@ export class FileSystemService {
369
374
  message: `Cannot delete: ${path} is not a file`
370
375
  };
371
376
  }
377
+ if (trashMode === 'local') {
378
+ const trashDir = join(this.vaultPath, '.trash');
379
+ const trashPath = join(trashDir, path);
380
+ // Ensure trash directory exists
381
+ await mkdir(dirname(trashPath), { recursive: true });
382
+ // Handle collisions by appending a timestamp
383
+ let finalTrashPath = trashPath;
384
+ try {
385
+ await access(finalTrashPath, constants.F_OK);
386
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
387
+ const ext = path.endsWith('.md') ? '.md' : '';
388
+ const base = ext ? path.slice(0, -ext.length) : path;
389
+ const collidedPath = `${base}-${timestamp}${ext}`;
390
+ finalTrashPath = join(trashDir, collidedPath);
391
+ }
392
+ catch {
393
+ // File does not exist in trash, no collision
394
+ }
395
+ await rename(fullPath, finalTrashPath);
396
+ return {
397
+ success: true,
398
+ path: path,
399
+ message: `Successfully moved note to vault trash: ${path}`
400
+ };
401
+ }
402
+ if (trashMode === 'system') {
403
+ await trash(fullPath);
404
+ return {
405
+ success: true,
406
+ path: path,
407
+ message: `Successfully moved note to system trash: ${path}`
408
+ };
409
+ }
372
410
  // Perform the deletion using Node.js native API
373
411
  await unlink(fullPath);
374
412
  return {
@@ -652,12 +690,20 @@ export class FileSystemService {
652
690
  if (!validation.isValid) {
653
691
  throw new Error(`Invalid frontmatter: ${validation.errors.join(', ')}`);
654
692
  }
655
- // Update the note with new frontmatter, preserving content
656
- await this.writeNote({
657
- path,
658
- content: note.content,
659
- frontmatter: newFrontmatter
660
- });
693
+ const fullPath = this.resolvePath(path);
694
+ if (merge && note.matter && note.matter.trim() !== '') {
695
+ // Preserve raw formatting for unmodified fields
696
+ const updatedContent = this.frontmatterHandler.preserveStringify(note.matter, frontmatter, note.content);
697
+ await writeFile(fullPath, updatedContent, 'utf-8');
698
+ }
699
+ else {
700
+ // Replace frontmatter entirely (or no existing matter to preserve)
701
+ await this.writeNote({
702
+ path,
703
+ content: note.content,
704
+ frontmatter: newFrontmatter
705
+ });
706
+ }
661
707
  }
662
708
  async getNotesInfo(paths) {
663
709
  const results = await Promise.allSettled(paths.map(async (path) => {
@@ -740,23 +786,31 @@ export class FileSystemService {
740
786
  else if (operation === 'remove') {
741
787
  newTags = newTags.filter(tag => !tags.includes(tag));
742
788
  }
743
- // Update frontmatter with new tags
744
- const updatedFrontmatter = {
745
- ...note.frontmatter
746
- };
789
+ // Build tag updates for preserveStringify
790
+ const tagUpdates = {};
747
791
  if (newTags.length > 0) {
748
- updatedFrontmatter.tags = newTags;
792
+ tagUpdates.tags = newTags;
749
793
  }
750
- else if ('tags' in updatedFrontmatter) {
751
- delete updatedFrontmatter.tags;
794
+ else {
795
+ tagUpdates.tags = undefined;
752
796
  }
753
- // Write back the note with updated frontmatter
754
- await this.writeNote({
755
- path,
756
- content: note.content,
757
- frontmatter: updatedFrontmatter,
758
- mode: 'overwrite'
759
- });
797
+ // Write back the note with updated frontmatter, preserving raw formatting for unmodified fields
798
+ let updatedContent;
799
+ if (note.matter && note.matter.trim() !== '') {
800
+ updatedContent = this.frontmatterHandler.preserveStringify(note.matter, tagUpdates, note.content);
801
+ }
802
+ else {
803
+ const updatedFrontmatter = { ...note.frontmatter };
804
+ if (newTags.length > 0) {
805
+ updatedFrontmatter.tags = newTags;
806
+ }
807
+ else {
808
+ delete updatedFrontmatter.tags;
809
+ }
810
+ updatedContent = this.frontmatterHandler.stringify(updatedFrontmatter, note.content);
811
+ }
812
+ const fullPath = this.resolvePath(path);
813
+ await writeFile(fullPath, updatedContent, 'utf-8');
760
814
  return {
761
815
  path,
762
816
  operation,
@@ -828,4 +882,49 @@ export class FileSystemService {
828
882
  recentlyModified: recentFiles
829
883
  };
830
884
  }
885
+ async listAllTags() {
886
+ const tagCounts = new Map();
887
+ const inlineTagRegex = /(?:^|\s)#([a-zA-Z][a-zA-Z0-9_/\-]*)/g;
888
+ const scanDirectory = async (dirPath, relativePath = '') => {
889
+ const entries = await readdir(dirPath, { withFileTypes: true });
890
+ for (const entry of entries) {
891
+ const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
892
+ const fullEntryPath = join(dirPath, entry.name);
893
+ if (entry.isDirectory()) {
894
+ if (!this.pathFilter.isAllowedForListing(entryRelativePath))
895
+ continue;
896
+ await scanDirectory(fullEntryPath, entryRelativePath);
897
+ }
898
+ else if (entry.isFile() && this.pathFilter.isAllowed(entryRelativePath)) {
899
+ try {
900
+ const content = await readFile(fullEntryPath, 'utf-8');
901
+ const parsed = this.frontmatterHandler.parse(content);
902
+ // Frontmatter tags
903
+ const fmTags = parsed.frontmatter?.tags;
904
+ if (Array.isArray(fmTags)) {
905
+ for (const tag of fmTags) {
906
+ if (typeof tag === 'string' && tag.trim()) {
907
+ const normalized = tag.trim().toLowerCase();
908
+ tagCounts.set(normalized, (tagCounts.get(normalized) || 0) + 1);
909
+ }
910
+ }
911
+ }
912
+ // Inline #tags from body content
913
+ let match;
914
+ while ((match = inlineTagRegex.exec(parsed.content)) !== null) {
915
+ const normalized = match[1].toLowerCase();
916
+ tagCounts.set(normalized, (tagCounts.get(normalized) || 0) + 1);
917
+ }
918
+ }
919
+ catch {
920
+ // Skip files that can't be read
921
+ }
922
+ }
923
+ }
924
+ };
925
+ await scanDirectory(this.vaultPath);
926
+ return Array.from(tagCounts.entries())
927
+ .map(([tag, count]) => ({ tag, count }))
928
+ .sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
929
+ }
831
930
  }
@@ -10,6 +10,7 @@ export declare class FrontmatterHandler {
10
10
  stringify(frontmatterData: Record<string, any>, content: string): string;
11
11
  validate(frontmatterData: Record<string, any>): FrontmatterValidationResult;
12
12
  private checkForProblematicValues;
13
+ preserveStringify(rawMatter: string, updates: Record<string, any>, content: string): string;
13
14
  extractFrontmatter(content: string): Record<string, any>;
14
15
  updateFrontmatter(content: string, updates: Record<string, any>): string;
15
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/frontmatter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAmB5E;AAED,qBAAa,kBAAkB;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAkBlC,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAaxE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,2BAA2B;IAqB3E,OAAO,CAAC,yBAAyB;IAmDjC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAKxD,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;CAWzE"}
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/frontmatter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAmB5E;AAED,qBAAa,kBAAkB;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAoBlC,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAaxE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,2BAA2B;IAqB3E,OAAO,CAAC,yBAAyB;IAmDjC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAyB3F,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAKxD,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;CAWzE"}
@@ -1,4 +1,5 @@
1
1
  import matter from 'gray-matter';
2
+ import { parseDocument } from 'yaml';
2
3
  /**
3
4
  * Parse a frontmatter value that may be a JSON string (LLM clients sometimes
4
5
  * pass frontmatter as a serialized JSON string instead of an object).
@@ -32,7 +33,8 @@ export class FrontmatterHandler {
32
33
  return {
33
34
  frontmatter: parsed.data,
34
35
  content: parsed.content,
35
- originalContent: content
36
+ originalContent: content,
37
+ matter: parsed.matter
36
38
  };
37
39
  }
38
40
  catch (error) {
@@ -40,7 +42,8 @@ export class FrontmatterHandler {
40
42
  return {
41
43
  frontmatter: {},
42
44
  content: content,
43
- originalContent: content
45
+ originalContent: content,
46
+ matter: ''
44
47
  };
45
48
  }
46
49
  }
@@ -113,6 +116,31 @@ export class FrontmatterHandler {
113
116
  }
114
117
  }
115
118
  }
119
+ preserveStringify(rawMatter, updates, content) {
120
+ try {
121
+ if (!rawMatter || rawMatter.trim() === '') {
122
+ // No existing frontmatter to preserve - fall back to regular stringify
123
+ if (!updates || Object.keys(updates).length === 0) {
124
+ return content;
125
+ }
126
+ return matter.stringify(content, updates);
127
+ }
128
+ const doc = parseDocument(rawMatter.trimStart());
129
+ for (const [key, value] of Object.entries(updates)) {
130
+ if (value === undefined) {
131
+ doc.delete(key);
132
+ }
133
+ else {
134
+ doc.set(key, value);
135
+ }
136
+ }
137
+ const yamlContent = doc.toString();
138
+ return `---\n${yamlContent}---\n${content}`;
139
+ }
140
+ catch (error) {
141
+ throw new Error(`Failed to stringify frontmatter: ${error instanceof Error ? error.message : 'Unknown error'}`);
142
+ }
143
+ }
116
144
  extractFrontmatter(content) {
117
145
  const parsed = this.parse(content);
118
146
  return parsed.frontmatter;
@@ -124,6 +152,6 @@ export class FrontmatterHandler {
124
152
  if (!validation.isValid) {
125
153
  throw new Error(`Invalid frontmatter: ${validation.errors.join(', ')}`);
126
154
  }
127
- return this.stringify(updatedFrontmatter, parsed.content);
155
+ return this.preserveStringify(parsed.matter || '', updates, parsed.content);
128
156
  }
129
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pathfilter.d.ts","sourceRoot":"","sources":["../../src/pathfilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,UAAU;IACrB,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,iBAAiB,CAAW;gBAExB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAuB9C,OAAO,CAAC,eAAe;IAkBvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAqBhC,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAQ1C,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,MAAM;IA0Bd,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE;CAGvC"}
1
+ {"version":3,"file":"pathfilter.d.ts","sourceRoot":"","sources":["../../src/pathfilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,UAAU;IACrB,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,iBAAiB,CAAW;gBAExB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAuB9C,OAAO,CAAC,eAAe;IAkBvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAyBhC,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAY1C,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,MAAM;IA0Bd,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE;CAGvC"}
@@ -37,6 +37,9 @@ export class PathFilter {
37
37
  return regex.test(path);
38
38
  }
39
39
  isAllowed(path) {
40
+ if (typeof path !== "string") {
41
+ throw new Error("path is required and must be a string");
42
+ }
40
43
  // Normalize path separators
41
44
  const normalizedPath = path.replace(/\\/g, '/');
42
45
  if (this.isIgnoredPath(normalizedPath)) {
@@ -52,6 +55,9 @@ export class PathFilter {
52
55
  return true;
53
56
  }
54
57
  isAllowedForListing(path) {
58
+ if (typeof path !== "string") {
59
+ throw new Error("path is required and must be a string");
60
+ }
55
61
  // Normalize path separators
56
62
  const normalizedPath = path.replace(/\\/g, '/');
57
63
  // Listing includes non-note files, but still blocks restricted system paths
@@ -2,6 +2,7 @@ export interface ParsedNote {
2
2
  frontmatter: Record<string, any>;
3
3
  content: string;
4
4
  originalContent: string;
5
+ matter?: string;
5
6
  }
6
7
  export interface NoteWriteParams {
7
8
  path: string;
@@ -24,6 +25,7 @@ export interface PatchNoteResult {
24
25
  export interface DeleteNoteParams {
25
26
  path: string;
26
27
  confirmPath: string;
28
+ trashMode?: 'none' | 'local' | 'system';
27
29
  }
28
30
  export interface DeleteResult {
29
31
  success: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitbonsai/mcpvault",
3
- "version": "0.10.0",
3
+ "version": "0.11.3",
4
4
  "description": "Universal AI bridge for Obsidian vaults - connect any MCP-compatible assistant",
5
5
  "homepage": "https://mcpvault.org",
6
6
  "author": "bitbonsai",
@@ -37,12 +37,14 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.20.0",
40
- "gray-matter": "^4.0.3"
40
+ "gray-matter": "^4.0.3",
41
+ "trash": "^10.1.1",
42
+ "yaml": "^2.8.3"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@types/node": "^25.3.3",
44
46
  "tsx": "^4.20.6",
45
- "typescript": "^5.9.3",
47
+ "typescript": "^6.0.2",
46
48
  "vitest": "^4.0.15"
47
49
  },
48
50
  "engines": {