@plaud-ai/mcp 0.2.3 → 0.3.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.
package/dist/index.js CHANGED
@@ -1,16 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getClient
4
- } from "./chunk-GZ7QPRKV.js";
4
+ } from "./chunk-BU3JMO4H.js";
5
5
  import {
6
6
  loadSkills
7
7
  } from "./chunk-4QBEOJPX.js";
8
8
  import {
9
+ normalizeMcpHost,
9
10
  registerTools
10
- } from "./chunk-BZOIX6WC.js";
11
+ } from "./chunk-QXV7KYGD.js";
11
12
  import {
12
- runOAuthCallback
13
- } from "./chunk-3XRFIJUG.js";
13
+ capture,
14
+ classifyError,
15
+ clearUser,
16
+ extractIdentity,
17
+ initTelemetry,
18
+ oauthCallbackErrorType,
19
+ runOAuthCallback,
20
+ setMcpHost,
21
+ setUser,
22
+ shutdown
23
+ } from "./chunk-TPGKCGTQ.js";
24
+ import "./chunk-NPCCDRWQ.js";
25
+ import "./chunk-RUFCT6DQ.js";
26
+ import "./chunk-MCKGQKYU.js";
14
27
 
15
28
  // src/index.ts
16
29
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -18,7 +31,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
18
31
  import open from "open";
19
32
  var server = new McpServer({
20
33
  name: "plaud",
21
- version: "0.2.3"
34
+ version: "0.3.0"
22
35
  });
23
36
  var CALLBACK_PORT = 8199;
24
37
  var LOGIN_TIMEOUT_MS = 12e4;
