obsidian_rag_query
Perform semantic search and question-answering on your Obsidian vault using a local retrieval-augmented generation pipeline.
Instructions
Run the local obsidian-rag CLI for true semantic retrieval/LLM answering. Requires rag to be installed and indexed.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| question | Yes | ||
| k | No | ||
| folder | No | ||
| tag | No | ||
| source | No | ||
| ragVault | No | Optional obsidian-rag vault registry name; independent from this MCP vault name. | |
| session | No | ||
| raw | No | ||
| multi | No | ||
| force | No | ||
| timeoutMs | No |
Implementation Reference
- src/tools.ts:289-340 (registration)Registration, schema, and handler for the 'obsidian_rag_query' tool. Uses the tool() helper (line 41-60) to register with the MCP server. The schema defines inputs: question, k, folder, tag, source, ragVault, session, raw, multi, force, timeoutMs. The handler builds CLI args and executes 'config.ragCommand' (default 'rag', configurable via OBSIDIAN_RAG_COMMAND env var) via execFileAsync, returning stdout as the answer.
tool( "obsidian_rag_query", "Run the local obsidian-rag CLI for true semantic retrieval/LLM answering. Requires rag to be installed and indexed.", { question: z.string(), k: z.number().int().min(1).max(15).optional().default(5), folder: z.string().optional(), tag: z.string().optional(), source: z.string().optional(), ragVault: z.string().optional().describe("Optional obsidian-rag vault registry name; independent from this MCP vault name."), session: z.string().optional(), raw: z.boolean().optional().default(false), multi: z.boolean().optional().default(false), force: z.boolean().optional().default(false), timeoutMs: z.number().int().min(5000).max(300000).optional().default(180000), }, async (args) => { const cliArgs = ["query", "-k", String(args.k), "--plain"]; if (args.folder) cliArgs.push("--folder", args.folder); if (args.tag) cliArgs.push("--tag", args.tag); if (args.source) cliArgs.push("--source", args.source); if (args.ragVault) cliArgs.push("--vault", args.ragVault); if (args.session) cliArgs.push("--session", args.session); if (args.raw) cliArgs.push("--raw"); if (args.multi) cliArgs.push("--multi"); if (args.force) cliArgs.push("--force"); cliArgs.push(args.question); try { const { stdout, stderr } = await execFileAsync(config.ragCommand, cliArgs, { timeout: args.timeoutMs, maxBuffer: 10 * 1024 * 1024, env: { ...process.env, NO_COLOR: "1" }, }); return { ok: true, command: config.ragCommand, args: cliArgs, answer: stdout.trim(), stderr: stderr.trim() || undefined }; } catch (error) { const err = error as Error & { stdout?: string; stderr?: string; code?: string | number }; const notFound = err.code === "ENOENT"; return { ok: false, command: config.ragCommand, args: cliArgs, error: notFound ? `RAG command '${config.ragCommand}' not found. Install obsidian-rag and ensure it is in PATH, or set OBSIDIAN_RAG_COMMAND to the full path.` : err.message, code: err.code, stdout: err.stdout?.trim(), stderr: err.stderr?.trim(), }; } }, { readOnlyHint: true, idempotentHint: false }, ); - src/tools.ts:292-304 (schema)Zod schema for obsidian_rag_query: question (string), k (1-15, default 5), folder/tag/source/ragVault/session (optional strings), raw/multi/force (optional booleans, default false), timeoutMs (5000-300000, default 180000).
{ question: z.string(), k: z.number().int().min(1).max(15).optional().default(5), folder: z.string().optional(), tag: z.string().optional(), source: z.string().optional(), ragVault: z.string().optional().describe("Optional obsidian-rag vault registry name; independent from this MCP vault name."), session: z.string().optional(), raw: z.boolean().optional().default(false), multi: z.boolean().optional().default(false), force: z.boolean().optional().default(false), timeoutMs: z.number().int().min(5000).max(300000).optional().default(180000), }, - src/tools.ts:305-338 (handler)Handler that constructs CLI args list: ['query', '-k', k, '--plain'] plus optional flags, then calls execFileAsync with config.ragCommand. On success returns {ok:true, command, args, answer, stderr}. On failure returns {ok:false, command, args, error, code, stdout, stderr}. Handles ENOENT with a user-friendly install message.
async (args) => { const cliArgs = ["query", "-k", String(args.k), "--plain"]; if (args.folder) cliArgs.push("--folder", args.folder); if (args.tag) cliArgs.push("--tag", args.tag); if (args.source) cliArgs.push("--source", args.source); if (args.ragVault) cliArgs.push("--vault", args.ragVault); if (args.session) cliArgs.push("--session", args.session); if (args.raw) cliArgs.push("--raw"); if (args.multi) cliArgs.push("--multi"); if (args.force) cliArgs.push("--force"); cliArgs.push(args.question); try { const { stdout, stderr } = await execFileAsync(config.ragCommand, cliArgs, { timeout: args.timeoutMs, maxBuffer: 10 * 1024 * 1024, env: { ...process.env, NO_COLOR: "1" }, }); return { ok: true, command: config.ragCommand, args: cliArgs, answer: stdout.trim(), stderr: stderr.trim() || undefined }; } catch (error) { const err = error as Error & { stdout?: string; stderr?: string; code?: string | number }; const notFound = err.code === "ENOENT"; return { ok: false, command: config.ragCommand, args: cliArgs, error: notFound ? `RAG command '${config.ragCommand}' not found. Install obsidian-rag and ensure it is in PATH, or set OBSIDIAN_RAG_COMMAND to the full path.` : err.message, code: err.code, stdout: err.stdout?.trim(), stderr: err.stderr?.trim(), }; } }, - src/tools.ts:41-60 (helper)The 'tool' helper function that wraps server.tool() with annotations and jsonResult formatting. All tools including obsidian_rag_query use this helper for registration.
const tool = <S extends ToolShape>( name: string, description: string, schema: S, handler: (args: z.objectOutputType<S, z.ZodTypeAny>) => Promise<unknown> | unknown, annotations: { readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean } = {}, ) => { (server.tool as any)( name, description, schema, { readOnlyHint: annotations.readOnlyHint ?? false, destructiveHint: annotations.destructiveHint ?? false, idempotentHint: annotations.idempotentHint ?? false, openWorldHint: false, }, async (args: unknown) => jsonResult(await handler(args as z.objectOutputType<S, z.ZodTypeAny>), pretty), ); }; - src/tools.ts:38-60 (registration)registerObsidianTools function that registers all tools on the MCP server, including the tool() helper wrapper definition.
export function registerObsidianTools(server: McpServer, vaults: VaultManager, config: ObsidianMcpConfig): void { vaults.onInvalidate = invalidateNotesCache; const pretty = config.pretty; const tool = <S extends ToolShape>( name: string, description: string, schema: S, handler: (args: z.objectOutputType<S, z.ZodTypeAny>) => Promise<unknown> | unknown, annotations: { readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean } = {}, ) => { (server.tool as any)( name, description, schema, { readOnlyHint: annotations.readOnlyHint ?? false, destructiveHint: annotations.destructiveHint ?? false, idempotentHint: annotations.idempotentHint ?? false, openWorldHint: false, }, async (args: unknown) => jsonResult(await handler(args as z.objectOutputType<S, z.ZodTypeAny>), pretty), ); };