# Data Model: Obsidian MCP Bundle **Phase**: 1 (Design & Contracts) **Date**: 2026-03-22 **Purpose**: Define data structures and their relationships ## Overview This MCP bundle operates on Obsidian's data model (vault, notes, properties, tasks, links, tags). The bundle does NOT maintain its own data store - it provides a typed interface layer over Obsidian CLI operations. ## Core Entities ### Vault **Description**: An Obsidian knowledge base (collection of markdown files and folders) **Attributes**: - `name` (string, required): Vault identifier for user configuration - `path` (string, readonly): Filesystem path to vault root - `fileCount` (number, readonly): Total number of files - `folderCount` (number, readonly): Total number of folders - `size` (number, readonly): Total size in bytes **Validation Rules**: - Name must match an existing Obsidian vault - Path must be accessible with read/write permissions **State Transitions**: N/A (vault state managed by Obsidian) **CLI Mapping**: `obsidian vault`, `obsidian vaults` --- ### Note **Description**: A markdown file within the vault **Attributes**: - `name` (string, required): File name without extension OR exact path - `path` (string, readonly): Full path relative to vault root - `content` (string): File contents (markdown text) - `size` (number, readonly): File size in bytes - `created` (datetime, readonly): Creation timestamp - `modified` (datetime, readonly): Last modification timestamp - `properties` (Property[], readonly): Frontmatter metadata **Validation Rules**: - Name must be valid filename (no path separators in name-only mode) - Path must be within vault (no directory traversal) - Name ambiguity: If multiple files match name, require exact path - Content must be valid UTF-8 text **Relationships**: - HAS_MANY Properties (frontmatter) - HAS_MANY Tasks (checkbox items) - HAS_MANY Links (outgoing) - REFERENCED_BY Links (backlinks) - HAS_MANY Tags **State Transitions**: - Created → Exists - Exists → Modified (content/properties changed) - Exists → Deleted (moved to trash or permanent delete) - Deleted → Restored (from history) **CLI Mapping**: `obsidian create`, `obsidian read`, `obsidian file`, `obsidian delete` --- ### Property **Description**: Frontmatter metadata key-value pair **Attributes**: - `name` (string, required): Property key (alphanumeric, underscores) - `value` (string|number|boolean|date|array, required): Property value - `type` (enum, readonly): text|number|checkbox|date|datetime|list - `file` (string, required): Note path containing this property **Validation Rules**: - Name must be valid YAML key - Value must match type (enforced by Obsidian) - List values must be array of strings - Date/datetime must be ISO 8601 format **Relationships**: - BELONGS_TO Note **CLI Mapping**: `obsidian property:set`, `obsidian property:read`, `obsidian property:remove`, `obsidian properties` --- ### Task **Description**: Markdown checkbox item with status tracking **Attributes**: - `ref` (string, required): Task reference in format "path:line" - `file` (string, required): Note path containing task - `line` (number, required): Line number in file (1-indexed) - `text` (string, readonly): Task description - `status` (string, required): Empty (""), "x" (done), or custom character - `done` (boolean, computed): True if status is "x" **Validation Rules**: - Line must exist in file - Line must contain checkbox syntax: `- [ ]` or `- [x]` or `- [char]` - Status character must be single character or empty **Relationships**: - BELONGS_TO Note - MAY_REFERENCE other Notes (via wikilinks in task text) **State Transitions**: - todo (status="") → done (status="x") - done (status="x") → todo (status="") - Any status → custom status (status="char") **CLI Mapping**: `obsidian tasks`, `obsidian task` --- ### Link **Description**: Connection between notes (wikilink or markdown link) **Attributes**: - `source` (string, required): Source note path - `target` (string, required): Target note name or path - `type` (enum, readonly): wikilink|markdown|unresolved - `count` (number, optional): Number of occurrences (for aggregation) - `resolved` (boolean, readonly): False if target doesn't exist **Validation Rules**: - Source must be existing note - Target format depends on type (wikilink uses `[[name]]`, markdown uses `[text](path)`) **Relationships**: - ORIGINATES_FROM Note (source) - POINTS_TO Note (target, if resolved) **CLI Mapping**: `obsidian links`, `obsidian backlinks`, `obsidian unresolved` --- ### Tag **Description**: Categorization marker (e.g., #project, #important) **Attributes**: - `name` (string, required): Tag without # prefix - `count` (number, optional): Number of occurrences in vault - `files` (string[], optional): Files containing this tag **Validation Rules**: - Name must start with letter, can contain letters/numbers/hyphens/underscores - Case-sensitive - Nested tags use slash (e.g., "project/active") **Relationships**: - APPEARS_IN many Notes **CLI Mapping**: `obsidian tags`, `obsidian tag` --- ### Alias **Description**: Alternative name for a note (defined in frontmatter) **Attributes**: - `alias` (string, required): Alternative name - `file` (string, required): Note path this alias refers to **Validation Rules**: - Alias must be defined in note's frontmatter `aliases` property **Relationships**: - BELONGS_TO Note **CLI Mapping**: `obsidian aliases` --- ### Template **Description**: Reusable note template **Attributes**: - `name` (string, required): Template name/path - `content` (string, readonly): Template content (may include variables) - `resolved` (string, optional): Template with variables resolved **Validation Rules**: - Name must match existing template file - Variables use {{variable}} syntax **CLI Mapping**: `obsidian templates`, `obsidian template:read`, `obsidian template:insert` --- ### DailyNote **Description**: Special note type for daily journaling **Attributes**: - `date` (date, required): Date for the daily note - `path` (string, readonly): Path to daily note file - `content` (string): Daily note content **Validation Rules**: - Date must be valid ISO date (YYYY-MM-DD) - Path follows Obsidian's daily note configuration **Relationships**: - IS_A Note (special type) **State Transitions**: - Not created → Created (on first daily:append or daily:prepend) **CLI Mapping**: `obsidian daily`, `obsidian daily:read`, `obsidian daily:append`, `obsidian daily:prepend`, `obsidian daily:path` --- ### Plugin **Description**: Obsidian plugin (core or community) **Attributes**: - `id` (string, required): Plugin identifier - `name` (string, readonly): Display name - `version` (string, readonly): Plugin version - `enabled` (boolean, readonly): Activation status - `type` (enum, readonly): core|community **Validation Rules**: - ID must match installed plugin **CLI Mapping**: `obsidian plugins`, `obsidian plugins:enabled`, `obsidian plugin` --- ### Theme **Description**: Obsidian visual theme **Attributes**: - `name` (string, required): Theme name - `version` (string, readonly): Theme version - `active` (boolean, readonly): Whether currently active **Validation Rules**: - Name must match installed theme or be empty string for default **CLI Mapping**: `obsidian themes`, `obsidian theme`, `obsidian theme:set` --- ### Bookmark **Description**: Saved reference to file, folder, search, or URL **Attributes**: - `title` (string, required): Bookmark display name - `type` (enum, readonly): file|folder|search|url - `target` (string, required): File path, folder path, search query, or URL **Validation Rules**: - File/folder targets must exist - URLs must be valid HTTP/HTTPS - Search queries must be non-empty **CLI Mapping**: `obsidian bookmarks`, `obsidian bookmark` --- ## Tool Request/Response Schemas Each MCP tool accepts parameters matching entity attributes and returns structured results. ### Common Patterns **Success Response**: ```typescript { success: true, data: { /* entity or array of entities */ } } ``` **Error Response**: ```typescript { success: false, error: { code: string, // e.g., "NOTE_NOT_FOUND" message: string, // Human-readable error details?: unknown // Additional context } } ``` ### Parameter Validation All tool parameters are validated using Zod schemas before CLI execution: ```typescript // Example: Create note parameters { name: string (1-255 chars), path?: string (valid path), content?: string, template?: string, overwrite?: boolean, open?: boolean, newtab?: boolean } ``` ## CLI Output Parsing Obsidian CLI returns data in various formats. The bundle normalizes to JSON: ### Format Mapping - **JSON output** → Direct parse - **TSV output** → Parse to array of objects - **CSV output** → Parse to array of objects - **Text output** → Wrap in `{ raw: string }` object - **Empty output** → Return `{ success: true }` ### Special Cases - **Ambiguous file names**: CLI returns multiple paths → Error with path list - **File not found**: CLI exits with error code → Map to NOT_FOUND error - **Obsidian not running**: CLI connection fails → Map to OBSIDIAN_NOT_RUNNING error ## Data Flow ``` AI Assistant ↓ MCP Tool Call (JSON-RPC) ↓ Input Validation (Zod) ↓ Parameter Sanitization ↓ CLI Command Construction ↓ Obsidian CLI Execution (spawn with timeout) ↓ Output Parsing (JSON/TSV/CSV/Text) ↓ Error Mapping (if needed) ↓ Structured Response (JSON) ↓ MCP Tool Result (JSON-RPC) ↓ AI Assistant ``` ## Concurrency & State - **No shared state**: Each tool call is independent - **Concurrent file modifications**: Rely on Obsidian's built-in conflict detection (per clarification) - **Vault state**: Managed entirely by Obsidian (bundle is stateless) - **CLI commands**: Sequential execution per tool call (no parallel CLI commands from single call) ## Error Handling Errors are categorized and mapped to user-friendly messages: | Error Category | Code | Message Pattern | |----------------|------|-----------------| | Obsidian not running | OBSIDIAN_NOT_RUNNING | "Obsidian application is not running. Please start Obsidian and try again." | | Vault not found | VAULT_NOT_FOUND | "Vault '{name}' not found. Check your vault name in MCP settings." | | File not found | FILE_NOT_FOUND | "Note '{name}' not found. Use exact path or check spelling." | | Ambiguous name | AMBIGUOUS_NAME | "Multiple notes named '{name}' found: {paths}. Please specify exact path." | | Permission denied | PERMISSION_DENIED | "Cannot access {path}. Check file permissions." | | Timeout | TIMEOUT | "Operation took too long (>30s). Try with smaller scope." | | Validation error | VALIDATION_ERROR | "{field} {constraint}" (e.g., "name must be 1-255 characters") | | CLI error | CLI_ERROR | "{original CLI error message}" (for unexpected errors) |