pkm-mcp-server 1.3.0 → 1.3.2

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +33 -40
  3. package/init.js +88 -95
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.3.2] - 2026-03-21
10
+
11
+ ### Fixed
12
+ - Fix `claude mcp add` argument ordering — the `-e` flag is variadic and was greedily consuming the server name. Moved `--` separator before the server name to terminate option parsing.
13
+
14
+ ## [1.3.1] - 2026-03-21
15
+
16
+ ### Fixed
17
+ - `pkm-mcp-server init` now correctly registers the MCP server with Claude Code via `claude mcp add` instead of writing to the wrong config file (`~/.claude/settings.json`). Previously, the server appeared to register successfully but was never visible to Claude Code.
18
+
9
19
  ## [1.3.0] - 2026-03-19
10
20
 
11
21
  ### Added
package/README.md CHANGED
@@ -82,69 +82,59 @@ Ambiguous matches return an error listing candidates. Exact paths always work un
82
82
 
83
83
  ## Quick Start
84
84
 
85
- ### 1. Install
85
+ ### 1. Install and Set Up
86
86
 
87
87
  **From npm** (recommended):
88
88
 
89
89
  ```bash
90
90
  npm install -g pkm-mcp-server
91
+ pkm-mcp-server init
91
92
  ```
92
93
 
94
+ The setup wizard walks you through:
95
+ 1. Vault path (existing or new)
96
+ 2. Note templates (full set, minimal, or skip)
97
+ 3. PARA folder structure
98
+ 4. OpenAI API key for semantic search (optional)
99
+ 5. Automatic registration with Claude Code
100
+
101
+ Restart Claude Code after setup. The server provides all tools except semantic search out of the box.
102
+
93
103
  **From source:**
94
104
 
95
105
  ```bash
96
106
  git clone https://github.com/AdrianV101/Obsidian-MCP.git
97
107
  cd Obsidian-MCP
98
108
  npm install
109
+ node cli.js init
99
110
  ```
100
111
 
101
- ### 2. Register with Claude Code
102
-
103
- Add to `~/.claude/settings.json`:
112
+ ### 2. Manual Registration (alternative)
104
113
 
105
- **If installed from npm:**
114
+ If you prefer to skip the wizard, register directly with the Claude CLI:
106
115
 
107
- ```json
108
- {
109
- "mcpServers": {
110
- "obsidian-pkm": {
111
- "command": "npx",
112
- "args": ["-y", "pkm-mcp-server"],
113
- "env": {
114
- "VAULT_PATH": "/absolute/path/to/your/obsidian/vault"
115
- }
116
- }
117
- }
118
- }
116
+ ```bash
117
+ claude mcp add -s user -e VAULT_PATH=/absolute/path/to/your/vault -- obsidian-pkm npx -y pkm-mcp-server
119
118
  ```
120
119
 
121
- **If installed from source:**
120
+ For a source install:
122
121
 
123
- ```json
124
- {
125
- "mcpServers": {
126
- "obsidian-pkm": {
127
- "command": "node",
128
- "args": ["/absolute/path/to/index.js"],
129
- "env": {
130
- "VAULT_PATH": "/absolute/path/to/your/obsidian/vault"
131
- }
132
- }
133
- }
134
- }
122
+ ```bash
123
+ claude mcp add -s user -e VAULT_PATH=/absolute/path/to/your/vault -- obsidian-pkm node /absolute/path/to/cli.js
135
124
  ```
136
125
 
137
- Restart Claude Code. The server provides all tools except semantic search out of the box.
126
+ Verify with `claude mcp list` you should see `obsidian-pkm: ... - Connected`.
138
127
 
139
128
  ### 3. Enable Semantic Search (optional)
140
129
 
141
- Add your OpenAI API key to the env block:
130
+ If you didn't set this up during `init`, add your OpenAI API key by re-registering:
142
131
 
