virlow-mcp 3.11.7 → 3.11.8

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.
Files changed (2) hide show
  1. package/dist/cli.js +30 -4
  2. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -435,9 +435,7 @@ var NotesService = class {
435
435
  const key = this.requireSession();
436
436
  const row = await this.api.getNote(id);
437
437
  const exposure = await this.exposure();
438
- if (!exposure.allows(row.folderId)) {
439
- throw new Error(`Note ${id} is in a folder that is not exposed to MCP. Turn on "Expose to MCP" in that folder's settings in the Virlow app to read it here.`);
440
- }
438
+ this.requireExposed(exposure, row.folderId, id);
441
439
  const { title, content } = await this.decryptRow(key, row);
442
440
  return { id: row.id, title, content, folderId: row.folderId };
443
441
  }
@@ -464,6 +462,8 @@ var NotesService = class {
464
462
  return this.enqueue(async () => {
465
463
  const key = this.requireSession();
466
464
  const existingRow = await this.api.getNote(id);
465
+ const exposure = await this.exposure();
466
+ this.requireExposed(exposure, existingRow.folderId, id);
467
467
  const current = await this.decryptRow(key, existingRow);
468
468
  const title = changes.title ?? current.title;
469
469
  const content = changes.content ?? current.content;
@@ -482,6 +482,7 @@ var NotesService = class {
482
482
  async searchNotes(query, limit = 20) {
483
483
  const key = this.requireSession();
484
484
  const q = query.toLowerCase();
485
+ const exposure = await this.exposure();
485
486
  const results = [];
486
487
  let scanned = 0;
487
488
  let total = 0;
@@ -492,6 +493,8 @@ var NotesService = class {
492
493
  for (const row of notes) {
493
494
  if (row.hidden === true)
494
495
  continue;
496
+ if (!exposure.allows(row.folderId))
497
+ continue;
495
498
  if (scanned >= SEARCH_SCAN_CAP)
496
499
  break scan;
497
500
  scanned++;
@@ -512,18 +515,33 @@ var NotesService = class {
512
515
  }
513
516
  async moveNote(id, folderId) {
514
517
  this.requireSession();
518
+ const existingRow = await this.api.getNote(id);
519
+ const exposure = await this.exposure();
520
+ this.requireExposed(exposure, existingRow.folderId, id);
521
+ if (folderId !== null) {
522
+ this.requireExposed(exposure, folderId, id);
523
+ }
515
524
  await this.api.updateNote(id, { folderId });
516
525
  }
517
526
  async setStar(id, starred) {
518
527
  this.requireSession();
528
+ const existingRow = await this.api.getNote(id);
529
+ const exposure = await this.exposure();
530
+ this.requireExposed(exposure, existingRow.folderId, id);
519
531
  await this.api.updateNote(id, { starred });
520
532
  }
521
533
  async archiveNote(id, archived) {
522
534
  this.requireSession();
535
+ const existingRow = await this.api.getNote(id);
536
+ const exposure = await this.exposure();
537
+ this.requireExposed(exposure, existingRow.folderId, id);
523
538
  await this.api.updateNote(id, { archived });
524
539
  }
525
540
  async trashNote(id) {
526
541
  this.requireSession();
542
+ const existingRow = await this.api.getNote(id);
543
+ const exposure = await this.exposure();
544
+ this.requireExposed(exposure, existingRow.folderId, id);
527
545
  await this.api.updateNote(id, { deleted: true });
528
546
  }
529
547
  async listFolders() {
@@ -545,6 +563,14 @@ var NotesService = class {
545
563
  const { folders } = await this.api.listFolders();
546
564
  return computeExposure(folders);
547
565
  }
566
+ /** Throws unless `folderId` is exposed to MCP. Every method that reads or
567
+ * mutates a specific note (by id) must call this — knowing a note's id is
568
+ * not consent to bypass the "Expose to MCP" folder switch. */
569
+ requireExposed(exposure, folderId, id) {
570
+ if (!exposure.allows(folderId)) {
571
+ throw new Error(`Note ${id} is in a folder that is not exposed to MCP. Turn on "Expose to MCP" in that folder's settings in the Virlow app to access it here.`);
572
+ }
573
+ }
548
574
  async createFolder(name, parentId) {
549
575
  this.requireSession();
550
576
  const folder = await this.api.createFolder(name, parentId);
@@ -1535,7 +1561,7 @@ function startUnlockServer(api, vault, opts = {}) {
1535
1561
  }
1536
1562
 
1537
1563
  // src/version.ts
1538
- var VERSION = true ? "3.11.7" : "dev";
1564
+ var VERSION = true ? "3.11.8" : "dev";
1539
1565
 
1540
1566
  // src/server.ts
1541
1567
  function textResult(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virlow-mcp",
3
- "version": "3.11.7",
3
+ "version": "3.11.8",
4
4
  "description": "Local MCP server for Virlow Secure Notes: end-to-end-encrypted AI memories and notes for Cursor, Claude, Codex, and any MCP client.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,9 +34,9 @@
34
34
  "esbuild": "^0.25.0",
35
35
  "typescript": "^5.8.0",
36
36
  "vitest": "^3.0.0",
37
- "@batalabs/virlow-crypto": "3.11.7",
38
- "@batalabs/virlow-memory": "3.11.7",
39
- "@batalabs/virlow-mcp-core": "3.11.7"
37
+ "@batalabs/virlow-crypto": "3.11.8",
38
+ "@batalabs/virlow-memory": "3.11.8",
39
+ "@batalabs/virlow-mcp-core": "3.11.8"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "node build.mjs",