neuro-vault-mcp 3.2.1 → 3.3.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/dist/cli.js CHANGED
@@ -14275,7 +14275,7 @@ function unwrap(schema) {
14275
14275
  function isPlainObject2(value) {
14276
14276
  return typeof value === "object" && value !== null && !Array.isArray(value);
14277
14277
  }
14278
- function coerceField(schema, value) {
14278
+ function coerceFieldValue(schema, value) {
14279
14279
  if (value === null || value === void 0) return value;
14280
14280
  const inner = unwrap(schema);
14281
14281
  if (inner instanceof external_exports.ZodNumber) {
@@ -14303,18 +14303,26 @@ function coerceField(schema, value) {
14303
14303
  }
14304
14304
  return value;
14305
14305
  }
14306
- function coerceInput(schema, value) {
14307
- const inner = unwrap(schema);
14308
- if (!(inner instanceof external_exports.ZodObject)) return value;
14309
- if (!isPlainObject2(value)) return value;
14310
- const shape = inner.shape;
14311
- const out = { ...value };
14312
- for (const key of Object.keys(shape)) {
14313
- if (key in out) {
14314
- out[key] = coerceField(shape[key], out[key]);
14315
- }
14306
+ function wrapField(field) {
14307
+ let inner = field;
14308
+ let isOptional = false;
14309
+ while (inner instanceof external_exports.ZodOptional || inner instanceof external_exports.ZodNullable || inner instanceof external_exports.ZodDefault) {
14310
+ if (inner instanceof external_exports.ZodOptional) isOptional = true;
14311
+ const next = inner.unwrap();
14312
+ if (!next) break;
14313
+ inner = next;
14316
14314
  }
14317
- return out;
14315
+ const wrapped = external_exports.preprocess((v) => coerceFieldValue(inner, v), inner);
14316
+ return isOptional ? wrapped.optional() : wrapped;
14317
+ }
14318
+ function wrapSchemaWithCoercion(schema) {
14319
+ if (!(schema instanceof external_exports.ZodObject)) return schema;
14320
+ const shape = schema.shape;
14321
+ const newShape = {};
14322
+ for (const [key, field] of Object.entries(shape)) {
14323
+ newShape[key] = wrapField(field);
14324
+ }
14325
+ return external_exports.object(newShape);
14318
14326
  }
14319
14327
 
14320
14328
  // src/lib/tool-response.ts
@@ -14385,18 +14393,18 @@ function formatZodError(error48) {
14385
14393
  return { message, issues };
14386
14394
  }
14387
14395
  function registerTool(tool) {
14396
+ const coercingSchema = wrapSchemaWithCoercion(tool.inputSchema);
14388
14397
  return {
14389
14398
  name: tool.name,
14390
14399
  spec: {
14391
14400
  ...tool.title !== void 0 ? { title: tool.title } : {},
14392
14401
  description: tool.description,
14393
- inputSchema: tool.inputSchema,
14402
+ inputSchema: coercingSchema,
14394
14403
  ...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
14395
14404
  ...tool.annotations !== void 0 ? { annotations: tool.annotations } : {}
14396
14405
  },
14397
14406
  handler: async (args) => invokeTool(async () => {
14398
- const coerced = coerceInput(tool.inputSchema, args);
14399
- const result = tool.inputSchema.safeParse(coerced);
14407
+ const result = coercingSchema.safeParse(args);
14400
14408
  if (!result.success) {
14401
14409
  const { message, issues } = formatZodError(result.error);
14402
14410
  throw new ToolHandlerError("INVALID_PARAMS", message, { details: { issues } });
@@ -15368,10 +15376,18 @@ function normalizePath(raw) {
15368
15376
  var VALID_FIELDS = ["frontmatter", "content"];
15369
15377
  var DEFAULT_FIELDS = ["frontmatter", "content"];
15370
15378
  function validateReadNotesInput(input) {
15371
- if (!Array.isArray(input.paths)) {
15372
- throw invalidArgument("paths must be an array", "paths");
15379
+ let paths;
15380
+ if (typeof input.paths === "string") {
15381
+ if (input.paths === "") {
15382
+ throw invalidArgument("paths must not be empty", "paths");
15383
+ }
15384
+ paths = [input.paths];
15385
+ } else if (Array.isArray(input.paths)) {
15386
+ paths = input.paths;
15387
+ } else {
15388
+ throw invalidArgument("paths must be a string or an array of strings", "paths");
15373
15389
  }
15374
- if (input.paths.length < 1 || input.paths.length > 50) {
15390
+ if (paths.length < 1 || paths.length > 50) {
15375
15391
  throw invalidArgument("paths must contain between 1 and 50 entries", "paths");
15376
15392
  }
15377
15393
  let fields;
@@ -15391,7 +15407,7 @@ function validateReadNotesInput(input) {
15391
15407
  }
15392
15408
  fields = input.fields;
15393
15409
  }
15394
- return { paths: input.paths, fields };
15410
+ return { paths, fields };
15395
15411
  }
15396
15412
 
15397
15413
  // src/modules/operations/tools/append-daily.ts
@@ -15901,7 +15917,7 @@ function buildReadDailyTool(deps) {
15901
15917
  // src/modules/operations/tools/read-notes.ts
15902
15918
  var readNotesFieldSchema = external_exports.enum(["frontmatter", "content"]);
15903
15919
  var inputSchema12 = external_exports.object({
15904
- paths: external_exports.array(external_exports.string()).min(1).max(50),
15920
+ paths: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string()).min(1).max(50)]),
15905
15921
  fields: external_exports.array(readNotesFieldSchema).min(1).optional()
15906
15922
  });
15907
15923
  function buildReadNotesTool(deps) {
@@ -15909,7 +15925,7 @@ function buildReadNotesTool(deps) {
15909
15925
  return {
15910
15926
  name: "read_notes",
15911
15927
  title: "Read Notes",
15912
- description: "Read multiple notes in one call. `paths` is an array of 1\u201350 vault-relative POSIX paths; duplicates are de-duplicated and results returned in input order. `fields` projects which parts of each note to return \u2014 choose from `frontmatter` and `content`; default `['frontmatter','content']`. One missing or unreadable path does not fail the others \u2014 per-item errors come back inline. A single MCP roundtrip with parallel disk reads. Reads are direct from disk and do not require Obsidian to be running.",
15928
+ description: "Read one or more notes in one call. `paths` is a vault-relative POSIX path string or an array of 1\u201350 such paths; duplicates are de-duplicated and results returned in input order. `fields` projects which parts of each note to return \u2014 choose from `frontmatter` and `content`; default `['frontmatter','content']`. One missing or unreadable path does not fail the others \u2014 per-item errors come back inline. A single MCP roundtrip with parallel disk reads. Reads are direct from disk and do not require Obsidian to be running.",
15913
15929
  inputSchema: inputSchema12,
15914
15930
  handler: async (input) => {
15915
15931
  const { paths, fields } = validateReadNotesInput(input);
@@ -16084,7 +16100,7 @@ When operating directly inside the vault itself (not from an external project th
16084
16100
  ### Notes (body)
16085
16101
 
16086
16102
  Use \`read_notes\`, \`create_note\`, \`edit_note\`, \`read_daily\`, \`append_daily\` when the user asks to:
16087
- - Read one or more notes by path (\`read_notes\` \u2014 accepts an array of 1\u201350 vault-relative POSIX paths; prefer this over multiple sequential reads)
16103
+ - Read one or more notes by path (\`read_notes\` \u2014 accepts a single vault-relative POSIX path string or an array of 1\u201350 such paths; prefer this over multiple sequential reads)
16088
16104
  - Create a new note, task, or idea (\`create_note\`)
16089
16105
  - Add content to an existing note (\`edit_note\`)
16090
16106
  - Read or update today's daily note (\`read_daily\` / \`append_daily\`)
@@ -16136,7 +16152,7 @@ Use \`search_notes\` when the user is recalling a topic fuzzily, asking a concep
16136
16152
 
16137
16153
  ## Routing between operations and semantic
16138
16154
 
16139
- If the user gives an exact anchor (note path, daily note, tag, frontmatter field), prefer operations tools. If the user is recalling fuzzily or asking a conceptual question, prefer \`search_notes\`. After semantic search finds a relevant note, you can read it with \`read_notes\` (passing the path in a one-element array, or batching with sibling paths) to see the details.
16155
+ If the user gives an exact anchor (note path, daily note, tag, frontmatter field), prefer operations tools. If the user is recalling fuzzily or asking a conceptual question, prefer \`search_notes\`. After semantic search finds a relevant note, you can read it with \`read_notes\` (passing the path as a single string, or batching with sibling paths in an array) to see the details.
16140
16156
 
16141
16157
  For tag-driven questions ("which notes are tagged X?", "show me everything in #ai") use \`query_notes\` with \`{ filter: { tags: '<name>' } }\`, not \`search_notes\` \u2014 the answer is exact, not fuzzy.
16142
16158
  `;