UNPKG

obsidian-mcp-server

Version:

MCP server for Obsidian vaults — read, write, search, and surgically edit notes, tags, and frontmatter via the Local REST API plugin. STDIO or Streamable HTTP.

237 lines 10.7 kB
/** * @fileoverview obsidian_search_notes — text/jsonlogic/omnisearch search with * MCP-spec cursor pagination. The `omnisearch` mode is added conditionally by * the entry point only when the Omnisearch plugin's HTTP server is reachable * at startup. Text-mode hits additionally clip per file via `maxMatchesPerHit` * so a single match-heavy note can't blow the response budget — clipped hits * carry `truncated: true` and `totalMatches`. * @module mcp-server/tools/definitions/obsidian-search-notes.tool */ import { z } from '@cyanheads/mcp-ts-core'; import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors'; /** * Build the `obsidian_search_notes` tool. The `omnisearch` mode is included * in the input/output schemas only when `omnisearchReachable` is true so the * LLM never sees it as an option on a deployment where it can't run. Re-probe * requires a server restart. */ export declare function buildSearchNotesTool({ omnisearchReachable }: { omnisearchReachable: boolean; }): import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{ mode: z.ZodEnum<{ text: "text"; jsonlogic: "jsonlogic"; omnisearch: "omnisearch"; }>; query: z.ZodOptional<z.ZodString>; logic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; contextLength: z.ZodDefault<z.ZodNumber>; pathPrefix: z.ZodOptional<z.ZodString>; maxMatchesPerHit: z.ZodDefault<z.ZodNumber>; cursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ result: z.ZodDiscriminatedUnion<[z.ZodObject<{ mode: z.ZodLiteral<"text">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; matches: z.ZodArray<z.ZodObject<{ context: z.ZodString; match: z.ZodObject<{ start: z.ZodNumber; end: z.ZodNumber; }, z.core.$strip>; }, z.core.$strip>>; totalMatches: z.ZodOptional<z.ZodNumber>; truncated: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ mode: z.ZodLiteral<"jsonlogic">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; result: z.ZodUnknown; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ mode: z.ZodLiteral<"omnisearch">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; basename: z.ZodString; score: z.ZodNumber; foundWords: z.ZodArray<z.ZodString>; matches: z.ZodArray<z.ZodObject<{ match: z.ZodString; offset: z.ZodNumber; }, z.core.$strip>>; excerpt: z.ZodString; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; truncated: z.ZodBoolean; }, z.core.$strip>] | [z.ZodObject<{ mode: z.ZodLiteral<"text">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; matches: z.ZodArray<z.ZodObject<{ context: z.ZodString; match: z.ZodObject<{ start: z.ZodNumber; end: z.ZodNumber; }, z.core.$strip>; }, z.core.$strip>>; totalMatches: z.ZodOptional<z.ZodNumber>; truncated: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ mode: z.ZodLiteral<"jsonlogic">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; result: z.ZodUnknown; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>], "mode">; }, z.core.$strip>, readonly [{ readonly reason: "path_prefix_invalid_mode"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "`pathPrefix` was provided in a non-text mode (only `text` supports prefix filtering)."; readonly recovery: "Drop pathPrefix or switch mode to text for prefix filtering."; }, { readonly reason: "query_required"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "`query` is missing for `text` or `omnisearch` mode (required for both)."; readonly recovery: "Pass `query` — substring for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch."; }, { readonly reason: "logic_required"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "`logic` is missing for `jsonlogic` mode."; readonly recovery: "Pass a JSONLogic tree as `logic`, e.g. `{\"glob\": [{\"var\": \"path\"}, \"Projects/*.md\"]}`."; }, { readonly reason: "omnisearch_unreachable"; readonly code: JsonRpcErrorCode.ServiceUnavailable; readonly when: "Omnisearch was reachable at startup but is now unreachable (Obsidian quit, plugin disabled, or mobile session)."; readonly retryable: true; readonly recovery: "Restart Obsidian with the Omnisearch plugin enabled, then restart this MCP server so it re-probes the plugin URL."; }], { readonly effectiveQuery: z.ZodOptional<z.ZodString>; readonly notice: z.ZodOptional<z.ZodString>; }>; /** * Static specimen for the MCP definition linter (which duck-types tool * exports out of each `.tool.ts` file) and for existing tests that import * the tool directly. Defaults to `omnisearchReachable: false` — the safe * baseline that doesn't assume the optional plugin is installed. The entry * point (`src/index.ts`) builds the live tool via `buildSearchNotesTool` * with the actual probe result; this export is not the registered tool. * The omnisearch-enabled variant is exercised by tests rather than the * linter (two exports under the same tool name would collide on * `name-unique`). */ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{ mode: z.ZodEnum<{ text: "text"; jsonlogic: "jsonlogic"; omnisearch: "omnisearch"; }>; query: z.ZodOptional<z.ZodString>; logic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; contextLength: z.ZodDefault<z.ZodNumber>; pathPrefix: z.ZodOptional<z.ZodString>; maxMatchesPerHit: z.ZodDefault<z.ZodNumber>; cursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ result: z.ZodDiscriminatedUnion<[z.ZodObject<{ mode: z.ZodLiteral<"text">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; matches: z.ZodArray<z.ZodObject<{ context: z.ZodString; match: z.ZodObject<{ start: z.ZodNumber; end: z.ZodNumber; }, z.core.$strip>; }, z.core.$strip>>; totalMatches: z.ZodOptional<z.ZodNumber>; truncated: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ mode: z.ZodLiteral<"jsonlogic">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; result: z.ZodUnknown; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ mode: z.ZodLiteral<"omnisearch">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; basename: z.ZodString; score: z.ZodNumber; foundWords: z.ZodArray<z.ZodString>; matches: z.ZodArray<z.ZodObject<{ match: z.ZodString; offset: z.ZodNumber; }, z.core.$strip>>; excerpt: z.ZodString; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; truncated: z.ZodBoolean; }, z.core.$strip>] | [z.ZodObject<{ mode: z.ZodLiteral<"text">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; matches: z.ZodArray<z.ZodObject<{ context: z.ZodString; match: z.ZodObject<{ start: z.ZodNumber; end: z.ZodNumber; }, z.core.$strip>; }, z.core.$strip>>; totalMatches: z.ZodOptional<z.ZodNumber>; truncated: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ mode: z.ZodLiteral<"jsonlogic">; hits: z.ZodArray<z.ZodObject<{ filename: z.ZodString; result: z.ZodUnknown; }, z.core.$strip>>; totalCount: z.ZodNumber; nextCursor: z.ZodOptional<z.ZodString>; }, z.core.$strip>], "mode">; }, z.core.$strip>, readonly [{ readonly reason: "path_prefix_invalid_mode"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "`pathPrefix` was provided in a non-text mode (only `text` supports prefix filtering)."; readonly recovery: "Drop pathPrefix or switch mode to text for prefix filtering."; }, { readonly reason: "query_required"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "`query` is missing for `text` or `omnisearch` mode (required for both)."; readonly recovery: "Pass `query` — substring for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch."; }, { readonly reason: "logic_required"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "`logic` is missing for `jsonlogic` mode."; readonly recovery: "Pass a JSONLogic tree as `logic`, e.g. `{\"glob\": [{\"var\": \"path\"}, \"Projects/*.md\"]}`."; }, { readonly reason: "omnisearch_unreachable"; readonly code: JsonRpcErrorCode.ServiceUnavailable; readonly when: "Omnisearch was reachable at startup but is now unreachable (Obsidian quit, plugin disabled, or mobile session)."; readonly retryable: true; readonly recovery: "Restart Obsidian with the Omnisearch plugin enabled, then restart this MCP server so it re-probes the plugin URL."; }], { readonly effectiveQuery: z.ZodOptional<z.ZodString>; readonly notice: z.ZodOptional<z.ZodString>; }>; //# sourceMappingURL=obsidian-search-notes.tool.d.ts.map