/** * VaultMcpAdapter — read/write/list Obsidian vault notes via a WRAPPED on-device MCP server. * * Design: * - Accepts any object satisfying McpServerLike (constructor-injected), so tests can * inject a hand-rolled fake without spawning a subprocess. * - Resolves tool names from config.toolNames overrides before falling back to * DEFAULT_VAULT_TOOL_NAMES (cyanheads/obsidian-mcp-server convention). * - Runs an isOnDevice() guard before the first server interaction; rejects any * declaration whose mcpCommand implies a non-local network transport. * * SECURITY: mcpEnv may contain secrets. This module NEVER includes mcpEnv values in * error messages, log lines, or thrown strings. Only config.name is safe to surface. * * bober: on-device-only guard inspects mcpCommand/mcpArgs (never mcpEnv). If a * remote-transport MCP server is ever needed, add a new adapter — do not relax * this guard; it is a deliberate security/privacy boundary. */ import type { ToolDescriptor } from "../mcp/external-client.js"; import type { VaultSection } from "../config/schema.js"; import type { VaultNote } from "./types.js"; /** * Minimal interface satisfied by ExternalMcpServer. * Tests inject a hand-rolled fake; no concrete class dependency for the adapter's core. */ export interface McpServerLike { start(): Promise; listTools(): Promise; callTool(name: string, args: unknown): Promise; stop(): Promise; } /** * Default MCP tool names for cyanheads/obsidian-mcp-server. * Override any entry via config.obsidian.toolNames to support alternative servers * (e.g., Obsidian Local REST API plugin's built-in MCP). */ export declare const DEFAULT_VAULT_TOOL_NAMES: { readonly readNote: "obsidian_read_file"; readonly writeNote: "obsidian_update_file"; readonly listNotes: "obsidian_list_files_in_dir"; }; /** * Returns true when the obsidian config describes an on-device (local) MCP server. * * Predicate: * REJECT if mcpCommand matches a remote URL scheme (https?|wss?|ftp|tcp://). * REJECT if any mcpArgs element embeds a non-loopback host (e.g. --url=https://cloud.example.com). * ACCEPT bare executables (node, npx, /abs/path) with no remote-pointing args. * * SECURITY: Never inspects mcpEnv values — those are secrets and must not drive guard logic. */ export declare function isOnDevice(cfg: NonNullable): boolean; /** * Thin adapter that exposes readNote / writeNote / listNotes over an injected MCP server. * * Usage (production): * const server = new ExternalMcpServer(provider); // satisfies McpServerLike * const adapter = new VaultMcpAdapter(server, config.vault.obsidian); * await adapter.readNote("Notes/Foo.md"); * * Usage (tests): inject a hand-rolled fake that satisfies McpServerLike. */ export declare class VaultMcpAdapter { private readonly server; private readonly config; private readonly toolNames; constructor(server: McpServerLike, config: NonNullable); /** * Guard that MUST be called before any method that would invoke server.start(). * Throws a clear Error (naming only config.name) when the declaration is not on-device. * SECURITY: error message never includes mcpEnv. */ private guardOnDevice; /** * Read a note at the given vault-relative path. * Starts the server (after the on-device guard), calls the configured read tool, * and parses the returned markdown into a VaultNote via sprint-1 parseNote. */ readNote(path: string): Promise; /** * Write a note to the vault. * Serializes the VaultNote to markdown via sprint-1 serializeNote, then calls * the configured write tool with the note's path and serialized content. */ writeNote(note: VaultNote): Promise; /** * List notes in a vault directory. * Calls the configured list tool and returns an array of path strings. */ listNotes(dir?: string): Promise; /** * Stop the underlying MCP server. Safe to call even if start() was never called. */ stop(): Promise; } //# sourceMappingURL=mcp-adapter.d.ts.map