143
- ```json
144
- "env": {
145
- "VAULT_PATH": "/absolute/path/to/your/obsidian/vault",
146
- "OPENAI_API_KEY": "sk-..."
147
- }
132
+ ```bash
133
+ claude mcp remove obsidian-pkm
134
+ claude mcp add -s user \
135
+ -e VAULT_PATH=/absolute/path/to/your/vault \
136
+ -e OPENAI_API_KEY=sk-... \
137
+ -- obsidian-pkm npx -y pkm-mcp-server
148
138
  ```
149
139
 
150
140
  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.
@@ -290,13 +280,16 @@ All paths passed to tools are relative to vault root. The server includes path s
290
280
  You need C++ build tools. See [Prerequisites](#prerequisites) for your platform. On Linux, `sudo apt install build-essential python3` usually fixes it.
291
281
 
292
282
  **Server starts but all tool calls fail with ENOENT**
293
- Your `VAULT_PATH` is wrong or missing. The server now validates this at startup and exits with a clear error. Set it explicitly in your `settings.json` env block.
283
+ 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`
294
284
 
295
285
  **`vault_write` says "no templates available"**
296
- Copy the `templates/` files from this repo into your vault's `05-Templates/` directory. The server loads templates from there at startup.
286
+ 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.
297
287
 
298
288
  **Semantic search not appearing in tool list**
299
- Set `OPENAI_API_KEY` in your `settings.json` env block. Without it, `vault_semantic_search` and `vault_suggest_links` are hidden entirely.
289
+ 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.
290
+
291
+ **Server not showing up in Claude Code after install**
292
+ 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.
300
293
 
301
294
  **Semantic index not updating after file changes**
302
295
  Check your Node version with `node -v`. The file watcher uses `fs.watch({ recursive: true })` which requires Node.js >= 18.13 on Linux.
package/init.js CHANGED
@@ -2,6 +2,10 @@ import os from "os";
2
2
  import path from "path";
3
3
  import fs from "fs/promises";
4
4
  import { fileURLToPath } from "url";
5
+ import { execFile as execFileCb } from "child_process";
6
+ import { promisify } from "util";
7
+
8
+ const execFileAsync = promisify(execFileCb);
5
9
 
6
10
  /**
7
11
  * Resolve user-provided path input: expand ~, $HOME, resolve relative, normalise.
@@ -119,41 +123,44 @@ export async function backupVault(vaultPath) {
119
123
  }
120
124
 
121
125
  /**
122
- * Read/merge/write settings.json atomically.
123
- * @param {string} settingsPath - Absolute path to settings.json
124
- * @param {object} serverConfig - The obsidian-pkm server config block
125
- * @returns {Promise<object>} The full merged config object
126
+ * Build argument array for `claude mcp add` command.
127
+ * @param {{ vaultPath: string, openaiKey: string|null, installType: { command: string, args: string[] } }} opts
128
+ * @returns {string[]}
126
129
  */
