@plaud-ai/mcp 0.3.2 → 0.3.4

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PlaudClient,
3
3
  capture
4
- } from "./chunk-R5G6UXSI.js";
4
+ } from "./chunk-D2JZW2TW.js";
5
5
  import {
6
6
  httpClientDuration,
7
7
  httpClientRequests
@@ -1,9 +1,10 @@
1
1
  import {
2
- skillsCombined
3
- } from "./chunk-4QBEOJPX.js";
2
+ loadSkills,
3
+ skillsSourceDir
4
+ } from "./chunk-242FRP4P.js";
4
5
 
5
6
  // src/install-utils.ts
6
- import { readFile, writeFile, mkdir } from "fs/promises";
7
+ import { readFile, writeFile, mkdir, cp, rm } from "fs/promises";
7
8
  import { existsSync } from "fs";
8
9
  import { join, dirname } from "path";
9
10
  import { homedir, platform } from "os";
@@ -12,11 +13,12 @@ var SKILLS_MARKER_START = "<!-- plaud-skills:start -->";
12
13
  var SKILLS_MARKER_END = "<!-- plaud-skills:end -->";
13
14
  function findNodeBinCommand(command) {
14
15
  const commandName = platform() === "win32" ? `${command}.cmd` : command;
16
+ const sibling = join(dirname(process.execPath), commandName);
17
+ if (existsSync(sibling)) return sibling;
15
18
  const found = spawnSync(platform() === "win32" ? "where" : "which", [commandName], { encoding: "utf-8" });
16
19
  const firstMatch = found.status === 0 ? found.stdout.split(/\r?\n/).find((line) => line.trim())?.trim() : void 0;
17
20
  if (firstMatch) return firstMatch;
18
- const sibling = join(dirname(process.execPath), commandName);
19
- return existsSync(sibling) ? sibling : commandName;
21
+ return commandName;
20
22
  }
21
23
  function isLocalPackageSpec(spec) {
22
24
  return spec.endsWith(".tgz") || spec.startsWith(".") || spec.startsWith("/") || /^[A-Za-z]:[\\/]/.test(spec);
@@ -34,6 +36,12 @@ function getMcpEntry() {
34
36
  args: ["-y", packageSpec]
35
37
  };
36
38
  }
