@plaud-ai/mcp 0.2.4 → 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.
@@ -0,0 +1,50 @@
1
+ import {
2
+ PlaudClient,
3
+ capture
4
+ } from "./chunk-TPGKCGTQ.js";
5
+ import {
6
+ httpClientDuration,
7
+ httpClientRequests
8
+ } from "./chunk-RUFCT6DQ.js";
9
+
10
+ // src/config.ts
11
+ function buildExtraHeaders() {
12
+ const headers = {};
13
+ if (process.env.PLAUD_ENV) headers["x-pld-env"] = process.env.PLAUD_ENV;
14
+ if (process.env.PLAUD_REGION) headers["x-pld-region"] = process.env.PLAUD_REGION;
15
+ return headers;
16
+ }
17
+ function normalizeEndpoint(path) {
18
+ return path.replace(/\/open\/third-party\/files\/[^/?]+/, "/open/third-party/files/:id").split("?")[0];
19
+ }
20
+ function recordUpstreamRequest(obs) {
21
+ const endpoint = normalizeEndpoint(obs.path);
22
+ httpClientRequests.inc({ host: obs.host, endpoint, status: String(obs.status) });
23
+ httpClientDuration.observe({ host: obs.host, endpoint }, obs.durationMs / 1e3);
24
+ }
25
+ var CONFIG = {
26
+ clientId: process.env.PLAUD_MCP_CLIENT_ID ?? process.env.PLAUD_CLIENT_ID ?? "client_9c501dad-8a0d-40b2-a7b0-d1cb8787f674",
27
+ clientSecret: process.env.PLAUD_CLIENT_SECRET ?? "",
28
+ redirectUri: "http://localhost:8199/auth/callback",
29
+ tokenFile: "tokens-mcp.json",
30
+ apiBase: process.env.PLAUD_API_BASE,
31
+ authorizationUrl: process.env.PLAUD_AUTH_URL,
32
+ tokenUrl: process.env.PLAUD_TOKEN_URL,
33
+ refreshUrl: process.env.PLAUD_REFRESH_URL,
34
+ extraHeaders: buildExtraHeaders(),
35
+ onRequest: recordUpstreamRequest,
36
+ // Background token refresh → passive telemetry event. CLI/stdio only:
37
+ // capture() no-ops in the HTTP server (which never inits frontend telemetry).
38
+ onTokenRefresh: (status, errorType) => capture(`auth:token_refresh:${status}`, { passive: true, ...errorType ? { error_type: errorType } : {} })
39
+ };
40
+ var client = null;
41
+ function getClient() {
42
+ if (!client) {
43
+ client = new PlaudClient(CONFIG);
44
+ }
45
+ return client;
46
+ }
47
+
48
+ export {
49
+ getClient
50
+ };
@@ -0,0 +1,15 @@
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+ var __commonJS = (cb, mod) => function __require2() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+
12
+ export {
13
+ __require,
14
+ __commonJS
15
+ };
@@ -0,0 +1,10 @@
1
+ // src/logger.ts
2
+ import pino from "pino";
3
+ var logger = pino(
4
+ { level: process.env.LOG_LEVEL ?? "info" },
5
+ pino.destination(2)
6
+ );
7
+
8
+ export {
9
+ logger
10
+ };
@@ -1,12 +1,18 @@
1
- // src/logger.ts
2
- import pino from "pino";
3
- var logger = pino(
4
- { level: process.env.LOG_LEVEL ?? "info" },
5
- pino.destination(2)
6
- );
1
+ import {
2
+ capture,
3
+ classifyError
4
+ } from "./chunk-TPGKCGTQ.js";
5
+ import {
6
+ logger
7
+ } from "./chunk-NPCCDRWQ.js";
8
+ import {
9
+ mcpToolCalls,
10
+ mcpToolDuration
11
+ } from "./chunk-RUFCT6DQ.js";
7
12
 
8
13
  // src/tools/index.ts
9
14
  import { z } from "zod";
15
+ import { randomUUID } from "crypto";
10
16
  var MAX_FILTER_PAGES = 5;
11
17
  var FILTER_PAGE_SIZE = 100;
