Skip to main content
Glama
sureshsankaran

Obsidian Tools MCP Server

read_note

Retrieve content from a specific note in your Obsidian vault by providing its file path.

Instructions

Read the content of a single note

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYesPath to the note relative to vault root

Implementation Reference

  • The handler function for the 'read_note' tool. It resolves the full path to the note, checks if the file exists, and reads its contents using fs.readFile.
    async function handleReadNote(args: { path: string }): Promise<string> { const fullPath = resolvePath(args.path); if (!(await fileExists(fullPath))) { throw new Error(`Note not found at ${args.path}`); } return await fs.readFile(fullPath, "utf-8"); }
  • The schema definition for the 'read_note' tool in the tools array, used for tool listing and validation. Defines input as an object with a required 'path' string.
    { name: "read_note", description: "Read the content of a single note", inputSchema: { type: "object", properties: { path: { type: "string", description: "Path to the note relative to vault root", }, }, required: ["path"], }, },
  • src/index.ts:893-895 (registration)
    The tool is registered in the main switch statement dispatcher for handling tool calls in the MCP server request handler.
    case "read_note": result = await handleReadNote(args as { path: string }); break;
  • Helper functions resolvePath and fileExists used by the read_note handler to normalize paths and check file existence.
    function resolvePath(notePath: string): string { // Ensure .md extension const normalizedPath = notePath.endsWith(".md") ? notePath : `${notePath}.md`; return path.join(VAULT_PATH, normalizedPath); } async function ensureDir(filePath: string): Promise<void> { const dir = path.dirname(filePath); await fs.mkdir(dir, { recursive: true }); } async function fileExists(filePath: string): Promise<boolean> { try { await fs.access(filePath); return true; } catch { return false; } } async function getAllNotes(dir: string = VAULT_PATH): Promise<string[]> { const notes: string[] = []; const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory() && !entry.name.startsWith(".")) { notes.push(...(await getAllNotes(fullPath))); } else if (entry.isFile() && entry.name.endsWith(".md")) { notes.push(path.relative(VAULT_PATH, fullPath)); } } return notes; } function parseFrontmatter(content: string): { frontmatter: Record<string, unknown> | null; body: string; raw: string; } { const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); if (match) { try { // Simple YAML parsing for common cases const yaml: Record<string, unknown> = {}; const lines = match[1].split("\n"); for (const line of lines) { const colonIndex = line.indexOf(":"); if (colonIndex > 0) { const key = line.slice(0, colonIndex).trim(); let value: unknown = line.slice(colonIndex + 1).trim(); // Handle arrays if (value === "") { value = []; } else if ( typeof value === "string" && value.startsWith("[") && value.endsWith("]") ) { value = value .slice(1, -1) .split(",") .map((v) => v.trim()); } yaml[key] = value; } } return { frontmatter: yaml, body: match[2], raw: match[1] }; } catch { return { frontmatter: null, body: content, raw: "" }; } } return { frontmatter: null, body: content, raw: "" }; } function serializeFrontmatter(obj: Record<string, unknown>): string { const lines: string[] = []; for (const [key, value] of Object.entries(obj)) { if (value === null || value === undefined) continue; if (Array.isArray(value)) { lines.push(`${key}: [${value.join(", ")}]`); } else { lines.push(`${key}: ${value}`); } } return `---\n${lines.join("\n")}\n---\n`; } // Tool handlers async function handleCreateNote(args: { path: string; content: string; overwrite?: boolean; }): Promise<string> { const fullPath = resolvePath(args.path); if (!args.overwrite && (await fileExists(fullPath))) { throw new Error( `Note already exists at ${args.path}. Use overwrite: true to replace.` ); } await ensureDir(fullPath); await fs.writeFile(fullPath, args.content, "utf-8"); return `Successfully created note at ${args.path}`; } async function handleDeleteNote(args: { path: string }): Promise<string> { const fullPath = resolvePath(args.path); if (!(await fileExists(fullPath))) { throw new Error(`Note not found at ${args.path}`); } await fs.unlink(fullPath); return `Successfully deleted note at ${args.path}`; } async function handleUpdateNote(args: { path: string; content: string; }): Promise<string> { const fullPath = resolvePath(args.path); if (!(await fileExists(fullPath))) { throw new Error(`Note not found at ${args.path}`); } await fs.writeFile(fullPath, args.content, "utf-8"); return `Successfully updated note at ${args.path}`; } async function handleAppendToNote(args: { path: string; content: string; separator?: string; }): Promise<string> { const fullPath = resolvePath(args.path); const separator = args.separator ?? "\n\n"; if (!(await fileExists(fullPath))) { throw new Error(`Note not found at ${args.path}`); } const existingContent = await fs.readFile(fullPath, "utf-8"); const newContent = existingContent + separator + args.content; await fs.writeFile(fullPath, newContent, "utf-8"); return `Successfully appended content to ${args.path}`; } async function handlePrependToNote(args: { path: string; content: string; separator?: string; }): Promise<string> { const fullPath = resolvePath(args.path); const separator = args.separator ?? "\n\n"; if (!(await fileExists(fullPath))) { throw new Error(`Note not found at ${args.path}`); } const existingContent = await fs.readFile(fullPath, "utf-8"); const { frontmatter, body, raw } = parseFrontmatter(existingContent); let newContent: string; if (frontmatter) { newContent = `---\n${raw}\n---\n${args.content}${separator}${body}`; } else { newContent = args.content + separator + existingContent; } await fs.writeFile(fullPath, newContent, "utf-8"); return `Successfully prepended content to ${args.path}`; } async function handleRenameNote(args: { oldPath: string; newPath: string; }): Promise<string> { const oldFullPath = resolvePath(args.oldPath); const newFullPath = resolvePath(args.newPath); if (!(await fileExists(oldFullPath))) { throw new Error(`Note not found at ${args.oldPath}`); } if (await fileExists(newFullPath)) { throw new Error(`Note already exists at ${args.newPath}`); } await ensureDir(newFullPath); await fs.rename(oldFullPath, newFullPath); return `Successfully moved note from ${args.oldPath} to ${args.newPath}`; } async function handleReadNote(args: { path: string }): Promise<string> { const fullPath = resolvePath(args.path); if (!(await fileExists(fullPath))) { throw new Error(`Note not found at ${args.path}`); } return await fs.readFile(fullPath, "utf-8"); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sureshsankaran/obsidian-tools-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server