pkm-mcp-server 1.5.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "obsidian-pkm-marketplace",
3
+ "description": "Obsidian PKM Plugin for Claude Code",
4
+ "plugins": [
5
+ {
6
+ "name": "obsidian-pkm",
7
+ "description": "Bidirectional knowledge flow between Claude Code and Obsidian — tools, skills, and hooks for PKM",
8
+ "source": "."
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "obsidian-pkm",
3
+ "version": "2.0.0",
4
+ "description": "Bidirectional knowledge flow between Claude Code and Obsidian — tools, skills, and hooks for PKM",
5
+ "author": {
6
+ "name": "Adrian Verhoosel"
7
+ },
8
+ "repository": "https://github.com/AdrianV101/obsidian-pkm-plugin",
9
+ "license": "MIT",
10
+ "keywords": ["obsidian", "pkm", "knowledge-management", "mcp", "claude-code"],
11
+ "mcpServers": "./.mcp.json",
12
+ "hooks": "./hooks/hooks.json",
13
+ "commands": ["./commands"],
14
+ "skills": ["./skills"]
15
+ }
package/.mcp.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "obsidian-pkm": {
3
+ "command": "node",
4
+ "args": ["${CLAUDE_PLUGIN_ROOT}/cli.js"],
5
+ "env": {
6
+ "VAULT_PATH": "${VAULT_PATH}",
7
+ "OPENAI_API_KEY": "${OPENAI_API_KEY}"
8
+ }
9
+ }
10
+ }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,54 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.0.0] - 2026-03-24
10
+
11
+ ### Breaking Changes
12
+ - **Package renamed**: `pkm-mcp-server` → `obsidian-pkm`
13
+ - **CLI binary renamed**: `pkm-mcp-server` → `obsidian-pkm`
14
+ - **GitHub repo renamed**: `Obsidian-MCP` → `obsidian-pkm-plugin`
15
+
16
+ ### Added
17
+ - Claude Code plugin structure (`.claude-plugin/plugin.json`)
18
+ - Declarative hook registration (`hooks/hooks.json`) — no more manual hook copying
19
+ - `/obsidian-pkm:setup` slash command for guided configuration
20
+ - Self-hosted marketplace for `claude plugin install` support
21
+ - `.mcp.json` for plugin-managed MCP server
22
+
23
+ ### Changed
24
+ - Version jump from 1.6.x to 2.0.0 reflects the structural migration to a full plugin
25
+ - Init wizard simplified for npm fallback path
26
+
27
+ ## [1.6.0] - 2026-03-23
28
+
29
+ ### Security
30
+ - **Fix command injection in capture-handler.sh** — replaced `eval` of Node.js output with safe per-variable stdout capture. Previously, crafted `title` or `content` values containing shell metacharacters (e.g., `$(...)`) could execute arbitrary commands.
31
+ - `handleTrash` and `handleMove` now catch ENOENT errors and return relative paths instead of leaking absolute vault paths in error messages
32
+
33
+ ### Changed
34
+ - Wikilink extraction in `handleLinks` and `handleSuggestLinks` now uses shared `extractWikilinks` from `graph.js` instead of duplicating the regex
35
+ - Extracted `addToBasenameMap`/`removeFromBasenameMap` helpers in `handlers.js` (replaces 3 inline copies)
36
+ - Named constant `SESSION_ID_DISPLAY_LEN` replaces magic number `8` for session ID truncation
37
+ - `@modelcontextprotocol/sdk` version range tightened from `^1.0.0` to `^1.27.0` to reflect actual minimum tested version
38
+
39
+ ### Fixed
40
+ - README hook configuration referenced deleted `stop-sweep.sh` (renamed to `stop-sweep.js` in v1.5.0)
41
+ - `vault_peek` tool description no longer claims "line numbers" in heading outline (removed in v1.1.0)
42
+ - CHANGELOG: added missing entries for v1.5.3, v1.5.2, v1.1.1; fixed all comparison links
43
+ - CLAUDE.md and sample-project/CLAUDE.md: added missing `note.md` template to template lists
44
+ - CONTRIBUTING.md: clarified that double quotes and semicolons are conventions, not lint-enforced
45
+ - CI workflow: added `permissions: {}` for least privilege; added `npm audit --omit=dev` step
46
+
47
+ ## [1.5.3] - 2026-03-21
48
+
49
+ ### Fixed
50
+ - Init wizard `patchMcpConfig()` now handles multi-line `MCP_CONFIG=` blocks correctly — previously only replaced the first line, leaving orphaned continuation lines
51
+
52
+ ## [1.5.2] - 2026-03-21
53
+
54
+ ### Fixed
55
+ - Hook scripts now auto-detect repo vs installed location — `capture-handler.sh` no longer hardcodes `node $SCRIPT_DIR/../index.js`, falling back to `npx pkm-mcp-server@latest` when `index.js` is not present
56
+
9
57
  ## [1.5.1] - 2026-03-21
10
58
 
11
59
  ### Fixed
@@ -109,6 +157,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
109
157
  ### Security
110
158
  - Resolved 7 transitive dependency vulnerabilities (hono, @hono/node-server, ajv, express-rate-limit, flatted, minimatch, qs)
111
159
 
160
+ ## [1.1.1] - 2026-02-24
161
+
162
+ ### Added
163
+ - Enum validation for task `status` and `priority` fields in `vault_write` and `vault_update_frontmatter`
164
+
112
165
  ## [1.1.0] - 2026-02-23
113
166
 
114
167
  ### Changed
@@ -158,8 +211,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
158
211
  - Atomic file creation in `vault_write` (`wx` flag) prevents race conditions
159
212
  - Error messages sanitized to prevent leaking absolute vault paths
160
213
 