12
18
  function parseDate(s) {
@@ -15,6 +21,38 @@ function parseDate(s) {
15
21
  if (Number.isNaN(d.getTime())) return null;
16
22
  return d.getTime();
17
23
  }
24
+ function normalizeMcpHost(name) {
25
+ if (!name) return void 0;
26
+ const n = name.trim().toLowerCase();
27
+ if (!n) return void 0;
28
+ if (n === "claude-ai" || n.includes("claude desktop") || n.includes("claude-desktop")) return "claude_desktop";
29
+ if (n.includes("claude code") || n.includes("claude-code")) return "claude_code";
30
+ if (n.includes("cursor")) return "cursor";
31
+ if (n.includes("windsurf")) return "windsurf";
32
+ if (n.includes("cline")) return "cline";
33
+ if (n.includes("continue")) return "continue";
34
+ if (n.includes("vscode") || n.includes("vs code")) return "vscode";
35
+ return n;
36
+ }
37
+ function emitToolClick(tool, props = {}) {
38
+ const requestId = randomUUID();
39
+ capture("mcp:tool:click", { tool_name: tool, request_id: requestId, passive: false, ...props });
40
+ return requestId;
41
+ }
42
+ function recordToolMetric(tool, status, durationMs, requestId, props = {}, err) {
43
+ mcpToolCalls.inc({ tool, status });
44
+ mcpToolDuration.observe({ tool }, durationMs / 1e3);
45
+ const event = status === "success" ? "mcp:tool:success" : "mcp:tool:error";
46
+ const evProps = {
47
+ tool_name: tool,
48
+ request_id: requestId,
49
+ passive: true,
50
+ ...props
51
+ };
52
+ if (status === "success") evProps.duration_ms = durationMs;
53
+ else evProps.error_type = classifyError(err);
54
+ capture(event, evProps);
55
+ }
18
56
  function registerTools(server, client) {
19
57
  server.registerTool(
20
58
  "list_files",
@@ -38,9 +76,11 @@ function registerTools(server, client) {
38
76
  const start = Date.now();
39
77
  const hasFilter = Boolean(query || date_from || date_to);
40
78
  logger.info({ event: "tool_call", tool: "list_files", has_filter: hasFilter });
79
+ const requestId = emitToolClick("list_files");
41
80
  try {
42
81
  if (!hasFilter) {
43
82
  const result = await client.listFiles(page, page_size);
83
+ recordToolMetric("list_files", "success", Date.now() - start, requestId);
44
84
  logger.info({ event: "tool_call_end", tool: "list_files", duration_ms: Date.now() - start });
45
85
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
46
86
  }
@@ -68,6 +108,7 @@ function registerTools(server, client) {
68
108
  if (items.length < FILTER_PAGE_SIZE) break;
69
109
  if (p === MAX_FILTER_PAGES) truncated = true;
70
110
  }
111
+ recordToolMetric("list_files", "success", Date.now() - start, requestId);
71
112
  logger.info({ event: "tool_call_end", tool: "list_files", duration_ms: Date.now() - start, scanned, matched: matches.length, truncated });
72
113
  return {
73
114
  content: [{ type: "text", text: JSON.stringify({
@@ -79,6 +120,7 @@ function registerTools(server, client) {
79
120
  }, null, 2) }]
80
121
  };
81
122
  } catch (err) {
123
+ recordToolMetric("list_files", "error", Date.now() - start, requestId, {}, err);
82
124
  logger.error({ event: "tool_call_error", tool: "list_files", duration_ms: Date.now() - start, error: String(err) });
83
125
  return { content: [{ type: "text", text: `Failed to list files: ${err}` }], isError: true };
84
126
  }
@@ -99,11 +141,14 @@ function registerTools(server, client) {
99
141
  async ({ file_id }) => {
100
142
  const start = Date.now();
101
143
  logger.info({ event: "tool_call", tool: "get_file", file_id });
144
+ const requestId = emitToolClick("get_file", { file_id });
102
145
  try {
103
146
  const file = await client.getFile(file_id);
147
+ recordToolMetric("get_file", "success", Date.now() - start, requestId, { file_id });
104
148
  logger.info({ event: "tool_call_end", tool: "get_file", duration_ms: Date.now() - start });
105
149
  return { content: [{ type: "text", text: JSON.stringify(file, null, 2) }] };
106
150
  } catch (err) {
151
+ recordToolMetric("get_file", "error", Date.now() - start, requestId, { file_id }, err);
107
152
  logger.error({ event: "tool_call_error", tool: "get_file", duration_ms: Date.now() - start, error: String(err) });
108
153
  return { content: [{ type: "text", text: `Failed to get file: ${err}` }], isError: true };
109
154
  }
@@ -124,11 +169,14 @@ function registerTools(server, client) {
124
169
  async ({ file_id }) => {
125
170
  const start = Date.now();
126
171
  logger.info({ event: "tool_call", tool: "get_note", file_id });
172
+ const requestId = emitToolClick("get_note", { file_id });
127
173
  try {
128
174
  const file = await client.getFile(file_id);
175
+ recordToolMetric("get_note", "success", Date.now() - start, requestId, { file_id });
129
176
  logger.info({ event: "tool_call_end", tool: "get_note", duration_ms: Date.now() - start });
130
177
  return { content: [{ type: "text", text: JSON.stringify(file.note_list ?? [], null, 2) }] };
131
178
  } catch (err) {
179
+ recordToolMetric("get_note", "error", Date.now() - start, requestId, { file_id }, err);
132
180
  logger.error({ event: "tool_call_error", tool: "get_note", duration_ms: Date.now() - start, error: String(err) });
133
181
  return { content: [{ type: "text", text: `Failed to get note: ${err}` }], isError: true };
134
182
  }
@@ -149,11 +197,14 @@ function registerTools(server, client) {
149
197
  async ({ file_id }) => {
150
198
  const start = Date.now();
151
199
  logger.info({ event: "tool_call", tool: "get_transcript", file_id });
200
+ const requestId = emitToolClick("get_transcript", { file_id });
152
201
  try {
153
202
  const file = await client.getFile(file_id);
203
+ recordToolMetric("get_transcript", "success", Date.now() - start, requestId, { file_id });
154
204
  logger.info({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start });
155
205
  return { content: [{ type: "text", text: JSON.stringify(file.source_list ?? [], null, 2) }] };
156
206
  } catch (err) {
207
+ recordToolMetric("get_transcript", "error", Date.now() - start, requestId, { file_id }, err);
157
208
  logger.error({ event: "tool_call_error", tool: "get_transcript", duration_ms: Date.now() - start, error: String(err) });
158
209
  return { content: [{ type: "text", text: `Failed to get transcript: ${err}` }], isError: true };
159
210
  }
@@ -173,11 +224,14 @@ function registerTools(server, client) {
173
224
  async () => {
174
225
  const start = Date.now();
175
226
  logger.info({ event: "tool_call", tool: "get_current_user" });
227
+ const requestId = emitToolClick("get_current_user");
176
228
  try {
177
229
  const user = await client.getCurrentUser();
230
+ recordToolMetric("get_current_user", "success", Date.now() - start, requestId);
178
231
  logger.info({ event: "tool_call_end", tool: "get_current_user", duration_ms: Date.now() - start });
179
232
  return { content: [{ type: "text", text: JSON.stringify(user, null, 2) }] };
180
233
  } catch (err) {
234
+ recordToolMetric("get_current_user", "error", Date.now() - start, requestId, {}, err);
181
235
  logger.error({ event: "tool_call_error", tool: "get_current_user", duration_ms: Date.now() - start, error: String(err) });
182
236
  return { content: [{ type: "text", text: `Failed to get user info: ${err}` }], isError: true };
183
237
  }
@@ -186,6 +240,6 @@ function registerTools(server, client) {
186
240
  }
187
241
 
188
242
  export {
189
- logger,
243
+ normalizeMcpHost,
190
244
  registerTools
191
245
  };
@@ -0,0 +1,60 @@
1
+ // src/metrics/registry.ts
2
+ import { Registry, Counter, Histogram, Gauge, collectDefaultMetrics } from "prom-client";
3
+ var registry = new Registry();
4
+ collectDefaultMetrics({ register: registry });
5
+ var httpRequestDuration = new Histogram({
6
+ name: "http_request_duration_seconds",
7
+ help: "HTTP request latency in seconds",
8
+ labelNames: ["method", "handler", "status"],
9
+ buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
10
+ registers: [registry]
11
+ });
12
+ var httpRequestsInProgress = new Gauge({
13
+ name: "http_requests_in_progress",
14
+ help: "Number of in-progress HTTP requests",
15
+ labelNames: ["method", "handler"],
16
+ registers: [registry]
17
+ });
18
+ var mcpToolCalls = new Counter({
19
+ name: "mcp_tool_calls_total",
20
+ help: "MCP tool invocation count",
21
+ labelNames: ["tool", "status"],
22
+ registers: [registry]
23
+ });
24
+ var mcpToolDuration = new Histogram({
25
+ name: "mcp_tool_duration_seconds",
26
+ help: "MCP tool execution latency in seconds",
27
+ labelNames: ["tool"],
28
+ buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30],
29
+ registers: [registry]
30
+ });
31
+ var oauthTokenRefresh = new Counter({
32
+ name: "oauth_token_refresh_total",
33
+ help: "OAuth token refresh attempts grouped by outcome",
34
+ labelNames: ["result"],
35
+ registers: [registry]
36
+ });
37
+ var httpClientRequests = new Counter({
38
+ name: "http_client_requests_total",
39
+ help: "Outbound HTTP requests to upstream services",
40
+ labelNames: ["host", "endpoint", "status"],
41
+ registers: [registry]
42
+ });
43
+ var httpClientDuration = new Histogram({
44
+ name: "http_client_duration_seconds",
45
+ help: "Outbound HTTP request latency in seconds",
46
+ labelNames: ["host", "endpoint"],
47
+ buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
48
+ registers: [registry]
49
+ });
50
+
51
+ export {
52
+ registry,
53
+ httpRequestDuration,
54
+ httpRequestsInProgress,
55
+ mcpToolCalls,
56
+ mcpToolDuration,
57
+ oauthTokenRefresh,
58
+ httpClientRequests,
59
+ httpClientDuration
60
+ };