39
+ function commandPathIsStale(command) {
40
+ if (!command) return false;
41
+ if (command.includes("fnm_multishells")) return true;
42
+ const isAbsolute = command.startsWith("/") || /^[A-Za-z]:[\\/]/.test(command);
43
+ return isAbsolute && !existsSync(command);
44
+ }
37
45
  function copyToClipboard(content) {
38
46
  if (platform() === "win32") {
39
47
  return spawnSync("clip", [], { input: content }).status === 0;
@@ -43,30 +51,28 @@ function copyToClipboard(content) {
43
51
  }
44
52
  return false;
45
53
  }
46
- async function writeSkillsToClaudeCode() {
47
- const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
48
- const combined = await skillsCombined();
49
- const block = `${SKILLS_MARKER_START}
50
- ${combined}
51
- ${SKILLS_MARKER_END}`;
52
- let existing = "";
53
- try {
54
- existing = await readFile(claudeMdPath, "utf-8");
55
- } catch {
56
- }
57
- if (existing.includes(SKILLS_MARKER_START)) {
58
- const updated = existing.replace(
59
- new RegExp(`${SKILLS_MARKER_START}[\\s\\S]*?${SKILLS_MARKER_END}`),
60
- block
61
- );
62
- await mkdir(dirname(claudeMdPath), { recursive: true });
63
- await writeFile(claudeMdPath, updated, "utf-8");
64
- } else {
65
- await mkdir(dirname(claudeMdPath), { recursive: true });
66
- await writeFile(claudeMdPath, existing + (existing.endsWith("\n") ? "" : "\n") + block + "\n", "utf-8");
54
+ async function installSkillsToClaudeCode() {
55
+ const sourceDir = skillsSourceDir();
56
+ const skills = await loadSkills();
57
+ const destRoot = join(homedir(), ".claude", "skills");
58
+ await mkdir(destRoot, { recursive: true });
59
+ const installed = [];
60
+ for (const skill of skills) {
61
+ await cp(join(sourceDir, skill.dir), join(destRoot, skill.dir), { recursive: true, force: true });
62
+ installed.push(skill.name);
67
63
  }
64
+ await removeLegacySkillsBlock();
65
+ return installed;
68
66
  }
69
67
  async function removeSkillsFromClaudeCode() {
68
+ const destRoot = join(homedir(), ".claude", "skills");
69
+ const skills = await loadSkills();
70
+ for (const skill of skills) {
71
+ await rm(join(destRoot, skill.dir), { recursive: true, force: true }).catch(() => void 0);
72
+ }
73
+ await removeLegacySkillsBlock();
74
+ }
75
+ async function removeLegacySkillsBlock() {
70
76
  const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
71
77
  let existing = "";
72
78
  try {
@@ -83,7 +89,8 @@ async function removeSkillsFromClaudeCode() {
83
89
 
84
90
  export {
85
91
  getMcpEntry,
92
+ commandPathIsStale,
86
93
  copyToClipboard,
87
- writeSkillsToClaudeCode,
94
+ installSkillsToClaudeCode,
88
95
  removeSkillsFromClaudeCode
89
96
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  capture,
3
3
  classifyError
4
- } from "./chunk-R5G6UXSI.js";
4
+ } from "./chunk-D2JZW2TW.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-NPCCDRWQ.js";
@@ -13,6 +13,7 @@ import {
13
13
  // src/tools/index.ts
14
14
  import { z } from "zod";
15
15
  import { randomUUID } from "crypto";
16
+ var toolHooks = null;
16
17
  var MAX_FILTER_PAGES = 5;
17
18
  var FILTER_PAGE_SIZE = 100;
18
19
  function parseDate(s) {
@@ -37,6 +38,10 @@ function normalizeMcpHost(name) {
37
38
  function emitToolClick(tool, props = {}) {
38
39
  const requestId = randomUUID();
39
40
  capture("mcp:tool:click", { tool_name: tool, request_id: requestId, passive: false, ...props });
41
+ toolHooks?.onToolClick({
42
+ tool,
43
+ fileId: typeof props.file_id === "string" ? props.file_id : void 0
44
+ });
40
45
  return requestId;
41
46
  }
42
47
  function recordToolMetric(tool, status, durationMs, requestId, props = {}, err) {
@@ -52,8 +57,16 @@ function recordToolMetric(tool, status, durationMs, requestId, props = {}, err)
52
57
  if (status === "success") evProps.duration_ms = durationMs;
53
58
  else evProps.error_type = classifyError(err);
54
59
  capture(event, evProps);
60
+ toolHooks?.onToolResult({
61
+ tool,
62
+ status,
63
+ durationMs,
64
+ fileId: typeof props.file_id === "string" ? props.file_id : void 0,
65
+ failureKind: status === "error" ? classifyError(err) : void 0
66
+ });
55
67
  }
56
- function registerTools(server, client) {
68
+ function registerTools(server, client, hooks) {
69
+ toolHooks = hooks ?? null;
57
70
  server.registerTool(
58
71
  "list_files",
59
72
  {
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getClient
4
- } from "./chunk-QJV4OLZI.js";
4
+ } from "./chunk-LOGKF7DL.js";
5
5
  import {
6
6
  loadSkills
7
- } from "./chunk-4QBEOJPX.js";
7
+ } from "./chunk-242FRP4P.js";
8
8
  import {
9
9
  normalizeMcpHost,
10
10
  registerTools
11
- } from "./chunk-XA77WLYF.js";
11
+ } from "./chunk-XWDA3G2U.js";
12
12
  import {
13
13
  capture,
14
14
  classifyError,
@@ -20,10 +20,9 @@ import {
20
20
  setMcpHost,
21
21
  setUser,
22
22
  shutdown
23
- } from "./chunk-R5G6UXSI.js";
23
+ } from "./chunk-D2JZW2TW.js";
24
24
  import "./chunk-NPCCDRWQ.js";
25
25
  import "./chunk-RUFCT6DQ.js";
26
- import "./chunk-MCKGQKYU.js";
27
26
 
28
27
  // src/index.ts
29
28
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -31,7 +30,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
31
30
  import open from "open";
32
31
  var server = new McpServer({
33
32
  name: "plaud",
34
- version: "0.3.2"
33
+ version: "0.3.4"
35
34
  });
36
35
  var CALLBACK_PORT = 8199;
37
36
  var LOGIN_TIMEOUT_MS = 12e4;
@@ -154,7 +153,7 @@ async function main() {
154
153
  const sub = process.argv[2];
155
154
  const sub2 = process.argv[3];
156
155
  if (sub === "install") {
157
- const { runInstall } = await import("./install-UUXHA6WE.js");
156
+ const { runInstall } = await import("./install-ETBOS6SA.js");
158
157
  const args = process.argv.slice(3);
159
158
  const yes = args.some((a) => a === "--yes" || a === "-y");
160
159
  const noLogin = args.some((a) => a === "--no-login");
@@ -162,33 +161,33 @@ async function main() {
162
161
  return;
163
162
  }
164
163
  if (sub === "clean-plugin") {
165
- const { runCleanPlugin } = await import("./setup-GLWZYNRW.js");
164
+ const { runCleanPlugin } = await import("./setup-SMS45JC5.js");
166
165
  await runCleanPlugin();
167
166
  return;
168
167
  }
169
168
  if (sub === "setup" && sub2 === "codex") {
170
- const { runSetupCodex } = await import("./setup-GLWZYNRW.js");
169
+ const { runSetupCodex } = await import("./setup-SMS45JC5.js");
171
170
  await runSetupCodex();
172
171
  return;
173
172
  }
174
173
  if (sub === "unsetup" && sub2 === "codex") {
175
- const { runUnsetupCodex } = await import("./setup-GLWZYNRW.js");
174
+ const { runUnsetupCodex } = await import("./setup-SMS45JC5.js");
176
175
  await runUnsetupCodex();
177
176
  return;
178
177
  }
179
178
  if (sub === "setup") {
180
- const { runSetup } = await import("./setup-GLWZYNRW.js");
179
+ const { runSetup } = await import("./setup-SMS45JC5.js");
181
180
  await runSetup();
182
181
  return;
183
182
  }
184
183
  if (sub === "unsetup") {
185
- const { runUnsetup } = await import("./setup-GLWZYNRW.js");
184
+ const { runUnsetup } = await import("./setup-SMS45JC5.js");
186
185
  await runUnsetup();
187
186
  return;
188
187
  }
189
188
  if (sub === "http") {
190
- const { startHttpServer } = await import("./server-D3JKIARR.js");
191
- const { startMetricsServer } = await import("./server-BOR5EZLF.js");
189
+ const { startHttpServer } = await import("./server-VVQEILKC.js");
190
+ const { startMetricsServer } = await import("./server-NKCNUA6P.js");
192
191
  startMetricsServer();
193
192
  startHttpServer();
194
193
  return;
@@ -213,7 +212,7 @@ Usage:
213
212
  try {
214
213
  await initTelemetry({
215
214
  surface: "mcp",
216
- appVersion: "0.3.2",
215
+ appVersion: "0.3.4",
217
216
  transport: "stdio"
218
217
  });
219
218
  } catch {
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  getClient
3
- } from "./chunk-QJV4OLZI.js";
3
+ } from "./chunk-LOGKF7DL.js";
4
4
  import {
5
+ commandPathIsStale,
5
6
  copyToClipboard,
6
7
  getMcpEntry,
7
- writeSkillsToClaudeCode
8
- } from "./chunk-QT25MKZJ.js";
8
+ installSkillsToClaudeCode
9
+ } from "./chunk-VHTXWQUE.js";
9
10
  import {
10
11
  skillsCombined
11
- } from "./chunk-4QBEOJPX.js";
12
+ } from "./chunk-242FRP4P.js";
12
13
  import {
13
14
  runOAuthCallback
14
- } from "./chunk-R5G6UXSI.js";
15
+ } from "./chunk-D2JZW2TW.js";
15
16
  import "./chunk-RUFCT6DQ.js";
16
- import "./chunk-MCKGQKYU.js";
17
17
 
18
18
  // src/install.ts
19
19
  import { createInterface } from "readline/promises";
@@ -85,12 +85,38 @@ import { existsSync as existsSync2 } from "fs";
85
85
  import { homedir as homedir2 } from "os";
86
86
  import { dirname, join as join2 } from "path";
87
87
  var RESTART_HINT2 = "Quit Codex Desktop and reopen it.";
88
+ var PLAUD_HEADER = "[mcp_servers.plaud]";
88
89
  function codexConfigPath() {
89
90
  return join2(homedir2(), ".codex", "config.toml");
90
91
  }
91
92
  function quoteTomlString(value) {
92
93
  return JSON.stringify(value);
93
94
  }
95
+ function plaudBlockRange(lines) {
96
+ const start = lines.findIndex((l) => l.trim() === PLAUD_HEADER);
97
+ if (start === -1) return null;
98
+ let end = lines.length;
99
+ for (let i = start + 1; i < lines.length; i++) {
100
+ if (/^\s*\[/.test(lines[i])) {
101
+ end = i;
102
+ break;
103
+ }
104
+ }
105
+ return { start, end };
106
+ }
107
+ function extractCommand(blockLines) {
108
+ for (const line of blockLines) {
109
+ const m = line.match(/^\s*command\s*=\s*("(?:[^"\\]|\\.)*")\s*$/);
110
+ if (m) {
111
+ try {
112
+ return JSON.parse(m[1]);
113
+ } catch {
114
+ return void 0;
115
+ }
116
+ }
117
+ }
118
+ return void 0;
119
+ }
94
120
  function codexAdapter() {
95
121
  return {
96
122
  id: "codex",
@@ -102,25 +128,38 @@ function codexAdapter() {
102
128
  async install({ entry }) {
103
129
  const configPath = codexConfigPath();
104
130
  const argsStr = entry.args.map(quoteTomlString).join(", ");
105
- const serverEntry = `
106
- [mcp_servers.plaud]
131
+ const blockBody = `${PLAUD_HEADER}
107
132
  command = ${quoteTomlString(entry.command)}
108
- args = [${argsStr}]
109
- `;
133
+ args = [${argsStr}]`;
110
134
  let content = "";
111
135
  try {
112
136
  content = await readFile(configPath, "utf-8");
113
137
  } catch {
114
138
  }
115
- if (content.includes("[mcp_servers.plaud]")) {
139
+ const lines = content.split("\n");
140
+ const range = plaudBlockRange(lines);
141
+ if (range) {
142
+ const existingCommand = extractCommand(lines.slice(range.start, range.end));
143
+ if (!commandPathIsStale(existingCommand)) {
144
+ return {
145
+ status: "already-configured",
146
+ message: "already configured",
147
+ restartHint: RESTART_HINT2
148
+ };
149
+ }
150
+ const rebuilt = [...lines.slice(0, range.start), ...blockBody.split("\n"), ...lines.slice(range.end)].join("\n");
151
+ await mkdir(dirname(configPath), { recursive: true });
152
+ await writeFile(configPath, rebuilt, "utf-8");
116
153
  return {
117
- status: "already-configured",
118
- message: "already configured",
154
+ status: "configured",
155
+ message: "repaired stale launch path (now stable npx @latest)",
119
156
  restartHint: RESTART_HINT2
120
157
  };
121
158
  }
122
159
  await mkdir(dirname(configPath), { recursive: true });
123
- await writeFile(configPath, content + serverEntry, "utf-8");
160
+ await writeFile(configPath, content + `
161
+ ${blockBody}
162
+ `, "utf-8");
124
163
  const combined = await skillsCombined();
125
164
  const copied = copyToClipboard(combined);
126
165
  return {
@@ -186,19 +225,21 @@ function jsonMcpServersAdapter(spec) {
186
225
  const servers = existingServers ?? {};
187
226
  const existing = servers[serverKey];
188
227
  if (existing) {
189
- if (entryAutoUpdates(existing)) {
228
+ const existingCommand = isJsonObject(existing) && typeof existing.command === "string" ? existing.command : void 0;
229
+ if (entryAutoUpdates(existing) && !commandPathIsStale(existingCommand)) {
190
230
  return {
191
231
  status: "already-configured",
192
232
  message: "already configured (npx @latest \u2014 auto-updates)",
193
233
  restartHint: spec.restartHint
194
234
  };
195
235
  }
236
+ const repairing = entryAutoUpdates(existing);
196
237
  config[rootKey] = { ...servers, [serverKey]: toEntry(ctx.entry) };
197
238
  await mkdir2(dirname2(configPath), { recursive: true });
198
239
  await writeFile2(configPath, JSON.stringify(config, null, 2), "utf-8");
199
240
  return {
200
241
  status: "configured",
201
- message: "migrated to npx @latest (was pinned/local \u2014 now auto-updates)",
242
+ message: repairing ? "repaired stale launch path (now stable npx @latest)" : "migrated to npx @latest (was pinned/local \u2014 now auto-updates)",
202
243
  restartHint: spec.restartHint
203
244
  };
204
245
  }
@@ -298,10 +339,13 @@ function zedAdapter() {
298
339
  return { status: "failed", message: `failed: ${configPath} field context_servers must be a JSON object.` };
299
340
  }
300
341
  const servers = existingServers ?? {};
301
- if (servers.plaud && entryAutoUpdates2(servers.plaud)) {
342
+ const existingPlaud = servers.plaud;
343
+ const existingPath = isJsonObject2(existingPlaud) && isJsonObject2(existingPlaud.command) && typeof existingPlaud.command.path === "string" ? existingPlaud.command.path : void 0;
344
+ if (existingPlaud && entryAutoUpdates2(existingPlaud) && !commandPathIsStale(existingPath)) {
302
345
  return { status: "already-configured", message: "already configured (npx @latest \u2014 auto-updates)", restartHint: RESTART_HINT3 };
303
346
  }
304
- const migrating = Boolean(servers.plaud);
347
+ const repairing = Boolean(existingPlaud) && entryAutoUpdates2(existingPlaud);
348
+ const migrating = Boolean(existingPlaud);
305
349
  config.context_servers = {
306
350
  ...servers,
307
351
  plaud: { command: { path: entry.command, args: entry.args } }
@@ -310,7 +354,7 @@ function zedAdapter() {
310
354
  await writeFile3(configPath, JSON.stringify(config, null, 2), "utf-8");
311
355
  return {
312
356
  status: "configured",
313
- message: migrating ? "migrated to npx @latest (was pinned/local \u2014 now auto-updates). Restart Zed." : "configured. Restart Zed to load the Plaud MCP.",
357
+ message: repairing ? "repaired stale launch path (now stable npx @latest). Restart Zed." : migrating ? "migrated to npx @latest (was pinned/local \u2014 now auto-updates). Restart Zed." : "configured. Restart Zed to load the Plaud MCP.",
314
358
  restartHint: RESTART_HINT3
315
359
  };
316
360
  }
@@ -537,8 +581,8 @@ async function runInstall(opts = {}) {
537
581
  if (succeeded.some((c) => c.id === "claude-code")) {
538
582
  process.stdout.write("\u2192 Claude Code skills... ");
539
583
  try {
540
- await writeSkillsToClaudeCode();
541
- console.log("written to ~/.claude/CLAUDE.md.");
584
+ const installed = await installSkillsToClaudeCode();
585
+ console.log(`installed ${installed.length} skill${installed.length === 1 ? "" : "s"} to ~/.claude/skills/.`);
542
586
  } catch (err) {
543
587
  console.log(`failed: ${err instanceof Error ? err.message : String(err)}`);
544
588
  }
@@ -4,7 +4,6 @@ import {
4
4
  import {
5
5
  registry
6
6
  } from "./chunk-RUFCT6DQ.js";
7
- import "./chunk-MCKGQKYU.js";
8
7
 
9
8
  // src/metrics/server.ts
10
9
  import express from "express";