@plaud-ai/mcp 0.3.9 → 0.3.11
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/{chunk-RUFCT6DQ.js → chunk-DIPROABB.js} +4 -3
- package/dist/{chunk-FSNFNJXH.js → chunk-O6BKY23H.js} +25 -18
- package/dist/chunk-OEZV5MA4.js +20 -0
- package/dist/{chunk-EY5K2UXG.js → chunk-PZSLLSMX.js} +2 -2
- package/dist/{chunk-5NWKLF3V.js → chunk-VUPXBO2J.js} +94 -2
- package/dist/index.js +29 -14
- package/dist/{install-RTIXREYV.js → install-XYADZE7L.js} +15 -5
- package/dist/instrumentation.js +54 -0
- package/dist/{server-SYSE62NO.js → server-KMU2TWA5.js} +477 -91
- package/dist/{server-NKCNUA6P.js → server-P6CNPNJ6.js} +2 -2
- package/package.json +10 -1
- package/dist/chunk-NPCCDRWQ.js +0 -10
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { Registry, Counter, Histogram, Gauge, collectDefaultMetrics } from "prom-client";
|
|
3
3
|
var registry = new Registry();
|
|
4
4
|
collectDefaultMetrics({ register: registry });
|
|
5
|
+
var LATENCY_BUCKETS = [0.01, 0.05, 0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 10, 30];
|
|
5
6
|
var httpRequestDuration = new Histogram({
|
|
6
7
|
name: "http_request_duration_seconds",
|
|
7
8
|
help: "HTTP request latency in seconds",
|
|
8
9
|
labelNames: ["method", "handler", "status"],
|
|
9
|
-
buckets:
|
|
10
|
+
buckets: LATENCY_BUCKETS,
|
|
10
11
|
registers: [registry]
|
|
11
12
|
});
|
|
12
13
|
var httpRequestsInProgress = new Gauge({
|
|
@@ -25,7 +26,7 @@ var mcpToolDuration = new Histogram({
|
|
|
25
26
|
name: "mcp_tool_duration_seconds",
|
|
26
27
|
help: "MCP tool execution latency in seconds",
|
|
27
28
|
labelNames: ["tool"],
|
|
28
|
-
buckets:
|
|
29
|
+
buckets: LATENCY_BUCKETS,
|
|
29
30
|
registers: [registry]
|
|
30
31
|
});
|
|
31
32
|
var oauthTokenRefresh = new Counter({
|
|
@@ -44,7 +45,7 @@ var httpClientDuration = new Histogram({
|
|
|
44
45
|
name: "http_client_duration_seconds",
|
|
45
46
|
help: "Outbound HTTP request latency in seconds",
|
|
46
47
|
labelNames: ["host", "endpoint"],
|
|
47
|
-
buckets:
|
|
48
|
+
buckets: LATENCY_BUCKETS,
|
|
48
49
|
registers: [registry]
|
|
49
50
|
});
|
|
50
51
|
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
capture,
|
|
3
3
|
classifyError,
|
|
4
|
+
describeError,
|
|
4
5
|
loadBlockContent,
|
|
5
6
|
localDayEnd,
|
|
6
7
|
localDayStart,
|
|
7
8
|
parseApiTimestamp
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VUPXBO2J.js";
|
|
9
10
|
import {
|
|
10
11
|
logger
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OEZV5MA4.js";
|
|
12
13
|
import {
|
|
13
14
|
mcpToolCalls,
|
|
14
15
|
mcpToolDuration
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DIPROABB.js";
|
|
16
17
|
|
|
17
18
|
// src/tools/index.ts
|
|
18
19
|
import { z } from "zod";
|
|
@@ -20,6 +21,7 @@ import { randomUUID } from "crypto";
|
|
|
20
21
|
var toolHooks = null;
|
|
21
22
|
var MAX_FILTER_PAGES = 5;
|
|
22
23
|
var FILTER_PAGE_SIZE = 100;
|
|
24
|
+
var MIN_PAGE_SIZE = 10;
|
|
23
25
|
var TRANSCRIPT_PAGE_SIZE = 50;
|
|
24
26
|
var TRANSCRIPT_BLOCKS = ["transaction", "outline", "transaction_polish", "mark_memo"];
|
|
25
27
|
var BLOCK_ITEMS_KEY = { mark_memo: "marks" };
|
|
@@ -38,7 +40,7 @@ async function resolveNotes(noteList) {
|
|
|
38
40
|
} catch (err) {
|
|
39
41
|
return {
|
|
40
42
|
...note,
|
|
41
|
-
data_content_error: err
|
|
43
|
+
data_content_error: err ? describeError(err) : "Failed to fetch content from data_link"
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
46
|
})
|
|
@@ -114,8 +116,13 @@ function registerTools(server, client, hooks) {
|
|
|
114
116
|
openWorldHint: true
|
|
115
117
|
},
|
|
116
118
|
inputSchema: {
|
|
117
|
-
|
|
118
|
-
page_size
|
|
119
|
+
// Advertise the lower bound instead of making clients discover it:
|
|
120
|
+
// the API rejects page_size < 10 with a 422, and an unconstrained
|
|
121
|
+
// schema meant they learned that from a failed call (#670875).
|
|
122
|
+
// Deliberately NO upper bound — the API's real ceiling is unverified,
|
|
123
|
+
// and capping here would newly reject page sizes it may well accept.
|
|
124
|
+
page: z.number().int().min(1).optional().default(1).describe("Page number, 1-based (ignored when filters are set)"),
|
|
125
|
+
page_size: z.number().int().min(MIN_PAGE_SIZE).optional().default(20).describe(`Items per page, minimum ${MIN_PAGE_SIZE} (ignored when filters are set)`),
|
|
119
126
|
query: z.string().optional().describe("Case-insensitive substring match on recording name"),
|
|
120
127
|
date_from: z.string().optional().describe("Start date inclusive, YYYY-MM-DD, interpreted in the server's timezone"),
|
|
121
128
|
date_to: z.string().optional().describe("End date inclusive, YYYY-MM-DD, interpreted in the server's timezone")
|
|
@@ -169,17 +176,17 @@ function registerTools(server, client, hooks) {
|
|
|
169
176
|
};
|
|
170
177
|
} catch (err) {
|
|
171
178
|
recordToolMetric("list_files", "error", Date.now() - start, requestId, {}, err);
|
|
172
|
-
logger.error({ event: "tool_call_error", tool: "list_files", duration_ms: Date.now() - start, error:
|
|
173
|
-
return { content: [{ type: "text", text: `Failed to list files: ${err}` }], isError: true };
|
|
179
|
+
logger.error({ event: "tool_call_error", tool: "list_files", duration_ms: Date.now() - start, error: describeError(err) });
|
|
180
|
+
return { content: [{ type: "text", text: `Failed to list files: ${describeError(err)}` }], isError: true };
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
);
|
|
177
184
|
server.registerTool(
|
|
178
185
|
"get_file",
|
|
179
186
|
{
|
|
180
|
-
description: "Get
|
|
187
|
+
description: "Get the full record for a Plaud recording: metadata, the audio download URL, and an inventory of its transcript blocks and notes. This is the tool that returns audio \u2014 `presigned_url` is a signed link to the recording's audio file, valid 24h, and is how you download it or hand the user a download link. Call this whenever the user asks for the audio, the download link, or the recording file itself; use get_transcript or get_note to read transcript or note bodies instead.",
|
|
181
188
|
annotations: {
|
|
182
|
-
title: "Get recording details",
|
|
189
|
+
title: "Get recording details and audio download URL",
|
|
183
190
|
readOnlyHint: true,
|
|
184
191
|
destructiveHint: false,
|
|
185
192
|
openWorldHint: true
|
|
@@ -214,8 +221,8 @@ Note: note_list ${linkedNotes.map((t) => `\`${t}\``).join(", ")} returned an emp
|
|
|
214
221
|
return { content: [{ type: "text", text }] };
|
|
215
222
|
} catch (err) {
|
|
216
223
|
recordToolMetric("get_file", "error", Date.now() - start, requestId, { file_id }, err);
|
|
217
|
-
logger.error({ event: "tool_call_error", tool: "get_file", duration_ms: Date.now() - start, error:
|
|
218
|
-
return { content: [{ type: "text", text: `Failed to get file: ${err}` }], isError: true };
|
|
224
|
+
logger.error({ event: "tool_call_error", tool: "get_file", duration_ms: Date.now() - start, error: describeError(err) });
|
|
225
|
+
return { content: [{ type: "text", text: `Failed to get file: ${describeError(err)}` }], isError: true };
|
|
219
226
|
}
|
|
220
227
|
}
|
|
221
228
|
);
|
|
@@ -243,8 +250,8 @@ Note: note_list ${linkedNotes.map((t) => `\`${t}\``).join(", ")} returned an emp
|
|
|
243
250
|
return { content: [{ type: "text", text: JSON.stringify(notes, null, 2) }] };
|
|
244
251
|
} catch (err) {
|
|
245
252
|
recordToolMetric("get_note", "error", Date.now() - start, requestId, { file_id }, err);
|
|
246
|
-
logger.error({ event: "tool_call_error", tool: "get_note", duration_ms: Date.now() - start, error:
|
|
247
|
-
return { content: [{ type: "text", text: `Failed to get note: ${err}` }], isError: true };
|
|
253
|
+
logger.error({ event: "tool_call_error", tool: "get_note", duration_ms: Date.now() - start, error: describeError(err) });
|
|
254
|
+
return { content: [{ type: "text", text: `Failed to get note: ${describeError(err)}` }], isError: true };
|
|
248
255
|
}
|
|
249
256
|
}
|
|
250
257
|
);
|
|
@@ -337,8 +344,8 @@ Note: note_list ${linkedNotes.map((t) => `\`${t}\``).join(", ")} returned an emp
|
|
|
337
344
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
338
345
|
} catch (err) {
|
|
339
346
|
recordToolMetric("get_transcript", "error", Date.now() - start, requestId, { file_id }, err);
|
|
340
|
-
logger.error({ event: "tool_call_error", tool: "get_transcript", duration_ms: Date.now() - start, error:
|
|
341
|
-
return { content: [{ type: "text", text: `Failed to get transcript: ${err}` }], isError: true };
|
|
347
|
+
logger.error({ event: "tool_call_error", tool: "get_transcript", duration_ms: Date.now() - start, error: describeError(err) });
|
|
348
|
+
return { content: [{ type: "text", text: `Failed to get transcript: ${describeError(err)}` }], isError: true };
|
|
342
349
|
}
|
|
343
350
|
}
|
|
344
351
|
);
|
|
@@ -364,8 +371,8 @@ Note: note_list ${linkedNotes.map((t) => `\`${t}\``).join(", ")} returned an emp
|
|
|
364
371
|
return { content: [{ type: "text", text: JSON.stringify(user, null, 2) }] };
|
|
365
372
|
} catch (err) {
|
|
366
373
|
recordToolMetric("get_current_user", "error", Date.now() - start, requestId, {}, err);
|
|
367
|
-
logger.error({ event: "tool_call_error", tool: "get_current_user", duration_ms: Date.now() - start, error:
|
|
368
|
-
return { content: [{ type: "text", text: `Failed to get user info: ${err}` }], isError: true };
|
|
374
|
+
logger.error({ event: "tool_call_error", tool: "get_current_user", duration_ms: Date.now() - start, error: describeError(err) });
|
|
375
|
+
return { content: [{ type: "text", text: `Failed to get user info: ${describeError(err)}` }], isError: true };
|
|
369
376
|
}
|
|
370
377
|
}
|
|
371
378
|
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/logger.ts
|
|
2
|
+
import pino from "pino";
|
|
3
|
+
import { trace } from "@opentelemetry/api";
|
|
4
|
+
var logger = pino(
|
|
5
|
+
{
|
|
6
|
+
level: process.env.LOG_LEVEL ?? "info",
|
|
7
|
+
// Tempo tracing (SOP §5⑤): stamp trace_id/span_id on every log line so logs
|
|
8
|
+
// can be found in OpenSearch by trace_id. No-op when no span is active
|
|
9
|
+
// (stdio mode, or OTEL_SDK_DISABLED=true).
|
|
10
|
+
mixin() {
|
|
11
|
+
const ctx = trace.getActiveSpan()?.spanContext();
|
|
12
|
+
return ctx ? { trace_id: ctx.traceId, span_id: ctx.spanId } : {};
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
pino.destination(2)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
logger
|
|
20
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PlaudClient,
|
|
3
3
|
capture
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VUPXBO2J.js";
|
|
5
5
|
import {
|
|
6
6
|
httpClientDuration,
|
|
7
7
|
httpClientRequests
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DIPROABB.js";
|
|
9
9
|
|
|
10
10
|
// src/config.ts
|
|
11
11
|
function buildExtraHeaders() {
|
|
@@ -32,6 +32,38 @@ function classifyError(err) {
|
|
|
32
32
|
return "client_error";
|
|
33
33
|
return "unknown";
|
|
34
34
|
}
|
|
35
|
+
function isTransportError(err) {
|
|
36
|
+
const type = classifyError(err);
|
|
37
|
+
return type === "network" || type === "timeout" || type === "server_error";
|
|
38
|
+
}
|
|
39
|
+
var MAX_CAUSE_DEPTH = 5;
|
|
40
|
+
var MAX_DESCRIPTION_LENGTH = 400;
|
|
41
|
+
function summarizeError(err) {
|
|
42
|
+
if (!(err instanceof Error))
|
|
43
|
+
return String(err ?? "unknown error");
|
|
44
|
+
const code = err.code;
|
|
45
|
+
const suffix = typeof code === "string" && !err.message.includes(code) ? ` (${code})` : "";
|
|
46
|
+
return `${err.name}: ${err.message}${suffix}`;
|
|
47
|
+
}
|
|
48
|
+
function describeError(err) {
|
|
49
|
+
const chain = [];
|
|
50
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51
|
+
let current = err;
|
|
52
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current && !seen.has(current); depth++) {
|
|
53
|
+
seen.add(current);
|
|
54
|
+
chain.push(summarizeError(current));
|
|
55
|
+
const aggregated = current instanceof AggregateError ? current.errors : void 0;
|
|
56
|
+
if (aggregated && aggregated.length > 0) {
|
|
57
|
+
chain.push(aggregated.map(summarizeError).join("; "));
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
current = current instanceof Error ? current.cause : void 0;
|
|
61
|
+
}
|
|
62
|
+
const text = chain.join(" | cause: ");
|
|
63
|
+
if (text.length === 0)
|
|
64
|
+
return "unknown error";
|
|
65
|
+
return text.length > MAX_DESCRIPTION_LENGTH ? `${text.slice(0, MAX_DESCRIPTION_LENGTH - 1)}\u2026` : text;
|
|
66
|
+
}
|
|
35
67
|
function oauthCallbackErrorType(status) {
|
|
36
68
|
switch (status) {
|
|
37
69
|
case "denied":
|
|
@@ -176,6 +208,21 @@ var OAuth = class {
|
|
|
176
208
|
await this.tokenStore.save(tokenSet);
|
|
177
209
|
return tokenSet;
|
|
178
210
|
}
|
|
211
|
+
/** Are there stored credentials? Local-only — never touches the network, so
|
|
212
|
+
* logout can tell "nothing to do" from "cannot reach Plaud". */
|
|
213
|
+
async hasStoredToken() {
|
|
214
|
+
return await this.tokenStore.load() !== null;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The current access token, refreshing it first when it is about to expire.
|
|
218
|
+
*
|
|
219
|
+
* `null` means genuinely unauthenticated: nothing stored, no refresh token, or
|
|
220
|
+
* the refresh token was rejected. A transport failure instead THROWS — it says
|
|
221
|
+
* nothing about the credentials, and reporting it as unauthenticated is what
|
|
222
|
+
* pushed #670875's reporter into a re-login loop (and would have let callers
|
|
223
|
+
* delete a perfectly good token file). Callers that only want to know whether
|
|
224
|
+
* we are logged in should use `isTransportError` to tell the two apart.
|
|
225
|
+
*/
|
|
179
226
|
async getAccessToken() {
|
|
180
227
|
const tokenSet = await this.tokenStore.load();
|
|
181
228
|
if (!tokenSet)
|
|
@@ -185,8 +232,10 @@ var OAuth = class {
|
|
|
185
232
|
try {
|
|
186
233
|
const refreshed = await this.refresh(tokenSet.refresh_token);
|
|
187
234
|
return refreshed.access_token;
|
|
188
|
-
} catch {
|
|
189
|
-
|
|
235
|
+
} catch (err) {
|
|
236
|
+
if (!isTransportError(err))
|
|
237
|
+
return null;
|
|
238
|
+
throw err;
|
|
190
239
|
}
|
|
191
240
|
}
|
|
192
241
|
return null;
|
|
@@ -312,6 +361,46 @@ var PlaudClient = class {
|
|
|
312
361
|
}
|
|
313
362
|
};
|
|
314
363
|
|
|
364
|
+
// ../shared/dist/proxy.js
|
|
365
|
+
var PROXY_ENV_VARS = [
|
|
366
|
+
"HTTP_PROXY",
|
|
367
|
+
"http_proxy",
|
|
368
|
+
"HTTPS_PROXY",
|
|
369
|
+
"https_proxy",
|
|
370
|
+
"ALL_PROXY",
|
|
371
|
+
"all_proxy"
|
|
372
|
+
];
|
|
373
|
+
function proxyEnvVars(env = process.env) {
|
|
374
|
+
return PROXY_ENV_VARS.filter((key) => (env[key] ?? "").trim().length > 0);
|
|
375
|
+
}
|
|
376
|
+
async function installProxyDispatcher(env = process.env) {
|
|
377
|
+
const active = proxyEnvVars(env);
|
|
378
|
+
if (active.length === 0)
|
|
379
|
+
return null;
|
|
380
|
+
try {
|
|
381
|
+
const { EnvHttpProxyAgent, setGlobalDispatcher } = await import("undici");
|
|
382
|
+
setGlobalDispatcher(withoutExperimentalWarning(() => new EnvHttpProxyAgent()));
|
|
383
|
+
return active;
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function withoutExperimentalWarning(build) {
|
|
389
|
+
const original = process.emitWarning;
|
|
390
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
391
|
+
const [first, second] = rest;
|
|
392
|
+
const code = typeof first === "object" && first !== null ? first.code : second;
|
|
393
|
+
if (code === "UNDICI-EHPA")
|
|
394
|
+
return;
|
|
395
|
+
return original.call(process, warning, ...rest);
|
|
396
|
+
});
|
|
397
|
+
try {
|
|
398
|
+
return build();
|
|
399
|
+
} finally {
|
|
400
|
+
process.emitWarning = original;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
315
404
|
// ../shared/dist/oauth-callback-server.js
|
|
316
405
|
import { createServer } from "http";
|
|
317
406
|
var SUCCESS_HTML = '<!doctype html><html><head><meta charset="utf-8"><title>Plaud</title></head><body style="font-family:system-ui;padding:2rem;text-align:center;"><h1>Authorization successful!</h1><p>You can close this tab.</p></body></html>';
|
|
@@ -905,9 +994,12 @@ function extractIdentity(user) {
|
|
|
905
994
|
|
|
906
995
|
export {
|
|
907
996
|
classifyError,
|
|
997
|
+
isTransportError,
|
|
998
|
+
describeError,
|
|
908
999
|
oauthCallbackErrorType,
|
|
909
1000
|
clientUserIdFromAccessToken,
|
|
910
1001
|
PlaudClient,
|
|
1002
|
+
installProxyDispatcher,
|
|
911
1003
|
runOAuthCallback,
|
|
912
1004
|
loadBlockContent,
|
|
913
1005
|
parseApiTimestamp,
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PZSLLSMX.js";
|
|
5
5
|
import {
|
|
6
6
|
loadSkills
|
|
7
7
|
} from "./chunk-242FRP4P.js";
|
|
8
8
|
import {
|
|
9
9
|
normalizeMcpHost,
|
|
10
10
|
registerTools
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-O6BKY23H.js";
|
|
12
12
|
import {
|
|
13
13
|
capture,
|
|
14
14
|
classifyError,
|
|
15
15
|
clearUser,
|
|
16
16
|
clientUserIdFromAccessToken,
|
|
17
|
+
describeError,
|
|
17
18
|
extractIdentity,
|
|
18
19
|
initTelemetry,
|
|
20
|
+
installProxyDispatcher,
|
|
19
21
|
oauthCallbackErrorType,
|
|
20
22
|
runOAuthCallback,
|
|
21
23
|
setMcpHost,
|
|
22
24
|
setUser,
|
|
23
25
|
shutdown
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import
|
|
26
|
-
|
|
26
|
+
} from "./chunk-VUPXBO2J.js";
|
|
27
|
+
import {
|
|
28
|
+
logger
|
|
29
|
+
} from "./chunk-OEZV5MA4.js";
|
|
30
|
+
import "./chunk-DIPROABB.js";
|
|
27
31
|
|
|
28
32
|
// src/index.ts
|
|
29
33
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -31,7 +35,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
31
35
|
import open from "open";
|
|
32
36
|
var server = new McpServer({
|
|
33
37
|
name: "plaud",
|
|
34
|
-
version: "0.3.
|
|
38
|
+
version: "0.3.11"
|
|
35
39
|
});
|
|
36
40
|
var CALLBACK_PORT = 8199;
|
|
37
41
|
var LOGIN_TIMEOUT_MS = 12e4;
|
|
@@ -39,7 +43,15 @@ server.registerTool("login", {
|
|
|
39
43
|
description: "Log in, sign in, or authenticate with Plaud account via OAuth (opens browser)"
|
|
40
44
|
}, async () => {
|
|
41
45
|
const client = getClient();
|
|
42
|
-
|
|
46
|
+
let existingToken;
|
|
47
|
+
try {
|
|
48
|
+
existingToken = await client.auth.getAccessToken();
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: "text", text: `Cannot reach Plaud to check the saved credentials: ${describeError(err)}` }],
|
|
52
|
+
isError: true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
43
55
|
if (existingToken) {
|
|
44
56
|
try {
|
|
45
57
|
await client.getCurrentUser();
|
|
@@ -50,7 +62,7 @@ server.registerTool("login", {
|
|
|
50
62
|
await client.auth.logout();
|
|
51
63
|
} else {
|
|
52
64
|
return {
|
|
53
|
-
content: [{ type: "text", text: `Failed to verify login state: ${
|
|
65
|
+
content: [{ type: "text", text: `Failed to verify login state: ${describeError(err)}` }],
|
|
54
66
|
isError: true
|
|
55
67
|
};
|
|
56
68
|
}
|
|
@@ -124,8 +136,7 @@ server.registerTool("logout", {
|
|
|
124
136
|
description: "Log out, sign out, revoke authorization, and disconnect from Plaud account"
|
|
125
137
|
}, async () => {
|
|
126
138
|
const client = getClient();
|
|
127
|
-
|
|
128
|
-
if (!existingToken) {
|
|
139
|
+
if (!await client.auth.hasStoredToken()) {
|
|
129
140
|
return { content: [{ type: "text", text: "Already logged out." }] };
|
|
130
141
|
}
|
|
131
142
|
try {
|
|
@@ -155,10 +166,14 @@ async function registerSkillPrompts() {
|
|
|
155
166
|
}
|
|
156
167
|
}
|
|
157
168
|
async function main() {
|
|
169
|
+
const proxied = await installProxyDispatcher();
|
|
170
|
+
if (proxied) {
|
|
171
|
+
logger.info({ event: "proxy_dispatcher_installed", proxy_env: proxied });
|
|
172
|
+
}
|
|
158
173
|
const sub = process.argv[2];
|
|
159
174
|
const sub2 = process.argv[3];
|
|
160
175
|
if (sub === "install") {
|
|
161
|
-
const { runInstall } = await import("./install-
|
|
176
|
+
const { runInstall } = await import("./install-XYADZE7L.js");
|
|
162
177
|
const args = process.argv.slice(3);
|
|
163
178
|
const yes = args.some((a) => a === "--yes" || a === "-y");
|
|
164
179
|
const noLogin = args.some((a) => a === "--no-login");
|
|
@@ -191,8 +206,8 @@ async function main() {
|
|
|
191
206
|
return;
|
|
192
207
|
}
|
|
193
208
|
if (sub === "http") {
|
|
194
|
-
const { startHttpServer } = await import("./server-
|
|
195
|
-
const { startMetricsServer } = await import("./server-
|
|
209
|
+
const { startHttpServer } = await import("./server-KMU2TWA5.js");
|
|
210
|
+
const { startMetricsServer } = await import("./server-P6CNPNJ6.js");
|
|
196
211
|
startMetricsServer();
|
|
197
212
|
startHttpServer();
|
|
198
213
|
return;
|
|
@@ -217,7 +232,7 @@ Usage:
|
|
|
217
232
|
try {
|
|
218
233
|
await initTelemetry({
|
|
219
234
|
surface: "mcp",
|
|
220
|
-
appVersion: "0.3.
|
|
235
|
+
appVersion: "0.3.11",
|
|
221
236
|
transport: "stdio"
|
|
222
237
|
});
|
|
223
238
|
} catch {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PZSLLSMX.js";
|
|
4
4
|
import {
|
|
5
5
|
commandPathIsStale,
|
|
6
6
|
copyToClipboard,
|
|
@@ -11,9 +11,11 @@ import {
|
|
|
11
11
|
skillsCombined
|
|
12
12
|
} from "./chunk-242FRP4P.js";
|
|
13
13
|
import {
|
|
14
|
+
describeError,
|
|
15
|
+
isTransportError,
|
|
14
16
|
runOAuthCallback
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-VUPXBO2J.js";
|
|
18
|
+
import "./chunk-DIPROABB.js";
|
|
17
19
|
|
|
18
20
|
// src/install.ts
|
|
19
21
|
import { createInterface } from "readline/promises";
|
|
@@ -489,7 +491,8 @@ async function runLogin() {
|
|
|
489
491
|
}
|
|
490
492
|
}
|
|
491
493
|
}
|
|
492
|
-
} catch {
|
|
494
|
+
} catch (err) {
|
|
495
|
+
if (isTransportError(err)) return { status: "already-authed" };
|
|
493
496
|
await client.auth.logout().catch(() => void 0);
|
|
494
497
|
}
|
|
495
498
|
const { url, codeVerifier, state } = client.auth.createAuthorizationRequest();
|
|
@@ -613,12 +616,19 @@ async function doLoginStep(nonInteractive) {
|
|
|
613
616
|
if (msg.includes("401") || msg.includes("Not authenticated")) {
|
|
614
617
|
console.log(" saved token was revoked server-side \u2014 clearing and re-authenticating.");
|
|
615
618
|
await client.auth.logout().catch(() => void 0);
|
|
619
|
+
} else if (isTransportError(err)) {
|
|
620
|
+
console.log(` cannot reach Plaud to verify the saved login \u2014 keeping it: ${describeError(err)}`);
|
|
621
|
+
return { status: "already-authed" };
|
|
616
622
|
} else {
|
|
617
623
|
console.log(" token present but user lookup failed \u2014 will re-auth.");
|
|
618
624
|
}
|
|
619
625
|
}
|
|
620
626
|
}
|
|
621
|
-
} catch {
|
|
627
|
+
} catch (err) {
|
|
628
|
+
if (isTransportError(err)) {
|
|
629
|
+
console.log(` cannot reach Plaud to refresh the saved login \u2014 keeping it: ${describeError(err)}`);
|
|
630
|
+
return { status: "already-authed" };
|
|
631
|
+
}
|
|
622
632
|
}
|
|
623
633
|
if (!nonInteractive) {
|
|
624
634
|
const yes = await prompt("Log in to Plaud now? (opens browser)", true);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/instrumentation.ts
|
|
2
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
3
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
|
|
4
|
+
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
|
5
|
+
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
|
|
6
|
+
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
|
|
7
|
+
var URL_ATTRIBUTES = ["http.url", "url.full", "http.target", "url.query"];
|
|
8
|
+
function redactUrl(url) {
|
|
9
|
+
const q = url.indexOf("?");
|
|
10
|
+
return q === -1 ? url : `${url.slice(0, q)}?REDACTED`;
|
|
11
|
+
}
|
|
12
|
+
function redactSpanUrlAttributes(span) {
|
|
13
|
+
const attrs = span.attributes;
|
|
14
|
+
for (const key of URL_ATTRIBUTES) {
|
|
15
|
+
const value = attrs[key];
|
|
16
|
+
if (typeof value !== "string" || value === "") continue;
|
|
17
|
+
attrs[key] = key === "url.query" ? "REDACTED" : redactUrl(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var RedactingSpanExporter = class {
|
|
21
|
+
constructor(inner) {
|
|
22
|
+
this.inner = inner;
|
|
23
|
+
}
|
|
24
|
+
inner;
|
|
25
|
+
export(spans, resultCallback) {
|
|
26
|
+
for (const span of spans) redactSpanUrlAttributes(span);
|
|
27
|
+
this.inner.export(spans, resultCallback);
|
|
28
|
+
}
|
|
29
|
+
shutdown() {
|
|
30
|
+
return this.inner.shutdown();
|
|
31
|
+
}
|
|
32
|
+
forceFlush() {
|
|
33
|
+
return this.inner.forceFlush?.() ?? Promise.resolve();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var sdk = new NodeSDK({
|
|
37
|
+
traceExporter: new RedactingSpanExporter(new OTLPTraceExporter()),
|
|
38
|
+
instrumentations: [
|
|
39
|
+
new HttpInstrumentation(),
|
|
40
|
+
// inbound server spans
|
|
41
|
+
new ExpressInstrumentation(),
|
|
42
|
+
// route-level spans
|
|
43
|
+
new UndiciInstrumentation()
|
|
44
|
+
// outbound: native fetch, injects traceparent
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
sdk.start();
|
|
48
|
+
process.on("SIGTERM", () => sdk.shutdown().catch(() => {
|
|
49
|
+
}));
|
|
50
|
+
export {
|
|
51
|
+
RedactingSpanExporter,
|
|
52
|
+
redactSpanUrlAttributes,
|
|
53
|
+
redactUrl
|
|
54
|
+
};
|