@@ -28,7 +41,20 @@ server.registerTool("login", {
28
41
  const client = getClient();
29
42
  const existingToken = await client.auth.getAccessToken();
30
43
  if (existingToken) {
31
- return { content: [{ type: "text", text: "Already logged in." }] };
44
+ try {
45
+ await client.getCurrentUser();
46
+ return { content: [{ type: "text", text: "Already logged in." }] };
47
+ } catch (err) {
48
+ const msg = err instanceof Error ? err.message : String(err);
49
+ if (msg.includes("401") || msg.includes("Not authenticated")) {
50
+ await client.auth.logout();
51
+ } else {
52
+ return {
53
+ content: [{ type: "text", text: `Failed to verify login state: ${msg}` }],
54
+ isError: true
55
+ };
56
+ }
57
+ }
32
58
  }
33
59
  const { url, codeVerifier, state } = client.auth.createAuthorizationRequest();
34
60
  const result = await runOAuthCallback({
@@ -43,8 +69,23 @@ server.registerTool("login", {
43
69
  });
44
70
  }
45
71
  });
72
+ if (result.status === "success") {
73
+ capture("auth:oauth_callback:success", { passive: true });
74
+ } else {
75
+ capture("auth:oauth_callback:error", {
76
+ passive: true,
77
+ error_type: oauthCallbackErrorType(result.status)
78
+ });
79
+ }
46
80
  switch (result.status) {
47
81
  case "success":
82
+ try {
83
+ const user = await client.getCurrentUser();
84
+ if (user && typeof user.id === "string") {
85
+ await setUser(user.id, extractIdentity(user));
86
+ }
87
+ } catch {
88
+ }
48
89
  return { content: [{ type: "text", text: "Successfully authenticated with Plaud!" }] };
49
90
  case "timeout":
50
91
  return {
@@ -84,10 +125,19 @@ server.registerTool("logout", {
84
125
  return { content: [{ type: "text", text: "Already logged out." }] };
85
126
  }
86
127
  try {
87
- await client.revokeCurrentUser();
128
+ try {
129
+ await client.revokeCurrentUser();
130
+ } catch {
131
+ }
132
+ await client.auth.logout();
133
+ capture("auth:logout:success", { passive: true });
134
+ } catch (err) {
135
+ capture("auth:logout:error", { passive: true, error_type: classifyError(err) });
136
+ }
137
+ try {
138
+ await clearUser();
88
139
  } catch {
89
140
  }
90
- await client.auth.logout();
91
141
  return {
92
142
  content: [{ type: "text", text: "Logged out and revoked authorization." }]
93
143
  };
@@ -104,7 +154,7 @@ async function main() {
104
154
  const sub = process.argv[2];
105
155
  const sub2 = process.argv[3];
106
156
  if (sub === "install") {
107
- const { runInstall } = await import("./install-C7WMVIPG.js");
157
+ const { runInstall } = await import("./install-2EIB7ZP3.js");
108
158
  const args = process.argv.slice(3);
109
159
  const yes = args.some((a) => a === "--yes" || a === "-y");
110
160
  const noLogin = args.some((a) => a === "--no-login");
@@ -112,32 +162,34 @@ async function main() {
112
162
  return;
113
163
  }
114
164
  if (sub === "clean-plugin") {
115
- const { runCleanPlugin } = await import("./setup-QXZHVVDP.js");
165
+ const { runCleanPlugin } = await import("./setup-GLWZYNRW.js");
116
166
  await runCleanPlugin();
117
167
  return;
118
168
  }
119
169
  if (sub === "setup" && sub2 === "codex") {
120
- const { runSetupCodex } = await import("./setup-QXZHVVDP.js");
170
+ const { runSetupCodex } = await import("./setup-GLWZYNRW.js");
121
171
  await runSetupCodex();
122
172
  return;
123
173
  }
124
174
  if (sub === "unsetup" && sub2 === "codex") {
125
- const { runUnsetupCodex } = await import("./setup-QXZHVVDP.js");
175
+ const { runUnsetupCodex } = await import("./setup-GLWZYNRW.js");
126
176
  await runUnsetupCodex();
127
177
  return;
128
178
  }
129
179
  if (sub === "setup") {
130
- const { runSetup } = await import("./setup-QXZHVVDP.js");
180
+ const { runSetup } = await import("./setup-GLWZYNRW.js");
131
181
  await runSetup();
132
182
  return;
133
183
  }
134
184
  if (sub === "unsetup") {
135
- const { runUnsetup } = await import("./setup-QXZHVVDP.js");
185
+ const { runUnsetup } = await import("./setup-GLWZYNRW.js");
136
186
  await runUnsetup();
137
187
  return;
138
188
  }
139
189
  if (sub === "http") {
140
- const { startHttpServer } = await import("./server-ORMBAVW6.js");
190
+ const { startHttpServer } = await import("./server-ZVB7U2VZ.js");
191
+ const { startMetricsServer } = await import("./server-BOR5EZLF.js");
192
+ startMetricsServer();
141
193
  startHttpServer();
142
194
  return;
143
195
  }
@@ -158,8 +210,37 @@ Usage:
158
210
  `);
159
211
  return;
160
212
  }
213
+ try {
214
+ await initTelemetry({
215
+ surface: "mcp",
216
+ appVersion: "0.3.0",
217
+ transport: "stdio"
218
+ });
219
+ } catch {
220
+ }
221
+ const exitWithFlush = async (code) => {
222
+ try {
223
+ await shutdown();
224
+ } catch {
225
+ }
226
+ process.exit(code);
227
+ };
228
+ process.on("SIGINT", () => void exitWithFlush(130));
229
+ process.on("SIGTERM", () => void exitWithFlush(143));
161
230
  await registerSkillPrompts();
231
+ server.server.oninitialized = () => {
232
+ try {
233
+ setMcpHost(normalizeMcpHost(server.server.getClientVersion()?.name));
234
+ } catch {
235
+ }
236
+ };
162
237
  const transport = new StdioServerTransport();
163
- await server.connect(transport);
238
+ try {
239
+ await server.connect(transport);
240
+ capture("mcp:server:success", { passive: true });
241
+ } catch (err) {
242
+ capture("mcp:server:error", { passive: true, error_type: classifyError(err) });
243
+ await exitWithFlush(1);
244
+ }
164
245
  }
165
246
  main().catch(console.error);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getClient
3
- } from "./chunk-GZ7QPRKV.js";
3
+ } from "./chunk-BU3JMO4H.js";
4
4
  import {
5
5
  copyToClipboard,
6
6
  getMcpEntry,
@@ -11,7 +11,9 @@ import {
11
11
  } from "./chunk-4QBEOJPX.js";
12
12
  import {
13
13
  runOAuthCallback
14
- } from "./chunk-3XRFIJUG.js";
14
+ } from "./chunk-TPGKCGTQ.js";
15
+ import "./chunk-RUFCT6DQ.js";
16
+ import "./chunk-MCKGQKYU.js";
15
17
 
16
18
  // src/install.ts
17
19
  import { createInterface } from "readline/promises";
@@ -411,8 +413,13 @@ async function runLogin() {
411
413
  try {
412
414
  const user = await client.getCurrentUser();
413
415
  return { status: "already-authed", who: pickIdentity(user) };
414
- } catch {
415
- return { status: "already-authed" };
416
+ } catch (err) {
417
+ const msg = err instanceof Error ? err.message : String(err);
418
+ if (msg.includes("401") || msg.includes("Not authenticated")) {
419
+ await client.auth.logout().catch(() => void 0);
420
+ } else {
421
+ return { status: "already-authed" };
422
+ }
416
423
  }
417
424
  }
418
425
  } catch {
@@ -534,8 +541,14 @@ async function doLoginStep(nonInteractive) {
534
541
  const who = pickIdentity(user);
535
542
  console.log(` already signed in${who ? ` as ${who}` : ""} \u2014 skipping OAuth.`);
536
543
  return { status: "already-authed", who };
537
- } catch {
538
- console.log(" token present but user lookup failed \u2014 will re-auth.");
544
+ } catch (err) {
545
+ const msg = err instanceof Error ? err.message : String(err);
546
+ if (msg.includes("401") || msg.includes("Not authenticated")) {
547
+ console.log(" saved token was revoked server-side \u2014 clearing and re-authenticating.");
548
+ await client.auth.logout().catch(() => void 0);
549
+ } else {
550
+ console.log(" token present but user lookup failed \u2014 will re-auth.");
551
+ }
539
552
  }
540
553
  }
541
554
  } catch {
@@ -0,0 +1,35 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-NPCCDRWQ.js";
4
+ import {
5
+ registry
6
+ } from "./chunk-RUFCT6DQ.js";
7
+ import "./chunk-MCKGQKYU.js";
8
+
9
+ // src/metrics/server.ts
10
+ import express from "express";
11
+ var METRICS_PORT = Number(process.env.PLAUD_METRICS_PORT ?? 9080);
12
+ var METRICS_HOST = process.env.PLAUD_METRICS_HOST ?? "0.0.0.0";
13
+ function startMetricsServer() {
14
+ const app = express();
15
+ app.get("/metrics", async (_req, res, next) => {
16
+ try {
17
+ res.setHeader("Content-Type", registry.contentType);
18
+ res.end(await registry.metrics());
19
+ } catch (err) {
20
+ next(err);
21
+ }
22
+ });
23
+ app.get("/healthz", (_req, res) => {
24
+ res.status(200).send("ok");
25
+ });
26
+ const server = app.listen(METRICS_PORT, METRICS_HOST, () => {
27
+ logger.info({ event: "metrics_server_started", port: METRICS_PORT, host: METRICS_HOST });
28
+ });
29
+ server.on("error", (err) => {
30
+ logger.error({ event: "metrics_server_error", err: err.message });
31
+ });
32
+ }
33
+ export {
34
+ startMetricsServer
35
+ };
@@ -1,10 +1,18 @@
1
1
  import {
2
- logger,
3
2
  registerTools
4
- } from "./chunk-BZOIX6WC.js";
3
+ } from "./chunk-QXV7KYGD.js";
5
4
  import {
6
5
  PlaudClient
7
- } from "./chunk-3XRFIJUG.js";
6
+ } from "./chunk-TPGKCGTQ.js";
7
+ import {
8
+ logger
9
+ } from "./chunk-NPCCDRWQ.js";
10
+ import {
11
+ httpRequestDuration,
12
+ httpRequestsInProgress,
13
+ oauthTokenRefresh
14
+ } from "./chunk-RUFCT6DQ.js";
15
+ import "./chunk-MCKGQKYU.js";
8
16
 
9
17
  // src/http/server.ts
10
18
  import express from "express";
@@ -680,17 +688,21 @@ var PlaudOAuthProvider = class extends ProxyOAuthServerProvider {
680
688
  }
681
689
  logger.error({ event: "oauth_token_refresh_failed", status: fetchRes.status, body: text.slice(0, 500) });
682
690
  if (fetchRes.status >= 500) {
691
+ oauthTokenRefresh.inc({ result: "upstream_error" });
683
692
  throw new McpServerError(`Upstream refresh endpoint error: ${fetchRes.status} ${text.slice(0, 200)}`);
684
693
  }
694
+ oauthTokenRefresh.inc({ result: "invalid_grant" });
685
695
  throw new InvalidGrantError(`Plaud token refresh: ${fetchRes.status} ${text.slice(0, 200)}`);
686
696
  }
687
697
  let data;
688
698
  try {
689
699
  data = await fetchRes.json();
690
700
  } catch (err) {
701
+ oauthTokenRefresh.inc({ result: "parse_failed" });
691
702
  logger.error({ event: "oauth_refresh_json_parse_failed", error: String(err) });
692
703
  throw new McpServerError("Refresh endpoint returned non-JSON response");
693
704
  }
705
+ oauthTokenRefresh.inc({ result: "success" });
694
706
  logger.info({
695
707
  event: "oauth_token_refresh_ok",
696
708
  has_refresh_token: !!data.refresh_token,
@@ -805,6 +817,22 @@ function startHttpServer() {
805
817
  };
806
818
  const app = createMcpExpressApp({ host: HTTP_HOST });
807
819
  app.set("trust proxy", 1);
820
+ app.use((req, res, next) => {
821
+ const start = process.hrtime.bigint();
822
+ const method = req.method;
823
+ const inProgressLabels = { method, handler: req.path };
824
+ httpRequestsInProgress.inc(inProgressLabels);
825
+ res.on("finish", () => {
826
+ const handler = req.route?.path ?? req.path;
827
+ const durSec = Number(process.hrtime.bigint() - start) / 1e9;
828
+ httpRequestDuration.observe(
829
+ { method, handler, status: String(res.statusCode) },
830
+ durSec
831
+ );
832
+ httpRequestsInProgress.dec(inProgressLabels);
833
+ });
834
+ next();
835
+ });
808
836
  app.get("/.well-known/openai-apps-challenge", (_req, res) => {
809
837
  res.type("text/plain").send(OPENAI_APPS_CHALLENGE_TOKEN);
810
838
  });
@@ -982,7 +1010,7 @@ function startHttpServer() {
982
1010
  apiBase,
983
1011
  staticToken: token
984
1012
  });
985
- const mcpServer = new McpServer({ name: "plaud", version: "0.2.3" });
1013
+ const mcpServer = new McpServer({ name: "plaud", version: "0.3.0" });
986
1014
  registerTools(mcpServer, client);
987
1015
  const transport = new StreamableHTTPServerTransport({
988
1016
  sessionIdGenerator: void 0,
@@ -7,6 +7,7 @@ import {
7
7
  import {
8
8
  skillsCombined
9
9
  } from "./chunk-4QBEOJPX.js";
10
+ import "./chunk-MCKGQKYU.js";
10
11
 
11
12
  // src/setup.ts
12
13
  import { readFile, writeFile, mkdir, rm } from "fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaud-ai/mcp",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -25,10 +25,12 @@
25
25
  "express": "^5.2.1",
26
26
  "open": "^10.2.0",
27
27
  "pino": "^10.3.1",
28
+ "prom-client": "^15.1.3",
28
29
  "zod": "^4.3.6"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@plaud-ai/shared": "workspace:*",
33
+ "@plaud-ai/telemetry": "workspace:*",
32
34
  "@types/express": "^5.0.6",
33
35
  "@types/node": "^25.5.0",
34
36
  "typescript": "^5.7.0"
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plaud",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Access your Plaud recordings in Claude",
5
5
  "author": {
6
6
  "name": "Plaud AI"