-
Notifications
You must be signed in to change notification settings - Fork 1
feat: v2.6.0 optimization tracks A+B+C+D #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| .git | ||
| .github | ||
| .gitignore | ||
| *.md | ||
| *.sh | ||
| scripts/ | ||
| test/ | ||
| node_modules/ | ||
| .npm | ||
| .env | ||
| .env.local | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,4 +18,5 @@ jobs: | |
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| cache: 'npm' | ||
| - run: npm test | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Clausidian Docker image — MCP server for Obsidian vault management | ||
| FROM node:18-alpine | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Copy package files | ||
| COPY package.json package-lock.json ./ | ||
|
|
||
| # Install dependencies | ||
| RUN npm ci | ||
|
|
||
| # Copy application code | ||
| COPY bin/ ./bin/ | ||
| COPY src/ ./src/ | ||
| COPY scaffold/ ./scaffold/ | ||
| COPY skill/ ./skill/ | ||
|
|
||
| # Create vault mount point | ||
| RUN mkdir -p /vault | ||
|
|
||
| # Set vault as default working directory for MCP operations | ||
| ENV VAULT_ROOT=/vault | ||
|
|
||
| # Default command: start MCP server | ||
| CMD ["node", "bin/cli.mjs", "serve", "--vault", "/vault"] | ||
|
|
||
| # Volume for vault data | ||
| VOLUME ["/vault"] | ||
|
|
||
| # Expose stdio for MCP protocol | ||
| EXPOSE 3000 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # MCP Configuration Setup | ||
|
|
||
| > `mcp-config-example.json` is a template for setting up clausidian as an MCP server in Claude Code. | ||
|
|
||
| ## Setup Instructions | ||
|
|
||
| ### 1. Replace the vault path | ||
|
|
||
| Edit `mcp-config-example.json` and replace `REPLACE_WITH_YOUR_VAULT_PATH` with your actual vault directory: | ||
|
|
||
| - **macOS/Linux**: `/Users/username/my-vault` or `$HOME/my-vault` (use full path, not `~`) | ||
| - **Windows**: `C:\\Users\\username\\my-vault` | ||
|
|
||
| ### 2. Common vault locations | ||
|
|
||
| - **iCloud Drive**: `/Users/username/Library/Mobile Documents/com~apple~CloudDocs/my-vault` | ||
| - **Dropbox**: `/Users/username/Dropbox/my-vault` | ||
| - **Local**: `/Users/username/obsidian-vault` | ||
|
|
||
| ### 3. Add to Claude Code MCP config | ||
|
|
||
| 1. Edit `~/.claude/.mcp.json` | ||
| 2. Paste the entire `mcpServers` block from this example into your existing `mcpServers` object | ||
|
|
||
| Example `~/.claude/.mcp.json`: | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "clausidian": { | ||
| "command": "clausidian", | ||
| "args": ["serve", "--vault", "/Users/username/my-vault"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### 4. Verify configuration | ||
|
|
||
| After editing: | ||
| 1. Restart Claude Code | ||
| 2. Run `/obsidian health` in any project | ||
| 3. If you see vault statistics, configuration is successful | ||
|
|
||
| ### 5. Troubleshooting | ||
|
|
||
| | Issue | Solution | | ||
| |-------|----------| | ||
| | Permission denied | Ensure your user has access to the vault directory | | ||
| | Path not found | Use `echo $HOME` to confirm path; always use full path (no `~`) | | ||
| | MCP not loading | Check Claude Code output logs for clausidian connection errors | | ||
| | Command not found | Ensure `clausidian` is installed: `npm install -g clausidian` | |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Apply changes to multiple notes at once. | ||
|
|
||
| Run: `clausidian batch tag --tag newtag --filter status:active` | ||
|
|
||
| Operations: | ||
| - `batch tag`: add/remove tags from matching notes | ||
| - `batch update`: change frontmatter (status, goal, summary) | ||
| - `batch archive`: bulk-archive matching notes | ||
| - Filter by type, tag, status, or keyword | ||
|
|
||
| $ARGUMENTS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| Get vault health score and diagnostics. | ||
|
|
||
| Run: `clausidian health --json` | ||
|
|
||
| Checks: | ||
| - Total notes, archive count, orphan notes | ||
| - Index freshness | ||
| - Tag coverage, broken link count | ||
| - Average note length and update frequency | ||
| - Overall health score (0-100) | ||
|
|
||
| $ARGUMENTS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| Display vault statistics (note count, types, tags, sizes). | ||
|
|
||
| Run: `clausidian stats --json` | ||
|
|
||
| Returns: | ||
| - Notes by type (area, project, resource, idea) | ||
| - Top tags and their usage | ||
| - Note length distribution | ||
| - Archive ratio | ||
| - Active notes (modified in last 7, 30, 90 days) | ||
| - Total vault size | ||
|
|
||
| $ARGUMENTS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Rebuild vault indices (_index.md, _tags.md, _graph.md, directory indexes). | ||
|
|
||
| Run: `clausidian sync` | ||
|
|
||
| This rescans all notes and updates: | ||
| - _index.md: central index of all notes with summaries | ||
| - _tags.md: tag-to-note mapping with counts | ||
| - _graph.md: Mermaid knowledge graph with relationship suggestions | ||
| - directory indexes: one per area/project/resource/idea type | ||
|
|
||
| $ARGUMENTS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| List all tags in vault with usage counts. | ||
|
|
||
| Run: `clausidian tag-list` | ||
|
|
||
| Shows: | ||
| - Each tag name | ||
| - How many notes use it | ||
| - Most common tags first | ||
| - Filtered by prefix if provided (e.g., `tag-list concept-`) | ||
|
|
||
| $ARGUMENTS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Update note frontmatter (tags, status, goal, summary). | ||
|
|
||
| Run: `clausidian update <note> --tags tag1,tag2 --status active` | ||
|
|
||
| If the CLI is not available: | ||
| 1. Read the note file | ||
| 2. Update the YAML frontmatter with provided fields | ||
| 3. Keep body unchanged | ||
| 4. Update indices (_tags.md, _graph.md) | ||
|
|
||
| $ARGUMENTS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ import { resolve, dirname } from 'path'; | |
| import { fileURLToPath } from 'url'; | ||
| import { Vault } from './vault.mjs'; | ||
| import { getMcpTools, getMcpDispatch } from './registry.mjs'; | ||
| import { SearchCache } from './search-cache.mjs'; | ||
|
|
||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | ||
| const PKG_VERSION = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8')).version; | ||
|
|
@@ -20,6 +21,13 @@ const PKG_VERSION = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.js | |
| const TOOLS = getMcpTools(); | ||
| const DISPATCH = getMcpDispatch(); | ||
|
|
||
| // Tools that modify vault state — invalidate cache on these only | ||
| const WRITE_TOOLS = new Set([ | ||
| 'note', 'journal', 'capture', 'update', 'patch', 'delete', | ||
| 'archive', 'rename', 'move', 'merge', 'relink', 'import', 'sync', | ||
| 'batch_tag', 'batch_update', 'batch_archive', | ||
| ]); | ||
|
|
||
| // ── MCP Resources ────────────────────────────────────── | ||
|
|
||
| const RESOURCES = [ | ||
|
|
@@ -93,6 +101,7 @@ export class McpServer { | |
| constructor(vaultRoot) { | ||
| this.vaultRoot = vaultRoot; | ||
| this._vault = null; | ||
| this._searchCache = new SearchCache(); | ||
| } | ||
|
|
||
| get vault() { | ||
|
|
@@ -101,21 +110,39 @@ export class McpServer { | |
| } | ||
|
|
||
| async handleToolCall(name, args) { | ||
| this.vault.invalidateCache(); | ||
| // Only invalidate cache for write operations | ||
| if (WRITE_TOOLS.has(name)) { | ||
| this.vault.invalidateCache(); | ||
| this._searchCache.clear(); | ||
| } | ||
|
|
||
| const handler = DISPATCH[name]; | ||
| if (!handler) throw new Error(`Unknown tool: ${name}`); | ||
|
|
||
| // Try search cache for read-only search tools | ||
| if ((name === 'search' || name === 'embed-search' || name === 'smart-search') && !WRITE_TOOLS.has(name)) { | ||
| const cached = this._searchCache.get(args.keyword, args); | ||
|
Comment on lines
+123
to
+124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The MCP cache path uses Useful? React with 👍 / 👎. |
||
| if (cached) return cached; | ||
| } | ||
|
|
||
| const origLog = console.log; | ||
| const origError = console.error; | ||
| console.log = () => {}; | ||
| console.error = () => {}; | ||
| let result; | ||
| try { | ||
| return await handler(this.vaultRoot, args); | ||
| result = await handler(this.vaultRoot, args); | ||
| } finally { | ||
| console.log = origLog; | ||
| console.error = origError; | ||
| } | ||
|
|
||
| // Cache search results | ||
| if ((name === 'search' || name === 'embed-search' || name === 'smart-search') && !WRITE_TOOLS.has(name)) { | ||
| this._searchCache.set(args.keyword, args, result); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| readResource(uri) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cache invalidation now depends on
WRITE_TOOLS, but this set omits mutating MCP tools (for examplepin/unpininsrc/commands/pin.mjs,tag_renameinsrc/commands/tag.mjs, andreviewinsrc/commands/review.mjs). After those tools update files, cached search responses are not cleared, so repeatedsearchcalls can return stale data (e.g.,search pinnedbefore/afterpinstill returns the pre-update cached result).Useful? React with 👍 / 👎.