161
- [Unreleased]: https://github.com/AdrianV101/Obsidian-MCP/compare/v1.2.1...HEAD
162
- [1.2.1]: https://github.com/AdrianV101/Obsidian-MCP/compare/v1.2.0...v1.2.1
163
- [1.2.0]: https://github.com/AdrianV101/Obsidian-MCP/compare/v1.1.0...v1.2.0
164
- [1.1.0]: https://github.com/AdrianV101/Obsidian-MCP/compare/v1.0.0...v1.1.0
165
- [1.0.0]: https://github.com/AdrianV101/Obsidian-MCP/releases/tag/v1.0.0
214
+ [Unreleased]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v2.0.0...HEAD
215
+ [2.0.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.6.0...v2.0.0
216
+ [1.6.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.5.3...v1.6.0
217
+ [1.5.3]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.5.2...v1.5.3
218
+ [1.5.2]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.5.1...v1.5.2
219
+ [1.5.1]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.5.0...v1.5.1
220
+ [1.5.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.4.2...v1.5.0
221
+ [1.4.2]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.4.1...v1.4.2
222
+ [1.4.1]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.4.0...v1.4.1
223
+ [1.4.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.3.3...v1.4.0
224
+ [1.3.3]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.3.2...v1.3.3
225
+ [1.3.2]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.3.1...v1.3.2
226
+ [1.3.1]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.3.0...v1.3.1
227
+ [1.3.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.2.1...v1.3.0
228
+ [1.2.1]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.2.0...v1.2.1
229
+ [1.2.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.1.1...v1.2.0
230
+ [1.1.1]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.1.0...v1.1.1
231
+ [1.1.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/compare/v1.0.0...v1.1.0
232
+ [1.0.0]: https://github.com/AdrianV101/obsidian-pkm-plugin/releases/tag/v1.0.0
package/README.md CHANGED
@@ -1,75 +1,76 @@
1
- # Obsidian PKM MCP Server
1
+ # Obsidian PKM Plugin
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/pkm-mcp-server)](https://www.npmjs.com/package/pkm-mcp-server)
3
+ [![npm version](https://img.shields.io/npm/v/obsidian-pkm)](https://www.npmjs.com/package/obsidian-pkm)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node.js >= 20](https://img.shields.io/badge/Node.js-%3E%3D20-green.svg)](https://nodejs.org/)
6
- [![CI](https://github.com/AdrianV101/Obsidian-MCP/actions/workflows/ci.yml/badge.svg)](https://github.com/AdrianV101/Obsidian-MCP/actions/workflows/ci.yml)
6
+ [![CI](https://github.com/AdrianV101/obsidian-pkm-plugin/actions/workflows/ci.yml/badge.svg)](https://github.com/AdrianV101/obsidian-pkm-plugin/actions/workflows/ci.yml)
7
7
 
8
- An MCP (Model Context Protocol) server that gives Claude Code full read/write access to your Obsidian vault. 19 tools for note CRUD, full-text search, semantic search, graph traversal, metadata queries, session activity tracking, and passive knowledge capture. Published on npm as [`pkm-mcp-server`](https://www.npmjs.com/package/pkm-mcp-server).
8
+ A Claude Code plugin that turns your Obsidian vault into persistent, structured memory for AI coding assistants. Provides 19 MCP tools for note creation, semantic search, graph traversal, metadata queries, session memory, and passive knowledge capture — plus hooks and skills for seamless workflow integration. Published on npm as [`obsidian-pkm`](https://www.npmjs.com/package/obsidian-pkm).
9
9
 
10
10
  ## Why
11
11
 
12
- Claude Code is powerful for writing code, but it forgets everything between sessions. This server turns your Obsidian vault into persistent, structured memory that Claude can read and write natively.
12
+ Claude Code has built-in memory, but it's flat text files scoped to individual projects — no structure, no search beyond exact matches, no connections between notes, and no way to query across projects. As knowledge grows, it doesn't scale. This server replaces that with a proper PKM layer: structured notes with enforced metadata, semantic search, a navigable knowledge graph, and cross-project access through a single Obsidian vault.
13
13
 
14
- - **Session continuity** - Claude logs what it did and can pick up where it left off
15
- - **Structured knowledge** - ADRs, research notes, devlogs created from enforced templates, not freeform text dumps
16
- - **Semantic recall** - "find my notes about caching strategies" works even if you never used the word "caching"
17
- - **Graph context** - Claude can explore related notes by following wikilinks, not just keyword matches
14
+ - **Structured session memory** Every tool call is logged with timestamps and session IDs, so Claude can recall exactly what was read, written, and searched in previous conversations not just what was saved to a text file.
15
+ - **Structured knowledge creation** ADRs, research notes, devlogs, and tasks are created from enforced templates with validated frontmatter — not freeform text dumps. Your vault stays consistent and queryable.
16
+ - **Semantic discovery** "Find my notes about caching strategies" works even if you never used the word "caching." Conceptual search surfaces relevant knowledge that keyword search misses.
17
+ - **Graph-aware connections** Claude explores your knowledge graph by following wikilinks, discovering related notes by proximity rather than just content. Link suggestions help weave new notes into your existing web of knowledge.
18
+ - **Passive capture** — Decisions, tasks, and research findings are captured in the background without interrupting your coding flow. A session-end sweep catches anything that slipped through.
18
19
 
19
- Without this, every Claude Code session starts from scratch. With it, your AI assistant has a working memory that compounds over time.
20
+ Without this, knowledge stays fragmented across per-project memory files and chat logs. With it, your AI assistant maintains a unified knowledge base that compounds over time.
20
21
 
21
22
  https://github.com/user-attachments/assets/58ad9c9b-d987-4728-89e7-33de20b73a38
22
23
 
23
24
  ## Features
24
25
 
26
+ ### Knowledge Creation & Editing
27
+
25
28
  | Tool | Description |
26
29
  |------|-------------|
27
- | `vault_read` | Read note contents (pagination by heading, tail, chunk, line range; auto-redirects large files to peek data) |
28
- | `vault_peek` | Inspect file metadata and structure without reading full content |
29
- | `vault_write` | Create notes from templates (enforces frontmatter) |
30
- | `vault_append` | Append to notes, with positional insert (after/before heading, end of section) |
31
- | `vault_edit` | Surgical string replacement |
32
- | `vault_search` | Full-text search across markdown files |
33
- | `vault_semantic_search` | Semantic similarity search via OpenAI embeddings |
34
- | `vault_suggest_links` | Suggest relevant notes to link based on content similarity |
35
- | `vault_list` | List files and folders |
36
- | `vault_recent` | Recently modified files |
37
- | `vault_links` | Wikilink analysis (incoming/outgoing) |
38
- | `vault_neighborhood` | Graph exploration via BFS wikilink traversal |
39
- | `vault_query` | Query notes by YAML frontmatter (type, status, tags/tags_any, dates, custom fields, sorting) |
40
- | `vault_tags` | Discover tags with counts; folder scoping, glob filters, inline tag parsing |
41
- | `vault_activity` | Session activity log for cross-conversation memory |
42
- | `vault_trash` | Soft-delete to `.trash/` (Obsidian convention), warns about broken incoming links |
43
- | `vault_move` | Move/rename files with automatic wikilink updating across vault |
44
- | `vault_update_frontmatter` | Atomic YAML frontmatter updates (set, create, remove fields; validates enum fields by note type) |
45
- | `vault_capture` | Signal a PKM-worthy capture (decision, task, research, bug); returns immediately, background hook creates the note |
30
+ | `vault_write` | Create notes from templates with enforced frontmatter (ADRs, research, devlogs, tasks, etc.) |
31
+ | `vault_append` | Add content to notes, with positional insert (after/before heading, end of section) |
32
+ | `vault_edit` | Surgical string replacement for precise edits |
33
+ | `vault_update_frontmatter` | Atomic YAML frontmatter updates (set, create, remove fields; validates enums by note type) |
34
+ | `vault_capture` | Signal a PKM-worthy capture (decision, task, research, bug); background hook creates the note |
46
35
 
47
- ### Fuzzy Path Resolution
36
+ ### Discovery & Search
48
37
 
49
- Read-only tools accept short names that resolve to full vault paths:
38
+ | Tool | Description |
39
+ |------|-------------|
40
+ | `vault_search` | Full-text keyword search across markdown files |
41
+ | `vault_semantic_search` | Conceptual similarity search via OpenAI embeddings — finds related notes even with different wording |
42
+ | `vault_query` | Query by YAML frontmatter (type, status, tags, dates, custom fields) with sorting |
43
+ | `vault_tags` | Discover all tags with per-note counts; folder scoping, glob filters, inline tag parsing |
44
+ | `vault_suggest_links` | Suggest relevant notes to link based on content similarity |
50
45
 
51
- ```javascript
52
- vault_read({ path: "devlog" })
53
- // Resolves to: 01-Projects/Obsidian-MCP/development/devlog.md
46
+ ### Graph & Connections
54
47
 
55
- vault_read({ path: "devlog.md" })
56
- // Same result — .md extension is optional
48
+ | Tool | Description |
49
+ |------|-------------|
50
+ | `vault_links` | Wikilink analysis (incoming and outgoing links for a note) |
51
+ | `vault_neighborhood` | Graph exploration via BFS wikilink traversal — discover related notes by proximity |
57
52
 
58
- vault_links({ path: "alpha" })
59
- // Works on vault_peek, vault_links, vault_neighborhood, vault_suggest_links too
60
- ```
53
+ ### Reading & Navigation
61
54
 
62
- Folder-scoped tools accept partial folder names:
55
+ | Tool | Description |
56
+ |------|-------------|
57
+ | `vault_read` | Read note contents (pagination by heading, tail, chunk, line range; auto-redirects large files) |
58
+ | `vault_peek` | Inspect file metadata and structure without reading full content |
59
+ | `vault_list` | List files and folders |
60
+ | `vault_recent` | Recently modified files |
63
61
 
64
- ```javascript
65
- vault_search({ query: "API design", folder: "Obsidian-MCP" })
66
- // Resolves folder to: 01-Projects/Obsidian-MCP
62
+ ### Organization & Maintenance
67
63
 
68
- vault_tags({ folder: "Obsidian-MCP" })
69
- // Works on vault_search, vault_query, vault_tags, vault_recent
70
- ```
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `vault_move` | Move/rename files with automatic wikilink updating across the vault |
67
+ | `vault_trash` | Soft-delete to `.trash/` (Obsidian convention), warns about broken incoming links |
71
68
 
72
- Ambiguous matches return an error listing candidates. Exact paths always work unchanged.
69
+ ### Session Memory
70
+
71
+ | Tool | Description |
72
+ |------|-------------|
73
+ | `vault_activity` | Cross-conversation memory — logs every tool call with timestamps and session IDs |
73
74
 
74
75
  ## Prerequisites
75
76
 
@@ -84,14 +85,32 @@ Ambiguous matches return an error listing candidates. Exact paths always work un
84
85
 
85
86
  ### 1. Install and Set Up
86
87
 
87
- **From npm** (recommended):
88
+ **Via Claude Code plugin marketplace** (recommended):
89
+
90
+ ```bash
91
+ claude plugin marketplace add AdrianV101/obsidian-pkm-plugin
92
+ claude plugin install obsidian-pkm
93
+ ```
94
+
95
+ Then run the setup skill in Claude Code:
96
+
97
+ ```
98
+ /obsidian-pkm:setup
99
+ ```
100
+
101
+ The setup skill walks you through vault path, templates, folder structure, semantic search, and Claude Code registration.
102
+
103
+ **Via npm** (fallback):
88
104
 
89
105
  ```bash
90
- npm install -g pkm-mcp-server
91
- pkm-mcp-server init
106
+ npm install -g obsidian-pkm
107
+ obsidian-pkm init
92
108
  ```
93
109
 
94
- The setup wizard walks you through 5 steps. Nothing is written until you confirm each step, and you can press Ctrl+C at any time to cancel.
110
+ The setup wizard walks you through vault path, templates, folder structure, semantic search, and Claude Code registration. Nothing is written until you confirm each step, and you can press Ctrl+C at any time to cancel.
111
+
112
+ <details>
113
+ <summary>Wizard step details</summary>
95
114
 
96
115
  **Step 1 — Vault path.** Point to an existing Obsidian vault or create a new one. The wizard resolves `~`, `$HOME`, and relative paths automatically. Safety checks prevent using system directories (`/`, `/home`, etc.) as a vault. For existing non-empty directories you can use it as-is, create a subfolder inside it, or wipe it (with triple confirmation). You'll be offered an optional backup before any changes — this creates a timestamped copy next to the vault (e.g. `PKM-backup-2026-03-21T14-30-00/`).
97
116
 
@@ -116,29 +135,31 @@ Existing templates are never overwritten.
116
135
 
117
136
  Each `_index.md` has `type: moc` frontmatter. Existing folders and index files are skipped.
118
137
 
119
- **Step 4 — OpenAI API key (optional).** Enables `vault_semantic_search` and `vault_suggest_links`. The key is stored only in your Claude Code configuration (`~/.claude.json`) and is used solely for generating text embeddings. You can add this later — see [Enable Semantic Search](#3-enable-semantic-search-optional).
138
+ **Step 4 — OpenAI API key (optional).** Enables `vault_semantic_search` and `vault_suggest_links`. The key is stored only in your Claude Code configuration (`~/.claude.json`) and is used solely for generating text embeddings. You can add this later — see [Enable Semantic Search](#4-enable-semantic-search-optional).
120
139
 
121
140
  **Step 5 — Claude Code registration.** Registers the MCP server via `claude mcp add -s user`. If `obsidian-pkm` is already registered, you'll be asked whether to overwrite. The exact command is shown for confirmation before running. If the `claude` CLI is not found on PATH, the wizard prints the manual registration command instead.
122
141
 
142
+ </details>
143
+
123
144
  Restart Claude Code after setup. The server provides all tools except semantic search out of the box.
124
145
 
125
146
  **From source:**
126
147
 
127
148
  ```bash
128
- git clone https://github.com/AdrianV101/Obsidian-MCP.git
129
- cd Obsidian-MCP
149
+ git clone https://github.com/AdrianV101/obsidian-pkm-plugin.git
150
+ cd obsidian-pkm-plugin
130
151
  npm install
131
152
  node cli.js init
132
153
  ```
133
154
 
134
- You can also run the wizard without a global install: `npx pkm-mcp-server init`.
155
+ You can also run the wizard without a global install: `npx obsidian-pkm init`.
135
156
 
136
157
  ### 2. Manual Registration (alternative)
137
158
 
138
159
  If you prefer to skip the wizard, register directly with the Claude CLI:
139
160
 
140
161
  ```bash
141
- claude mcp add -s user -e VAULT_PATH=/absolute/path/to/your/vault -- obsidian-pkm npx -y pkm-mcp-server@latest
162
+ claude mcp add -s user -e VAULT_PATH=/absolute/path/to/your/vault -- obsidian-pkm npx -y obsidian-pkm@latest
142
163
  ```
143
164
 
144
165
  For a source install:
@@ -149,7 +170,15 @@ claude mcp add -s user -e VAULT_PATH=/absolute/path/to/your/vault -- obsidian-pk
149
170
 
150
171
  Verify with `claude mcp list` — you should see `obsidian-pkm: ... - Connected`.
151
172
 
152
- ### 3. Enable Semantic Search (optional)
173
+ ### 3. Verify It Works
174
+
175
+ Open Claude Code and try:
176
+
177
+ > List the folders in my vault
178
+
179
+ Claude should call `vault_list` and show your vault's directory structure. If it works, the server is connected and ready.
180
+
181
+ ### 4. Enable Semantic Search (optional)
153
182
 
154
183
  If you didn't set this up during `init`, add your OpenAI API key by re-registering:
155
184
 
@@ -158,12 +187,12 @@ claude mcp remove obsidian-pkm
158
187
  claude mcp add -s user \
159
188
  -e VAULT_PATH=/absolute/path/to/your/vault \
160
189
  -e OPENAI_API_KEY=sk-... \
161
- -- obsidian-pkm npx -y pkm-mcp-server@latest
190
+ -- obsidian-pkm npx -y obsidian-pkm@latest
162
191
  ```
163
192
 
164
193
  This enables `vault_semantic_search` and `vault_suggest_links`. Uses `text-embedding-3-large` with a SQLite + sqlite-vec index stored at `.obsidian/semantic-index.db`. The index rebuilds automatically — delete the DB file to force a full re-embed.
165
194
 
166
- ### 4. Enable PKM Hooks (optional)
195
+ ### 5. Enable PKM Hooks (optional)
167
196
 
168
197
  The hook system adds automatic context loading at session start and passive knowledge capture during coding. Requires the [Claude CLI](https://docs.anthropic.com/en/docs/claude-cli) installed and authenticated.
169
198
 
@@ -178,7 +207,7 @@ Add to your `~/.claude/settings.json` (alongside the `mcpServers` block):
178
207
  "hooks": [
179
208
  {
180
209
  "type": "command",
181
- "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/Obsidian-MCP/hooks/session-start.js",
210
+ "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/obsidian-pkm-plugin/hooks/session-start.js",
182
211
  "timeout": 15,
183
212
  "statusMessage": "Loading PKM project context..."
184
213
  }
@@ -190,7 +219,7 @@ Add to your `~/.claude/settings.json` (alongside the `mcpServers` block):
190
219
  "hooks": [
191
220
  {
192
221
  "type": "command",
193
- "command": "VAULT_PATH=\"/path/to/your/vault\" /path/to/Obsidian-MCP/hooks/stop-sweep.sh",
222
+ "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/obsidian-pkm-plugin/hooks/stop-sweep.js",
194
223
  "async": true,
195
224
  "timeout": 10
196
225
  }
@@ -203,7 +232,7 @@ Add to your `~/.claude/settings.json` (alongside the `mcpServers` block):
203
232
  "hooks": [
204
233
  {
205
234
  "type": "command",
206
- "command": "VAULT_PATH=\"/path/to/your/vault\" /path/to/Obsidian-MCP/hooks/capture-handler.sh",
235
+ "command": "VAULT_PATH=\"/path/to/your/vault\" /path/to/obsidian-pkm-plugin/hooks/capture-handler.sh",
207
236
  "async": true,
208
237
  "timeout": 10
209
238
  }
@@ -214,12 +243,12 @@ Add to your `~/.claude/settings.json` (alongside the `mcpServers` block):
214
243
  }
215
244
  ```
216
245
 
217
- Replace `/path/to/your/vault` with your Obsidian vault path and `/path/to/Obsidian-MCP` with the path to this repo (or the global npm install location).
246
+ Replace `/path/to/your/vault` with your Obsidian vault path and `/path/to/obsidian-pkm-plugin` with the path to this repo (or the global npm install location).
218
247
 
219
248
  | Hook | Event | What it does |
220
249
  |------|-------|--------------|
221
250
  | `session-start.js` | SessionStart | Loads project context (index, devlog, active tasks) at session start |
222
- | `stop-sweep.sh` | Stop | Scans each exchange for PKM-worthy decisions/tasks, appends to daily captures |
251
+ | `stop-sweep.js` | Stop | PKM librarian: creates structured, graph-linked vault notes from the latest exchange |
223
252
  | `capture-handler.sh` | PostToolUse | Creates structured vault notes when `vault_capture` is called |
224
253
 
225
254
  See [hooks/README.md](hooks/README.md) for architecture details and troubleshooting.
@@ -246,7 +275,7 @@ Vault/
246
275
 
247
276
  ### Templates
248
277
 
249
- `vault_write` loads all `.md` files from `05-Templates/` at startup and enforces frontmatter on every note created. The setup wizard (`pkm-mcp-server init`) installs these automatically — or you can copy the files from `templates/` manually.
278
+ `vault_write` loads all `.md` files from `05-Templates/` at startup and enforces frontmatter on every note created. The setup wizard (`obsidian-pkm init`) installs these automatically — or you can copy the files from `templates/` manually.
250
279
 
251
280
  13 included templates: `adr`, `daily-note`, `devlog`, `fleeting-note`, `literature-note`, `meeting-notes`, `moc`, `note`, `permanent-note`, `project-index`, `research-note`, `task`, `troubleshooting-log`. Add your own templates to `05-Templates/` and they become available to `vault_write` automatically.
252
281
 
@@ -288,15 +317,17 @@ All paths passed to tools are relative to vault root. The server includes path s
288
317
 
289
318
  ## How It Works
290
319
 
291
- **Note creation** is template-based. `vault_write` loads templates from `05-Templates/`, substitutes Templater-compatible variables (`<% tp.date.now("YYYY-MM-DD") %>`, `<% tp.file.title %>`), and validates required frontmatter fields (`type`, `created`, `tags`). Optional frontmatter fields `status`, `priority`, `project`, `deciders`, `due`, `source`can be set per template type. Task notes enforce enum validation on `status` (pending/active/done/cancelled) and `priority` (low/normal/high/urgent).
320
+ **Knowledge creation** is template-based. `vault_write` loads templates from `05-Templates/`, substitutes Templater-compatible variables (`<% tp.date.now("YYYY-MM-DD") %>`, `<% tp.file.title %>`), and validates required frontmatter fields (`type`, `created`, `tags`). This ensures every note in your vault has consistent metadatamaking it queryable, sortable, and discoverable from day one. Task notes enforce enum validation on `status` and `priority`; other types accept `project`, `deciders`, `due`, and `source`.
321
+
322
+ **Knowledge discovery** works at two levels. Keyword search (`vault_search`) finds exact terms. Semantic search embeds notes using OpenAI and finds conceptually related content — so "managing overwhelm" surfaces notes about "cognitive load" even if those exact words never appear together. The semantic index watches for file changes in real-time and syncs across machines via Obsidian Sync.
292
323
 
293
- **Semantic search** embeds notes on startup and watches for changes via `fs.watch`. Long notes are chunked by `##` headings. The index is a regenerable cache stored in `.obsidian/` so it syncs across machines via Obsidian Sync. The initial sync runs in the background search is available immediately but may return incomplete results until sync finishes (a progress message is shown).
324
+ **Knowledge connections** are maintained through Obsidian's `[[wikilink]]` graph. `vault_neighborhood` traverses links via BFS to discover related notes by proximity, while `vault_suggest_links` recommends connections you haven't made yet. `vault_move` rewrites wikilinks across the vault when you reorganize, and `vault_trash` warns about links that would break.
294
325
 
295
- **Graph exploration** resolves `[[wikilinks]]` to file paths (handling aliases, headings, and ambiguous basenames), then does BFS traversal to return notes grouped by hop distance.
326
+ **Session memory** records every tool call with timestamps and session IDs, so Claude can recall what was read, written, or searched in previous conversations. This turns ephemeral chat sessions into a continuous thread of work.
296
327
 
297
- **Activity logging** records every tool call (except `vault_activity` itself) with timestamps and session IDs, enabling Claude to recall what happened in previous conversations.
328
+ **Passive capture** uses `vault_capture` to signal that something is worth persisting (a decision, task, research finding, or bug). The tool returns immediately — a PostToolUse hook spawns a background agent that creates the structured vault note. Combined with the Stop hook (which sweeps each session for un-captured knowledge), this keeps your vault up to date without interrupting the coding flow.
298
329
 
299
- **Passive capture** uses `vault_capture` to signal that something is worth persisting (a decision, task, research finding, or bug). The tool returns immediately a PostToolUse hook spawns a background agent that creates the structured vault note. Combined with the Stop hook (which sweeps each session for un-captured decisions and tasks), this keeps the vault up to date without interrupting the coding flow.
330
+ **Fuzzy path resolution** lets read-only tools accept short names instead of full vault paths. `vault_read({ path: "devlog" })` resolves to `01-Projects/MyApp/development/devlog.md` automatically (`.md` extension optional). Folder-scoped tools like `vault_search` and `vault_query` accept partial folder names `folder: "MyApp"` resolves to `01-Projects/MyApp`. Ambiguous matches return an error listing candidates. Write/destructive tools always require exact paths.
300
331
 
301
332
  ## Troubleshooting
302
333
 
@@ -304,13 +335,13 @@ All paths passed to tools are relative to vault root. The server includes path s
304
335
  You need C++ build tools. See [Prerequisites](#prerequisites) for your platform. On Linux, `sudo apt install build-essential python3` usually fixes it.
305
336
 
306
337
  **Server starts but all tool calls fail with ENOENT**
307
- Your `VAULT_PATH` is wrong or missing. The server validates this at startup and exits with a clear error. Re-register with the correct path: `claude mcp remove obsidian-pkm && claude mcp add -s user -e VAULT_PATH=/correct/path -- obsidian-pkm npx -y pkm-mcp-server@latest`
338
+ Your `VAULT_PATH` is wrong or missing. The server validates this at startup and exits with a clear error. Re-register with the correct path: `claude mcp remove obsidian-pkm && claude mcp add -s user -e VAULT_PATH=/correct/path -- obsidian-pkm npx -y obsidian-pkm@latest`
308
339
 
309
340
  **`vault_write` says "no templates available"**
310
- Run `pkm-mcp-server init` to install templates, or copy the `templates/` files from this repo into your vault's `05-Templates/` directory. The server loads templates from there at startup.
341
+ Run `obsidian-pkm init` to install templates, or copy the `templates/` files from this repo into your vault's `05-Templates/` directory. The server loads templates from there at startup.
311
342
 
312
343
  **Semantic search not appearing in tool list**
313
- Set `OPENAI_API_KEY` in your MCP server registration. See [Enable Semantic Search](#3-enable-semantic-search-optional). Without it, `vault_semantic_search` and `vault_suggest_links` are hidden entirely.
344
+ Set `OPENAI_API_KEY` in your MCP server registration. See [Enable Semantic Search](#4-enable-semantic-search-optional). Without it, `vault_semantic_search` and `vault_suggest_links` are hidden entirely.
314
345
 
315
346
  **Server not showing up in Claude Code after install**
316
347
  Run `claude mcp list` to check. If `obsidian-pkm` is missing, register it with `claude mcp add` (see [Manual Registration](#2-manual-registration-alternative)). Note: editing `~/.claude/settings.json` directly does **not** register MCP servers — use the CLI.
package/cli.js CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  import { createRequire } from "module";
4
4
 
5
+ console.warn("");
6
+ console.warn(" pkm-mcp-server has been renamed to obsidian-pkm.");
7
+ console.warn(" Install the new version:");
8
+ console.warn(" Marketplace: claude plugin marketplace add AdrianV101/obsidian-pkm-plugin");
9
+ console.warn(" npm: npm install -g obsidian-pkm");
10
+ console.warn(" Then uninstall the old package: npm uninstall -g pkm-mcp-server");
11
+ console.warn("");
12
+
5
13
  const subcommand = process.argv[2];
6
14
 
7
15
  try {
@@ -11,13 +19,13 @@ try {
11
19
  } else if (subcommand === "--version" || subcommand === "-v") {
12
20
  const require = createRequire(import.meta.url);
13
21
  const { version } = require("./package.json");
14
- console.log(`pkm-mcp-server v${version}`);
22
+ console.log(`obsidian-pkm v${version}`);
15
23
  } else if (!subcommand) {
16
24
  const { startServer } = await import("./index.js");
17
25
  await startServer();
18
26
  } else {
19
27
  console.error(`Unknown command: ${subcommand}`);
20
- console.error("Usage: pkm-mcp-server [init]");
28
+ console.error("Usage: obsidian-pkm [init]");
21
29
  process.exit(1);
22
30
  }
23
31
  } catch (e) {
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: setup
3
+ description: Configure Obsidian PKM plugin — set vault path, API keys, and verify setup
4
+ allowed-tools: [Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion]
5
+ ---
6
+
7
+ # Obsidian PKM Setup
8
+
9
+ You are configuring the Obsidian PKM plugin. Walk the user through these steps:
10
+
11
+ ## Step 1: Vault Path
12
+
13
+ Ask the user for their Obsidian vault path. Validate that:
14
+ - The path exists and is a directory
15
+ - It contains at least one `.md` file
16
+ - Suggest `~/Documents/PKM` as a default
17
+
18
+ If the path is valid, instruct the user to add `export VAULT_PATH="/absolute/path/to/vault"` to their shell profile (`~/.zshrc` or `~/.bashrc`) if not already set. Verify with `echo $VAULT_PATH`.
19
+
20
+ ## Step 2: OpenAI API Key (Optional)
21
+
22
+ Ask if they want semantic search features (vault_semantic_search, vault_suggest_links). If yes:
23
+ - Ask for their OpenAI API key
24
+ - Instruct them to add `export OPENAI_API_KEY="sk-..."` to their shell profile
25
+ - Explain this enables 2 additional tools but is completely optional
26
+
27
+ ## Step 3: Verify Setup
28
+
29
+ Run these checks:
30
+ 1. `echo $VAULT_PATH` — confirm it's set
31
+ 2. Count `.md` files in the vault: `find "$VAULT_PATH" -name "*.md" | wc -l`
32
+ 3. Check if templates exist: `ls "$VAULT_PATH/05-Templates/"` — if missing, offer to run `obsidian-pkm init` to scaffold the vault structure
33
+
34
+ ## Step 4: Migration Check
35
+
36
+ Check if the old `pkm-mcp-server` is installed:
37
+ 1. Run `npm list -g pkm-mcp-server --depth=0 2>/dev/null`
38
+ 2. Check for stale hooks: `ls ~/.claude/hooks/pkm/ 2>/dev/null`
39
+
40
+ If found:
41
+ - Suggest: `npm uninstall -g pkm-mcp-server`
42
+ - Suggest removing `~/.claude/hooks/pkm/` directory (hooks are now managed by the plugin)
43
+ - Check `~/.claude/settings.json` for old hook entries and offer to clean them up
44
+
45
+ ## Step 5: Done
46
+
47
+ Confirm setup is complete. Tell the user:
48
+ - "Your Obsidian PKM plugin is configured. Try asking me to list your vault folders to verify."
49
+ - If OPENAI_API_KEY was set: "Semantic search will build its index in the background on first use."
50
+ - **Important**: "If you just added environment variables to your shell profile, you'll need to restart your Claude Code session (or run `source ~/.zshrc`) for the MCP server and hooks to pick them up."
package/handlers.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  FORCE_HARD_CAP,
24
24
  CHUNK_SIZE,
25
25
  } from "./helpers.js";
26
- import { exploreNeighborhood, formatNeighborhood, findFilesLinkingTo, rewriteWikilinks } from "./graph.js";
26
+ import { exploreNeighborhood, formatNeighborhood, findFilesLinkingTo, rewriteWikilinks, extractWikilinks } from "./graph.js";
27
27
  import { getAllMarkdownFiles, extractFrontmatter } from "./utils.js";
28
28
 
29
29
  /**
@@ -63,6 +63,26 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
63
63
  return resolvePath(resolvedFolder);
64
64
  };
65
65
 
66
+ const SESSION_ID_DISPLAY_LEN = 8;
67
+
68
+ function addToBasenameMap(relativePath) {
69
+ const bn = path.basename(relativePath, ".md").toLowerCase();
70
+ if (!basenameMap.has(bn)) basenameMap.set(bn, []);
71
+ basenameMap.get(bn).push(relativePath);
72
+ allFilesSet.add(relativePath);
73
+ }
74
+
75
+ function removeFromBasenameMap(relativePath) {
76
+ allFilesSet.delete(relativePath);
77
+ const bn = path.basename(relativePath, ".md").toLowerCase();
78
+ const entries = basenameMap.get(bn);
79
+ if (entries) {
80
+ const idx = entries.indexOf(relativePath);
81
+ if (idx !== -1) entries.splice(idx, 1);
82
+ if (entries.length === 0) basenameMap.delete(bn);
83
+ }
84
+ }
85
+
66
86
  async function handleRead(args) {
67
87
  const filePath = resolveFile(args.path);
68
88
  const content = await fs.readFile(filePath, "utf-8");
@@ -193,12 +213,7 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
193
213
  }
194
214
 
195
215
  // Update basename map with the new file
196
- const newBasename = path.basename(outputPath, ".md").toLowerCase();
197
- if (!basenameMap.has(newBasename)) {
198
- basenameMap.set(newBasename, []);
199
- }
200
- basenameMap.get(newBasename).push(outputPath);
201
- allFilesSet.add(outputPath);
216
+ addToBasenameMap(outputPath);
202
217
 
203
218
  const fm = validation.frontmatter;
204
219
  const createdStr = fm.created instanceof Date
@@ -403,11 +418,7 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
403
418
  const result = { outgoing: [], incoming: [] };
404
419
 
405
420
  if (args.direction !== "incoming") {
406
- const linkRegex = /\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g;
407
- let match;
408
- while ((match = linkRegex.exec(content)) !== null) {
409
- result.outgoing.push(match[1]);
410
- }
421
+ result.outgoing = extractWikilinks(content);
411
422
  }
412
423
 
413
424
  if (args.direction !== "outgoing") {
@@ -580,20 +591,20 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
580
591
 
581
592
  if (entries.length === 0) {
582
593
  return {
583
- content: [{ type: "text", text: `No activity entries found. (current session: ${sessionId.slice(0, 8)})` }]
594
+ content: [{ type: "text", text: `No activity entries found. (current session: ${sessionId.slice(0, SESSION_ID_DISPLAY_LEN)})` }]
584
595
  };
585
596
  }
586
597
 
587
598
  const formatted = entries.map(e => {
588
599
  const ts = e.timestamp.replace("T", " ").slice(0, 19);
589
- const sessionShort = e.session_id.slice(0, 8);
600
+ const sessionShort = e.session_id.slice(0, SESSION_ID_DISPLAY_LEN);
590
601
  return `[${ts}] [${sessionShort}] ${e.tool_name}\n${e.args_json}`;
591
602
  }).join("\n\n");
592
603
 
593
604
  return {
594
605
  content: [{
595
606
  type: "text",
596
- text: `Activity log (${entries.length} entr${entries.length === 1 ? "y" : "ies"}, current session: ${sessionId.slice(0, 8)}):\n\n${formatted}`
607
+ text: `Activity log (${entries.length} entr${entries.length === 1 ? "y" : "ies"}, current session: ${sessionId.slice(0, SESSION_ID_DISPLAY_LEN)}):\n\n${formatted}`
597
608
  }]
598
609
  };
599
610
  }
@@ -651,13 +662,9 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
651
662
  }
652
663
  if (!body) throw new Error("No content to analyze");
653
664
 
654
- const linkRegex = /\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g;
655
- const linkedNames = new Set();
656
- let match;
657
- while ((match = linkRegex.exec(inputText)) !== null) {
658
- const target = match[1];
659
- linkedNames.add(path.basename(target, ".md").toLowerCase());
660
- }
665
+ const linkedNames = new Set(
666
+ extractWikilinks(inputText).map(t => path.basename(t, ".md").toLowerCase())
667
+ );
661
668
 
662
669
  const excludeFiles = new Set();
663
670
  if (sourcePath) excludeFiles.add(sourcePath);
@@ -696,7 +703,12 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
696
703
  const filePath = resolvePath(resolvedRelative);
697
704
 
698
705
  // Verify file exists
699
- await fs.access(filePath);
706
+ try {
707
+ await fs.access(filePath);
708
+ } catch (e) {
709
+ if (e.code === "ENOENT") throw new Error(`ENOENT: File not found: ${resolvedRelative}`, { cause: e });
710
+ throw e;
711
+ }
700
712
 
701
713
  // Find incoming links for warning output
702
714
  const allFilesList = Array.from(allFilesSet);
@@ -725,14 +737,7 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
725
737
  await fs.rename(filePath, trashAbsolute);
726
738
 
727
739
  // Update in-memory basename map
728
- allFilesSet.delete(resolvedRelative);
729
- const oldBasename = path.basename(resolvedRelative, ".md").toLowerCase();
730
- const entries = basenameMap.get(oldBasename);
731
- if (entries) {
732
- const idx = entries.indexOf(resolvedRelative);
733
- if (idx !== -1) entries.splice(idx, 1);
734
- if (entries.length === 0) basenameMap.delete(oldBasename);
735
- }
740
+ removeFromBasenameMap(resolvedRelative);
736
741
 
737
742
  // Build output
738
743
  let text = `Trashed ${resolvedRelative} → ${trashRelative}`;
@@ -754,7 +759,12 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
754
759
  const newAbsolute = resolvePath(newRelative);
755
760
 
756
761
  // Verify source exists
757
- await fs.access(oldAbsolute);
762
+ try {
763
+ await fs.access(oldAbsolute);
764
+ } catch (e) {
765
+ if (e.code === "ENOENT") throw new Error(`ENOENT: File not found: ${oldRelative}`, { cause: e });
766
+ throw e;
767
+ }
758
768
 
759
769
  // Verify destination does NOT exist
760
770
  try {
@@ -775,23 +785,11 @@ export async function createHandlers({ vaultPath, templateRegistry, semanticInde
775
785
  await fs.rename(oldAbsolute, newAbsolute);
776
786
 
777
787
  // Update basename map: remove old, add new
778
- allFilesSet.delete(oldRelative);
779
- const oldBasename = path.basename(oldRelative, ".md").toLowerCase();
780
- const oldEntries = basenameMap.get(oldBasename);
781
- if (oldEntries) {
782
- const idx = oldEntries.indexOf(oldRelative);
783
- if (idx !== -1) oldEntries.splice(idx, 1);
784
- if (oldEntries.length === 0) basenameMap.delete(oldBasename);
785
- }
786
-
787
- allFilesSet.add(newRelative);
788
- const newBasename = path.basename(newRelative, ".md").toLowerCase();
789
- if (!basenameMap.has(newBasename)) {
790
- basenameMap.set(newBasename, []);
791
- }
792
- basenameMap.get(newBasename).push(newRelative);
788
+ removeFromBasenameMap(oldRelative);
789
+ addToBasenameMap(newRelative);
793
790
 
794
791
  // Determine new link target — use full path if basename is now ambiguous
792
+ const newBasename = path.basename(newRelative, ".md").toLowerCase();
795
793
  const newEntries = basenameMap.get(newBasename);
796
794
  const isAmbiguous = newEntries && newEntries.length > 1;
797
795
  const newLinkTarget = isAmbiguous
package/hooks/README.md CHANGED
@@ -39,7 +39,7 @@ Add the following to your `~/.claude/settings.json`:
39
39
  "hooks": [
40
40
  {
41
41
  "type": "command",
42
- "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/Obsidian-MCP/hooks/session-start.js",
42
+ "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/obsidian-pkm-plugin/hooks/session-start.js",
43
43
  "timeout": 15,
44
44
  "statusMessage": "Loading PKM project context..."
45
45
  }
@@ -51,7 +51,7 @@ Add the following to your `~/.claude/settings.json`:
51
51
  "hooks": [
52
52
  {
53
53
  "type": "command",
54
- "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/Obsidian-MCP/hooks/stop-sweep.js",
54
+ "command": "VAULT_PATH=\"/path/to/your/vault\" node /path/to/obsidian-pkm-plugin/hooks/stop-sweep.js",
55
55
  "async": true,
56
56
  "timeout": 10
57
57
  }
@@ -64,7 +64,7 @@ Add the following to your `~/.claude/settings.json`:
64
64
  "hooks": [
65
65
  {
66
66
  "type": "command",
67
- "command": "VAULT_PATH=\"/path/to/your/vault\" /path/to/Obsidian-MCP/hooks/capture-handler.sh",
67
+ "command": "VAULT_PATH=\"/path/to/your/vault\" /path/to/obsidian-pkm-plugin/hooks/capture-handler.sh",
68
68
  "async": true,
69
69
  "timeout": 10
70
70
  }
@@ -75,7 +75,7 @@ Add the following to your `~/.claude/settings.json`:
75
75
  }
76
76
  ```
77
77
 
78
- Replace `/path/to/your/vault` with the absolute path to your Obsidian vault (e.g., `~/Documents/PKM`), and `/path/to/Obsidian-MCP` with the absolute path to this repository.
78
+ Replace `/path/to/your/vault` with the absolute path to your Obsidian vault (e.g., `~/Documents/PKM`), and `/path/to/obsidian-pkm-plugin` with the absolute path to this repository.
79
79
 
80
80
  ## Architecture Notes
81
81
 
@@ -15,19 +15,23 @@ trap cleanup EXIT
15
15
  # Read hook input from stdin
16
16
  INPUT=$(cat)
17
17
 
18
- # Extract tool_input fields (buffer all stdin before parsing)
19
- eval "$(echo "$INPUT" | node -e "
20
- let b='';
21
- process.stdin.on('data',c=>b+=c);
22
- process.stdin.on('end',()=>{
23
- const j=JSON.parse(b);
24
- const ti=j.tool_input||{};
25
- console.log('TOOL_INPUT='+JSON.stringify(JSON.stringify(ti)));
26
- console.log('CAPTURE_TYPE='+JSON.stringify(ti.type||''));
27
- console.log('CAPTURE_TITLE='+JSON.stringify(ti.title||''));
28
- console.log('CAPTURE_CONTENT='+JSON.stringify(ti.content||''));
29
- })
30
- ")"
18
+ # Extract tool_input fields safely (no eval stdout capture only)
19
+ TOOL_INPUT=$(echo "$INPUT" | node -e "
20
+ let b=''; process.stdin.on('data',c=>b+=c);
21
+ process.stdin.on('end',()=>{ process.stdout.write(JSON.stringify(JSON.parse(b).tool_input||{})); })
22
+ ")
23
+ CAPTURE_TYPE=$(echo "$INPUT" | node -e "
24
+ let b=''; process.stdin.on('data',c=>b+=c);
25
+ process.stdin.on('end',()=>{ process.stdout.write((JSON.parse(b).tool_input||{}).type||''); })
26
+ ")
27
+ CAPTURE_TITLE=$(echo "$INPUT" | node -e "
28
+ let b=''; process.stdin.on('data',c=>b+=c);
29
+ process.stdin.on('end',()=>{ process.stdout.write((JSON.parse(b).tool_input||{}).title||''); })
30
+ ")
31
+ CAPTURE_CONTENT=$(echo "$INPUT" | node -e "
32
+ let b=''; process.stdin.on('data',c=>b+=c);
33
+ process.stdin.on('end',()=>{ process.stdout.write((JSON.parse(b).tool_input||{}).content||''); })
34
+ ")
31
35
 
32
36
  # Skip if missing required fields
33
37
  if [ -z "$CAPTURE_TYPE" ] || [ -z "$CAPTURE_TITLE" ] || [ -z "$CAPTURE_CONTENT" ]; then
@@ -46,7 +50,7 @@ MCP_CONFIG=$(node -e "
46
50
  if (process.env.OPENAI_API_KEY) env.OPENAI_API_KEY = process.env.OPENAI_API_KEY;
47
51
  const server = useLocal
48
52
  ? { command: 'node', args: [localIndex], env }
49
- : { command: 'npx', args: ['-y', 'pkm-mcp-server@latest'], env };
53
+ : { command: 'npx', args: ['-y', 'obsidian-pkm@latest'], env };
50
54
  console.log(JSON.stringify({ mcpServers: { 'obsidian-pkm': server } }));
51
55
  " "$SCRIPT_DIR" "${VAULT_PATH:-$HOME/Documents/PKM}")
52
56
 
@@ -0,0 +1,35 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|clear|compact",
6
+ "hooks": [{
7
+ "type": "command",
8
+ "command": "VAULT_PATH=${VAULT_PATH} node ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.js",
9
+ "timeout": 15
10
+ }]
11
+ }
12
+ ],
13
+ "PostToolUse": [
14
+ {
15
+ "matcher": "mcp__obsidian-pkm__vault_capture",
16
+ "hooks": [{
17
+ "type": "command",
18
+ "command": "VAULT_PATH=${VAULT_PATH} bash ${CLAUDE_PLUGIN_ROOT}/hooks/capture-handler.sh",
19
+ "timeout": 30,
20
+ "async": true
21
+ }]
22
+ }
23
+ ],
24
+ "Stop": [
25
+ {
26
+ "hooks": [{
27
+ "type": "command",
28
+ "command": "VAULT_PATH=${VAULT_PATH} node ${CLAUDE_PLUGIN_ROOT}/hooks/stop-sweep.js",
29
+ "timeout": 15,
30
+ "async": true
31
+ }]
32
+ }
33
+ ]
34
+ }
35
+ }
@@ -61,7 +61,11 @@ async function main() {
61
61
 
62
62
  // Resolve project — no project, no captures
63
63
  const { projectPath, error } = await resolveProject(cwd, VAULT_PATH);
64
- if (error || !projectPath) return;
64
+ if (error) {
65
+ logError(`project resolution failed: ${error}`);
66
+ return;
67
+ }
68
+ if (!projectPath) return;
65
69
 
66
70
  // Build MCP config — auto-detect repo (../index.js exists) vs installed (use npx)
67
71
  const localIndex = join(__dirname, "..", "index.js");
@@ -74,7 +78,7 @@ async function main() {
74
78
  mcpServers: {
75
79
  "obsidian-pkm": useLocal
76
80
  ? { command: "node", args: [localIndex], env: mcpEnv }
77
- : { command: "npx", args: ["-y", "pkm-mcp-server@latest"], env: mcpEnv },
81
+ : { command: "npx", args: ["-y", "obsidian-pkm@latest"], env: mcpEnv },
78
82
  },
79
83
  });
80
84
 
package/index.js CHANGED
@@ -38,7 +38,7 @@ export async function startServer() {
38
38
 
39
39
  // Create the server
40
40
  const server = new Server(
41
- { name: "pkm-mcp-server", version: PKG_VERSION },
41
+ { name: "obsidian-pkm", version: PKG_VERSION },
42
42
  { capabilities: { tools: {} } }
43
43
  );
44
44
 
@@ -73,7 +73,7 @@ export async function startServer() {
73
73
  },
74
74
  {
75
75
  name: "vault_peek",
76
- description: "Inspect a file's metadata and structure without reading full content. Returns file size, frontmatter, heading outline with line numbers and approximate section sizes, and a brief preview. Use this to plan which sections to read from large files.",
76
+ description: "Inspect a file's metadata and structure without reading full content. Returns file size, frontmatter, heading outline with approximate section sizes, and a brief preview. Use this to plan which sections to read from large files.",
77
77
  inputSchema: {
78
78
  type: "object",
79
79
  properties: {
package/init.js CHANGED
@@ -192,7 +192,7 @@ export async function dirSize(dirPath) {
192
192
  export function detectInstallType(filePath) {
193
193
  const thisFile = filePath || fileURLToPath(import.meta.url);
194
194
  if (thisFile.includes("node_modules")) {
195
- return { command: "npx", args: ["-y", "pkm-mcp-server@latest"] };
195
+ return { command: "npx", args: ["-y", "obsidian-pkm@latest"] };
196
196
  }
197
197
  const cliPath = path.join(path.dirname(thisFile), "cli.js");
198
198
  return { command: "node", args: [cliPath] };
@@ -394,7 +394,7 @@ export async function runInit() {
394
394
  try {
395
395
  // ── Step 1: Welcome ──
396
396
  console.log(`
397
- pkm-mcp-server setup wizard
397
+ obsidian-pkm setup wizard
398
398
 
399
399
  This will walk you through setting up your Obsidian vault for use with the
400
400
  PKM MCP server. You'll be asked about 6 things:
@@ -508,7 +508,7 @@ Nothing is written until you confirm each step. Press Ctrl+C at any time to canc
508
508
 
509
509
  let skipRegistration = false;
510
510
  if (hasExisting) {
511
- const overwrite = await confirmPrompt({ message: "Claude Code is already configured for pkm-mcp-server. Overwrite?", default: false });
511
+ const overwrite = await confirmPrompt({ message: "Claude Code is already configured for obsidian-pkm. Overwrite?", default: false });
512
512
  if (!overwrite) {
513
513
  console.log(" Registration skipped.\n");
514
514
  skipRegistration = true;
@@ -548,7 +548,7 @@ Nothing is written until you confirm each step. Press Ctrl+C at any time to canc
548
548
  steps.push("MCP server: skipped (registration failed)");
549
549
  }
550
550
  } else {
551
- console.log(" Registration: skipped (you can run `pkm-mcp-server init` again later)");
551
+ console.log(" Registration: skipped (you can run `obsidian-pkm init` again later)");
552
552
  steps.push("MCP server: skipped");
553
553
  }
554
554
  } else {
@@ -678,7 +678,7 @@ To verify, restart Claude Code and try:
678
678
  "List the folders in my vault"
679
679
 
680
680
  Claude should call vault_list and show your vault's directory structure.
681
- If that doesn't work, check: https://github.com/AdrianV101/Obsidian-MCP#troubleshooting
681
+ If that doesn't work, check: https://github.com/AdrianV101/obsidian-pkm-plugin#troubleshooting
682
682
  `);
683
683
  } catch (e) {
684
684
  if (e.name === "ExitPromptError") {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "pkm-mcp-server",
3
- "version": "1.5.3",
4
- "description": "MCP server for Obsidian vault integration with Claude Code — 19 tools for notes, search, and graph traversal",
3
+ "version": "1.7.0",
4
+ "description": "Claude Code plugin for Obsidian vault integration — 19 MCP tools, hooks, and skills for PKM",
5
5
  "main": "cli.js",
6
6
  "exports": {
7
7
  ".": "./cli.js"
8
8
  },
9
9
  "bin": {
10
- "pkm-mcp-server": "cli.js"
10
+ "obsidian-pkm": "cli.js"
11
11
  },
12
12
  "type": "module",
13
13
  "engines": {
@@ -19,7 +19,11 @@
19
19
  "CHANGELOG.md",
20
20
  "hooks/",
21
21
  "templates/",
22
- "sample-project/"
22
+ "sample-project/",
23
+ ".claude-plugin/",
24
+ "commands/",
25
+ "skills/",
26
+ ".mcp.json"
23
27
  ],
24
28
  "scripts": {
25
29
  "start": "node cli.js",
@@ -29,7 +33,7 @@
29
33
  },
30
34
  "repository": {
31
35
  "type": "git",
32
- "url": "git+https://github.com/AdrianV101/Obsidian-MCP.git"
36
+ "url": "git+https://github.com/AdrianV101/obsidian-pkm-plugin.git"
33
37
  },
34
38
  "author": "Adrian Verhoosel",
35
39
  "license": "MIT",
@@ -45,15 +49,17 @@
45
49
  "markdown",
46
50
  "notes",
47
51
  "semantic-search",
48
- "wikilinks"
52
+ "wikilinks",
53
+ "plugin",
54
+ "hooks"
49
55
  ],
50
- "homepage": "https://github.com/AdrianV101/Obsidian-MCP#readme",
56
+ "homepage": "https://github.com/AdrianV101/obsidian-pkm-plugin#readme",
51
57
  "bugs": {
52
- "url": "https://github.com/AdrianV101/Obsidian-MCP/issues"
58
+ "url": "https://github.com/AdrianV101/obsidian-pkm-plugin/issues"
53
59
  },
54
60
  "dependencies": {
55
61
  "@inquirer/prompts": "^8.3.2",
56
- "@modelcontextprotocol/sdk": "^1.0.0",
62
+ "@modelcontextprotocol/sdk": "^1.27.0",
57
63
  "better-sqlite3": "^12.6.2",
58
64
  "js-yaml": "^4.1.0",
59
65
  "sqlite-vec": "^0.1.7"
@@ -185,6 +185,7 @@ Use these with `vault_write({ template: "name", path: "...", frontmatter: { tags
185
185
  | `moc` | Maps of Content (index/hub notes) |
186
186
  | `daily-note` | Daily notes |
187
187
  | `task` | Structured task notes with status, priority, due date |
188
+ | `note` | Minimal generic notes |
188
189
 
189
190
  ## Session End
190
191
 
File without changes