127
- export async function updateSettingsJson(settingsPath, serverConfig) {
128
- // Create parent directory
129
- await fs.mkdir(path.dirname(settingsPath), { recursive: true });
130
+ export function buildMcpAddArgs({ vaultPath, openaiKey, installType }) {
131
+ const args = ["mcp", "add", "-s", "user"];
132
+ args.push("-e", `VAULT_PATH=${vaultPath}`);
133
+ if (openaiKey) {
134
+ args.push("-e", `OPENAI_API_KEY=${openaiKey}`);
135
+ }
136
+ args.push("--", "obsidian-pkm", installType.command, ...installType.args);
137
+ return args;
138
+ }
130
139
 
131
- // Read existing or start fresh
132
- let config = {};
140
+ /**
141
+ * Check if the `claude` CLI is available on PATH.
142
+ * @returns {Promise<boolean>}
143
+ */
144
+ export async function checkClaudeCli() {
133
145
  try {
134
- const raw = await fs.readFile(settingsPath, "utf8");
135
- try {
136
- config = JSON.parse(raw);
137
- } catch {
138
- const err = new Error(`${settingsPath} is not valid JSON. Please fix it manually or delete it to start fresh.`);
139
- err.code = "INVALID_JSON";
140
- throw err;
141
- }
142
- } catch (e) {
143
- if (e.code !== "ENOENT") throw e;
144
- // File doesn't exist — start with {}
146
+ await execFileAsync("claude", ["--version"]);
147
+ return true;
148
+ } catch {
149
+ return false;
145
150
  }
151
+ }
146
152
 
147
- // Merge
148
- config.mcpServers = config.mcpServers || {};
149
- config.mcpServers["obsidian-pkm"] = serverConfig;
150
-
151
- // Atomic write
152
- const tmpPath = settingsPath + ".tmp";
153
- await fs.writeFile(tmpPath, JSON.stringify(config, null, 2) + "\n");
154
- await fs.rename(tmpPath, settingsPath);
155
-
156
- return config;
153
+ /**
154
+ * Check if obsidian-pkm is already registered in Claude Code.
155
+ * @returns {Promise<boolean>}
156
+ */
157
+ export async function checkExistingRegistration() {
158
+ try {
159
+ await execFileAsync("claude", ["mcp", "get", "obsidian-pkm"]);
160
+ return true;
161
+ } catch {
162
+ return false;
163
+ }
157
164
  }
158
165
 
159
166
  /**
@@ -207,7 +214,6 @@ export async function runInit() {
207
214
  const { confirm: confirmPrompt, input, select, password } = await import("@inquirer/prompts");
208
215
 
209
216
  const bundledTemplatesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "templates");
210
- const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
211
217
  const steps = [];
212
218
 
213
219
  try {
@@ -299,8 +305,8 @@ Nothing is written until you confirm each step. Press Ctrl+C at any time to canc
299
305
  console.log(`
300
306
  Get an API key at: https://platform.openai.com/api-keys
301
307
 
302
- This key is stored only in your local Claude Code settings file
303
- (~/.claude/settings.json) and is never sent to us or anyone else.
308
+ This key is stored only in your Claude Code configuration
309
+ (~/.claude.json) and is never sent to us or anyone else.
304
310
  It's used solely for generating text embeddings via OpenAI's API.
305
311
  `);
306
312
  openaiKey = await password({ message: "OpenAI API key (Enter to skip):", mask: "*" });
@@ -309,79 +315,66 @@ Nothing is written until you confirm each step. Press Ctrl+C at any time to canc
309
315
  console.log(" You can add this later by setting OPENAI_API_KEY in your Claude Code settings.\n");
310
316
  }
311
317
  // ── Step 6: Registration ──
312
- const installType = detectInstallType();
313
- const serverConfig = {
314
- command: installType.command,
315
- args: [...installType.args],
316
- env: { VAULT_PATH: vaultPath },
317
- };
318
- if (openaiKey) {
319
- serverConfig.env.OPENAI_API_KEY = openaiKey;
320
- }
318
+ const hasClaude = await checkClaudeCli();
319
+ if (!hasClaude) {
320
+ const installType = detectInstallType();
321
+ const manualCmd = `claude mcp add -s user -e VAULT_PATH=${vaultPath} -- obsidian-pkm ${installType.command} ${installType.args.join(" ")}`;
322
+ console.log(`
323
+ Claude Code CLI not found on PATH. To register manually, run:
321
324
 
322
- // Check for existing registration
323
- let hasExisting = false;
324
- try {
325
- const raw = await fs.readFile(settingsPath, "utf8");
326
- const existing = JSON.parse(raw);
327
- if (existing.mcpServers?.["obsidian-pkm"]) {
328
- hasExisting = true;
329
- }
330
- } catch (e) {
331
- if (e.code !== "ENOENT" && !(e instanceof SyntaxError)) {
332
- console.warn(` Warning: could not read ${settingsPath}: ${e.message}`);
325
+ ${manualCmd}
326
+ `);
327
+ steps.push("MCP server: skipped (Claude CLI not found)");
328
+ } else {
329
+ const installType = detectInstallType();
330
+ const hasExisting = await checkExistingRegistration();
331
+
332
+ let skipRegistration = false;
333
+ if (hasExisting) {
334
+ const overwrite = await confirmPrompt({ message: "Claude Code is already configured for pkm-mcp-server. Overwrite?", default: false });
335
+ if (!overwrite) {
336
+ console.log(" Registration skipped.\n");
337
+ skipRegistration = true;
338
+ } else {
339
+ // Remove existing before re-adding
340
+ try {
341
+ await execFileAsync("claude", ["mcp", "remove", "obsidian-pkm"]);
342
+ } catch (e) {
343
+ console.warn(` Warning: could not remove existing registration: ${e.message}`);
344
+ }
345
+ }
333
346
  }
334
- }
335
-
336
- let skipRegistration = false;
337
- if (hasExisting) {
338
- const overwrite = await confirmPrompt({ message: "Claude Code is already configured for pkm-mcp-server. Overwrite?", default: false });
339
- if (!overwrite) { console.log(" Registration skipped.\n"); skipRegistration = true; }
340
- }
341
347
 
342
- if (!skipRegistration) {
343
- // Show preview
344
- const previewObj = {
345
- mcpServers: {
346
- "obsidian-pkm": serverConfig,
347
- },
348
- };
349
- console.log(`\nWill write to ${settingsPath}:\n`);
350
- console.log(JSON.stringify(previewObj, null, 2));
351
- console.log("\nThis only adds the \"obsidian-pkm\" key under \"mcpServers\". No other settings will be changed.");
352
-
353
- const doRegister = await confirmPrompt({
354
- message: "Register MCP server with Claude Code?",
355
- default: true,
356
- });
357
-
358
- if (doRegister) {
359
- let registered = false;
360
- try {
361
- await updateSettingsJson(settingsPath, serverConfig);
362
- registered = true;
363
- } catch (regErr) {
364
- if (regErr.code === "INVALID_JSON") {
365
- console.error(`\n ${regErr.message}`);
348
+ if (!skipRegistration) {
349
+ const addArgs = buildMcpAddArgs({ vaultPath, openaiKey, installType });
350
+ const displayCmd = `claude ${addArgs.join(" ")}`;
351
+ console.log(`\nWill run:\n\n ${displayCmd}\n`);
352
+
353
+ const doRegister = await confirmPrompt({
354
+ message: "Register MCP server with Claude Code?",
355
+ default: true,
356
+ });
357
+
358
+ if (doRegister) {
359
+ try {
360
+ await execFileAsync("claude", addArgs);
361
+ console.log(" MCP server registered with Claude Code");
362
+ steps.push("MCP server: registered");
363
+ } catch (regErr) {
364
+ console.error(`\n Registration failed: ${regErr.message}`);
365
+ if (regErr.stderr) console.error(` ${regErr.stderr.trim()}`);
366
366
  const skipReg = await confirmPrompt({ message: "Skip registration and finish setup?", default: true });
367
367
  if (!skipReg) throw regErr;
368
368
  console.log(" Registration skipped.\n");
369
- } else {
370
- throw regErr;
369
+ steps.push("MCP server: skipped (registration failed)");
371
370
  }
372
- }
373
- if (registered) {
374
- console.log(" MCP server registered");
375
- steps.push("MCP server: registered");
376
371
  } else {
377
- steps.push("MCP server: skipped (malformed settings.json)");
372
+ console.log(" Registration: skipped (you can run `pkm-mcp-server init` again later)");
373
+ steps.push("MCP server: skipped");
378
374
  }
379
375
  } else {
380
- console.log(" Registration: skipped (you can run `pkm-mcp-server init` again later)");
381
376
  steps.push("MCP server: skipped");
382
377
  }
383
- } else {
384
- steps.push("MCP server: skipped");
385
378
  }
386
379
 
387
380
  // ── Step 7: Summary ──
@@ -398,7 +391,7 @@ Nothing is written until you confirm each step. Press Ctrl+C at any time to canc
398
391
  // Determine registration summary from steps
399
392
  const regStep = steps.find(s => s.startsWith("MCP server:"));
400
393
  const registrationSummary = regStep && regStep.includes("registered")
401
- ? `Registered in ${settingsPath}`
394
+ ? "Registered with Claude Code"
402
395
  : "Skipped";
403
396
 
404
397
  console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pkm-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "MCP server for Obsidian vault integration with Claude Code — 19 tools for notes, search, and graph traversal",
5
5
  "main": "cli.js",
6
6
  "exports": {