read_note
Retrieve a note from your Obsidian vault by specifying its relative path. Access note content for reading or downstream processing.
Instructions
Read a note from the Obsidian vault by its relative path.
Args: path: Vault-relative path to the note (e.g. "Cards/My Note.md")
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/mcp_server/tools.py:122-141 (handler)The actual implementation of read_note_impl — reads a note by vault-relative path, assembles markdown output with title, path, tags, frontmatter, and content.
@_tracked("read_note", ["path"]) async def read_note_impl(path: str) -> str: """Read a note by its vault-relative path.""" uid = current_user_id.get() try: note = read_file(path, user_id=uid) except FileNotFoundError: return f"Note not found: {path}" except ValueError as e: return str(e) parts = [f"# {note['title']}\n**Path:** `{note['path']}`"] if note["tags"]: parts.append(f"**Tags:** {', '.join(note['tags'])}") if note["frontmatter"]: fm_lines = [f" {k}: {v}" for k, v in note["frontmatter"].items() if k not in ("title", "tags")] if fm_lines: parts.append("**Frontmatter:**\n" + "\n".join(fm_lines)) parts.append(f"\n---\n{note['content']}") return "\n".join(parts) - src/services/vault.py:104-121 (handler)The read_file helper that reads a note file from disk, parses YAML frontmatter, extracts tags, and returns a dict with path, title, frontmatter, tags, content, size, and modified time.
def read_file(relative_path: str, user_id: int | None = None) -> dict: """Read a note, returning frontmatter + content.""" path = validate_path(relative_path, user_id=user_id) if not path.is_file(): raise FileNotFoundError(f"Note not found: {relative_path}") raw = path.read_text(encoding="utf-8") frontmatter, content = parse_frontmatter(raw) title = frontmatter.get("title") or path.stem tags = extract_tags(raw, frontmatter) return { "path": relative_path, "title": title, "frontmatter": frontmatter, "tags": tags, "content": content, "size": path.stat().st_size, "modified": path.stat().st_mtime, } - src/mcp_server/server.py:64-71 (registration)The MCP tool decorator registration for 'read_note', which delegates to read_note_impl.
@mcp.tool() async def read_note(path: str) -> str: """Read a note from the Obsidian vault by its relative path. Args: path: Vault-relative path to the note (e.g. "Cards/My Note.md") """ return await read_note_impl(path) - src/mcp_server/server.py:66-71 (schema)Type signature and docstring: read_note takes a 'path: str' and returns 'str'.
"""Read a note from the Obsidian vault by its relative path. Args: path: Vault-relative path to the note (e.g. "Cards/My Note.md") """ return await read_note_impl(path) - src/mcp_server/tools.py:73-90 (helper)The _tracked decorator used on read_note_impl to log tool usage timing and parameters.
def _tracked(tool_name: str, param_keys: list[str]): """Decorator that times the call and logs it to usage_logs.""" def decorator(fn): @wraps(fn) async def wrapper(*args, **kwargs): start = time.monotonic() result = await fn(*args, **kwargs) duration_ms = int((time.monotonic() - start) * 1000) params = {} for i, key in enumerate(param_keys): if i < len(args): params[key] = args[i] elif key in kwargs: params[key] = kwargs[key] await _log_usage(tool_name, _truncate_params(params), duration_ms, len(str(result))) return result return wrapper return decorator