@plaud-ai/mcp 0.3.4 → 0.3.6
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-LOGKF7DL.js → chunk-3K3M2X74.js} +1 -1
- package/dist/{chunk-D2JZW2TW.js → chunk-OYI4PXYP.js} +37 -0
- package/dist/{chunk-XWDA3G2U.js → chunk-YOMRLHNX.js} +91 -8
- package/dist/index.js +13 -8
- package/dist/{install-ETBOS6SA.js → install-3E63G6QX.js} +2 -2
- package/dist/{server-VVQEILKC.js → server-PJTOHPPJ.js} +10 -35
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/plaud-read/SKILL.md +2 -2
|
@@ -82,6 +82,16 @@ var TokenStore = class {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
// ../shared/dist/oauth.js
|
|
85
|
+
function clientUserIdFromAccessToken(token) {
|
|
86
|
+
if (!token)
|
|
87
|
+
return void 0;
|
|
88
|
+
try {
|
|
89
|
+
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString());
|
|
90
|
+
return typeof payload.sub === "string" && payload.sub.length > 0 ? payload.sub : void 0;
|
|
91
|
+
} catch {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
85
95
|
var DEFAULT_AUTHORIZATION_URL = "https://web.plaud.ai/platform/oauth";
|
|
86
96
|
var DEFAULT_TOKEN_URL = "https://platform.plaud.ai/developer/api/oauth/third-party/access-token";
|
|
87
97
|
var DEFAULT_REFRESH_URL = "https://platform.plaud.ai/developer/api/oauth/third-party/access-token/refresh";
|
|
@@ -419,6 +429,23 @@ function runOAuthCallback(opts) {
|
|
|
419
429
|
});
|
|
420
430
|
}
|
|
421
431
|
|
|
432
|
+
// ../shared/dist/source-block.js
|
|
433
|
+
async function loadBlockContent(block) {
|
|
434
|
+
if (!block)
|
|
435
|
+
return "";
|
|
436
|
+
const inline = block.data_content;
|
|
437
|
+
if (typeof inline === "string" && inline.length > 0)
|
|
438
|
+
return inline;
|
|
439
|
+
const link = block.data_link;
|
|
440
|
+
if (typeof link === "string" && link.length > 0) {
|
|
441
|
+
const res = await fetch(link);
|
|
442
|
+
if (!res.ok)
|
|
443
|
+
throw new Error(`Failed to fetch block content from data_link (HTTP ${res.status})`);
|
|
444
|
+
return await res.text();
|
|
445
|
+
}
|
|
446
|
+
return "";
|
|
447
|
+
}
|
|
448
|
+
|
|
422
449
|
// ../telemetry/dist/client.js
|
|
423
450
|
import { PostHog } from "posthog-node";
|
|
424
451
|
|
|
@@ -497,6 +524,8 @@ var EMAIL_REGEX = /[\w.+-]+@[\w-]+\.[\w.-]+/;
|
|
|
497
524
|
var ALLOWED_PROPERTIES = /* @__PURE__ */ new Set([
|
|
498
525
|
// Global common properties (Plaud Common Event Properties + Spec §2)
|
|
499
526
|
"user_id",
|
|
527
|
+
"client_user_id",
|
|
528
|
+
// per-OAuth-client user id (JWT sub, `client_user_…`); DA common param across web/CLI/stdio/HTTP (Mirela 2026-06-29)
|
|
500
529
|
"member_id",
|
|
501
530
|
// fill-if-present; OAuth /users/current doesn't return it yet (Q16)
|
|
502
531
|
"workspace_id",
|
|
@@ -696,6 +725,7 @@ var state = {
|
|
|
696
725
|
memberId: null,
|
|
697
726
|
workspaceId: null,
|
|
698
727
|
role: null,
|
|
728
|
+
clientUserId: null,
|
|
699
729
|
mcpHost: null
|
|
700
730
|
};
|
|
701
731
|
async function initTelemetry(options) {
|
|
@@ -716,6 +746,7 @@ async function initTelemetry(options) {
|
|
|
716
746
|
state.memberId = identity?.memberId ?? null;
|
|
717
747
|
state.workspaceId = identity?.workspaceId ?? null;
|
|
718
748
|
state.role = identity?.role ?? null;
|
|
749
|
+
state.clientUserId = identity?.clientUserId ?? null;
|
|
719
750
|
state.currentDistinctId = identity?.idHash || userId || await getAnonymousId();
|
|
720
751
|
state.initialised = true;
|
|
721
752
|
}
|
|
@@ -727,6 +758,7 @@ async function setUser(userId, identity) {
|
|
|
727
758
|
state.memberId = identity?.memberId ?? null;
|
|
728
759
|
state.workspaceId = identity?.workspaceId ?? null;
|
|
729
760
|
state.role = identity?.role ?? null;
|
|
761
|
+
state.clientUserId = identity?.clientUserId ?? null;
|
|
730
762
|
if (state.surface) {
|
|
731
763
|
try {
|
|
732
764
|
await saveUserIdentity(state.surface, userId, identity);
|
|
@@ -752,6 +784,7 @@ async function clearUser() {
|
|
|
752
784
|
state.memberId = null;
|
|
753
785
|
state.workspaceId = null;
|
|
754
786
|
state.role = null;
|
|
787
|
+
state.clientUserId = null;
|
|
755
788
|
if (state.surface) {
|
|
756
789
|
try {
|
|
757
790
|
await clearUserIdentity(state.surface);
|
|
@@ -797,6 +830,8 @@ function buildCommonProperties() {
|
|
|
797
830
|
out.transport = state.transport;
|
|
798
831
|
if (state.currentUserId)
|
|
799
832
|
out.user_id = state.currentUserId;
|
|
833
|
+
if (state.clientUserId)
|
|
834
|
+
out.client_user_id = state.clientUserId;
|
|
800
835
|
if (state.memberId)
|
|
801
836
|
out.member_id = state.memberId;
|
|
802
837
|
if (state.workspaceId)
|
|
@@ -820,8 +855,10 @@ function extractIdentity(user) {
|
|
|
820
855
|
export {
|
|
821
856
|
classifyError,
|
|
822
857
|
oauthCallbackErrorType,
|
|
858
|
+
clientUserIdFromAccessToken,
|
|
823
859
|
PlaudClient,
|
|
824
860
|
runOAuthCallback,
|
|
861
|
+
loadBlockContent,
|
|
825
862
|
shutdown,
|
|
826
863
|
initTelemetry,
|
|
827
864
|
setUser,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
capture,
|
|
3
|
-
classifyError
|
|
4
|
-
|
|
3
|
+
classifyError,
|
|
4
|
+
loadBlockContent
|
|
5
|
+
} from "./chunk-OYI4PXYP.js";
|
|
5
6
|
import {
|
|
6
7
|
logger
|
|
7
8
|
} from "./chunk-NPCCDRWQ.js";
|
|
@@ -16,6 +17,22 @@ import { randomUUID } from "crypto";
|
|
|
16
17
|
var toolHooks = null;
|
|
17
18
|
var MAX_FILTER_PAGES = 5;
|
|
18
19
|
var FILTER_PAGE_SIZE = 100;
|
|
20
|
+
var TRANSCRIPT_PAGE_SIZE = 50;
|
|
21
|
+
var TRANSCRIPT_BLOCKS = ["transaction", "outline", "transaction_polish"];
|
|
22
|
+
function encodeTranscriptCursor(offset) {
|
|
23
|
+
return Buffer.from(JSON.stringify({ o: offset }), "utf8").toString("base64url");
|
|
24
|
+
}
|
|
25
|
+
function decodeTranscriptCursor(cursor) {
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
28
|
+
if (typeof parsed?.o === "number" && Number.isInteger(parsed.o) && parsed.o >= 0) {
|
|
29
|
+
return parsed.o;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
19
36
|
function parseDate(s) {
|
|
20
37
|
if (!s) return null;
|
|
21
38
|
const d = new Date(s);
|
|
@@ -198,24 +215,90 @@ function registerTools(server, client, hooks) {
|
|
|
198
215
|
server.registerTool(
|
|
199
216
|
"get_transcript",
|
|
200
217
|
{
|
|
201
|
-
description: "Fetch the
|
|
218
|
+
description: "Fetch the timestamped transcript with speaker attribution for a Plaud recording. Defaults to the `transaction` block (real speaker names + timestamps), returned one page of utterances at a time to stay within client size limits \u2014 call again with the returned `next_cursor` to fetch the next page. Set `block` to `outline` or `transaction_polish` to fetch those blocks instead.",
|
|
202
219
|
annotations: {
|
|
203
220
|
title: "Get recording transcript",
|
|
204
221
|
readOnlyHint: true,
|
|
205
222
|
destructiveHint: false,
|
|
206
223
|
openWorldHint: true
|
|
207
224
|
},
|
|
208
|
-
inputSchema: {
|
|
225
|
+
inputSchema: {
|
|
226
|
+
file_id: z.string().describe("The file ID to retrieve transcript for"),
|
|
227
|
+
block: z.enum(TRANSCRIPT_BLOCKS).optional().describe(
|
|
228
|
+
"Which source block to fetch: `transaction` (default; raw transcript, speaker + timestamps), `transaction_polish` (AI-cleaned transcript; same per-utterance shape, keeps speaker + timestamps), or `outline`."
|
|
229
|
+
),
|
|
230
|
+
cursor: z.string().optional().describe("Opaque pagination cursor from a previous call's `next_cursor`. Omit to start from the first utterance."),
|
|
231
|
+
limit: z.number().int().min(1).max(500).optional().describe(`Max utterances to return in this page (default ${TRANSCRIPT_PAGE_SIZE}). Only applies to blocks returned as an utterance list.`)
|
|
232
|
+
}
|
|
209
233
|
},
|
|
210
|
-
async ({ file_id }) => {
|
|
234
|
+
async ({ file_id, block, cursor, limit }) => {
|
|
211
235
|
const start = Date.now();
|
|
212
|
-
|
|
236
|
+
const blockType = block ?? "transaction";
|
|
237
|
+
logger.info({ event: "tool_call", tool: "get_transcript", file_id, block: blockType });
|
|
213
238
|
const requestId = emitToolClick("get_transcript", { file_id });
|
|
214
239
|
try {
|
|
215
240
|
const file = await client.getFile(file_id);
|
|
241
|
+
const sourceList = file.source_list ?? [];
|
|
242
|
+
if (sourceList.length === 0) {
|
|
243
|
+
recordToolMetric("get_transcript", "success", Date.now() - start, requestId, { file_id });
|
|
244
|
+
logger.info({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start, paginated: false });
|
|
245
|
+
return { content: [{ type: "text", text: JSON.stringify(file.source_list ?? [], null, 2) }] };
|
|
246
|
+
}
|
|
247
|
+
const selected = sourceList.find((s) => s.data_type === blockType);
|
|
248
|
+
if (!selected) {
|
|
249
|
+
const available = sourceList.map((s) => s.data_type).filter(Boolean).join(", ") || "(none)";
|
|
250
|
+
recordToolMetric("get_transcript", "success", Date.now() - start, requestId, { file_id });
|
|
251
|
+
logger.info({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start, block: blockType, found: false });
|
|
252
|
+
return {
|
|
253
|
+
content: [{ type: "text", text: `Block "${blockType}" not available for this recording. Available blocks: ${available}.` }]
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const content = await loadBlockContent(selected);
|
|
257
|
+
let segments = null;
|
|
258
|
+
if (content) {
|
|
259
|
+
try {
|
|
260
|
+
const parsed = JSON.parse(content);
|
|
261
|
+
if (Array.isArray(parsed)) segments = parsed;
|
|
262
|
+
} catch {
|
|
263
|
+
segments = null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!segments) {
|
|
267
|
+
recordToolMetric("get_transcript", "success", Date.now() - start, requestId, { file_id });
|
|
268
|
+
logger.info({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start, block: blockType, paginated: false });
|
|
269
|
+
const text = content || `Block "${blockType}" has no content for this recording yet.`;
|
|
270
|
+
return { content: [{ type: "text", text }] };
|
|
271
|
+
}
|
|
272
|
+
let offset = 0;
|
|
273
|
+
if (cursor !== void 0) {
|
|
274
|
+
const decoded = decodeTranscriptCursor(cursor);
|
|
275
|
+
if (decoded === null) {
|
|
276
|
+
recordToolMetric("get_transcript", "error", Date.now() - start, requestId, { file_id }, new Error("invalid_cursor"));
|
|
277
|
+
logger.warn({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start, error: "invalid_cursor" });
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: "text", text: "Invalid cursor. Omit `cursor` to start from the first utterance." }],
|
|
280
|
+
isError: true
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
offset = decoded;
|
|
284
|
+
}
|
|
285
|
+
const pageSize = limit ?? TRANSCRIPT_PAGE_SIZE;
|
|
286
|
+
const page = segments.slice(offset, offset + pageSize);
|
|
287
|
+
const nextOffset = offset + page.length;
|
|
288
|
+
const hasMore = nextOffset < segments.length;
|
|
289
|
+
const payload = {
|
|
290
|
+
file_id,
|
|
291
|
+
block: blockType,
|
|
292
|
+
total: segments.length,
|
|
293
|
+
offset,
|
|
294
|
+
limit: pageSize,
|
|
295
|
+
returned: page.length,
|
|
296
|
+
next_cursor: hasMore ? encodeTranscriptCursor(nextOffset) : null,
|
|
297
|
+
segments: page
|
|
298
|
+
};
|
|
216
299
|
recordToolMetric("get_transcript", "success", Date.now() - start, requestId, { file_id });
|
|
217
|
-
logger.info({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start });
|
|
218
|
-
return { content: [{ type: "text", text: JSON.stringify(
|
|
300
|
+
logger.info({ event: "tool_call_end", tool: "get_transcript", duration_ms: Date.now() - start, block: blockType, paginated: true, total: segments.length, offset, returned: page.length });
|
|
301
|
+
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
219
302
|
} catch (err) {
|
|
220
303
|
recordToolMetric("get_transcript", "error", Date.now() - start, requestId, { file_id }, err);
|
|
221
304
|
logger.error({ event: "tool_call_error", tool: "get_transcript", duration_ms: Date.now() - start, error: String(err) });
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3K3M2X74.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-YOMRLHNX.js";
|
|
12
12
|
import {
|
|
13
13
|
capture,
|
|
14
14
|
classifyError,
|
|
15
15
|
clearUser,
|
|
16
|
+
clientUserIdFromAccessToken,
|
|
16
17
|
extractIdentity,
|
|
17
18
|
initTelemetry,
|
|
18
19
|
oauthCallbackErrorType,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
setMcpHost,
|
|
21
22
|
setUser,
|
|
22
23
|
shutdown
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-OYI4PXYP.js";
|
|
24
25
|
import "./chunk-NPCCDRWQ.js";
|
|
25
26
|
import "./chunk-RUFCT6DQ.js";
|
|
26
27
|
|
|
@@ -30,7 +31,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
30
31
|
import open from "open";
|
|
31
32
|
var server = new McpServer({
|
|
32
33
|
name: "plaud",
|
|
33
|
-
version: "0.3.
|
|
34
|
+
version: "0.3.6"
|
|
34
35
|
});
|
|
35
36
|
var CALLBACK_PORT = 8199;
|
|
36
37
|
var LOGIN_TIMEOUT_MS = 12e4;
|
|
@@ -81,7 +82,11 @@ server.registerTool("login", {
|
|
|
81
82
|
try {
|
|
82
83
|
const user = await client.getCurrentUser();
|
|
83
84
|
if (user && typeof user.id === "string") {
|
|
84
|
-
await
|
|
85
|
+
const token = await client.auth.getAccessToken();
|
|
86
|
+
await setUser(user.id, {
|
|
87
|
+
...extractIdentity(user),
|
|
88
|
+
clientUserId: clientUserIdFromAccessToken(token)
|
|
89
|
+
});
|
|
85
90
|
}
|
|
86
91
|
} catch {
|
|
87
92
|
}
|
|
@@ -153,7 +158,7 @@ async function main() {
|
|
|
153
158
|
const sub = process.argv[2];
|
|
154
159
|
const sub2 = process.argv[3];
|
|
155
160
|
if (sub === "install") {
|
|
156
|
-
const { runInstall } = await import("./install-
|
|
161
|
+
const { runInstall } = await import("./install-3E63G6QX.js");
|
|
157
162
|
const args = process.argv.slice(3);
|
|
158
163
|
const yes = args.some((a) => a === "--yes" || a === "-y");
|
|
159
164
|
const noLogin = args.some((a) => a === "--no-login");
|
|
@@ -186,7 +191,7 @@ async function main() {
|
|
|
186
191
|
return;
|
|
187
192
|
}
|
|
188
193
|
if (sub === "http") {
|
|
189
|
-
const { startHttpServer } = await import("./server-
|
|
194
|
+
const { startHttpServer } = await import("./server-PJTOHPPJ.js");
|
|
190
195
|
const { startMetricsServer } = await import("./server-NKCNUA6P.js");
|
|
191
196
|
startMetricsServer();
|
|
192
197
|
startHttpServer();
|
|
@@ -212,7 +217,7 @@ Usage:
|
|
|
212
217
|
try {
|
|
213
218
|
await initTelemetry({
|
|
214
219
|
surface: "mcp",
|
|
215
|
-
appVersion: "0.3.
|
|
220
|
+
appVersion: "0.3.6",
|
|
216
221
|
transport: "stdio"
|
|
217
222
|
});
|
|
218
223
|
} catch {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3K3M2X74.js";
|
|
4
4
|
import {
|
|
5
5
|
commandPathIsStale,
|
|
6
6
|
copyToClipboard,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-242FRP4P.js";
|
|
13
13
|
import {
|
|
14
14
|
runOAuthCallback
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-OYI4PXYP.js";
|
|
16
16
|
import "./chunk-RUFCT6DQ.js";
|
|
17
17
|
|
|
18
18
|
// src/install.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeMcpHost,
|
|
3
3
|
registerTools
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YOMRLHNX.js";
|
|
5
5
|
import {
|
|
6
6
|
PlaudClient
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OYI4PXYP.js";
|
|
8
8
|
import {
|
|
9
9
|
logger
|
|
10
10
|
} from "./chunk-NPCCDRWQ.js";
|
|
@@ -610,31 +610,6 @@ var PlaudOAuthProvider = class extends ProxyOAuthServerProvider {
|
|
|
610
610
|
});
|
|
611
611
|
res.redirect(target.toString());
|
|
612
612
|
}
|
|
613
|
-
/**
|
|
614
|
-
* Resolve the REAL Plaud user_id (the `/users/current` `id` field) from a fresh
|
|
615
|
-
* access token — the same id the CLI/stdio telemetry and the Plaud app use, so
|
|
616
|
-
* warehouse auth.* events join with tool events, subscription and frontend data.
|
|
617
|
-
* The JWT `sub` is only an OAuth pseudo-id (`client_user_…`), so we resolve via
|
|
618
|
-
* the API. Best-effort: on any failure fall back to the JWT sub, so the event
|
|
619
|
-
* still carries a stable id and the auth flow is never blocked.
|
|
620
|
-
*/
|
|
621
|
-
async resolveRealUserId(accessToken) {
|
|
622
|
-
if (!accessToken) return void 0;
|
|
623
|
-
try {
|
|
624
|
-
const client = new PlaudClient({
|
|
625
|
-
clientId: this._plaudClientId,
|
|
626
|
-
clientSecret: "",
|
|
627
|
-
redirectUri: "",
|
|
628
|
-
apiBase: this._plaudApiBase,
|
|
629
|
-
staticToken: accessToken
|
|
630
|
-
});
|
|
631
|
-
const user = await client.getCurrentUser();
|
|
632
|
-
const id = typeof user?.id === "string" ? user.id : void 0;
|
|
633
|
-
return id ?? subFromJwt(accessToken);
|
|
634
|
-
} catch {
|
|
635
|
-
return subFromJwt(accessToken);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
613
|
// Override to use PKCE public-client flow — no Basic auth, client_id sent in body.
|
|
639
614
|
async exchangeAuthorizationCode(client, authorizationCode, codeVerifier, _redirectUri, resource) {
|
|
640
615
|
const failedAuth = (failureKind) => {
|
|
@@ -710,7 +685,7 @@ var PlaudOAuthProvider = class extends ProxyOAuthServerProvider {
|
|
|
710
685
|
throw new McpServerError("Token endpoint returned non-JSON response");
|
|
711
686
|
}
|
|
712
687
|
logger.info({ event: "oauth_token_exchange_ok", has_refresh_token: !!data.refresh_token });
|
|
713
|
-
const authorizedUserId =
|
|
688
|
+
const authorizedUserId = subFromJwt(data.access_token);
|
|
714
689
|
this._tracker?.track({
|
|
715
690
|
name: "auth.oauth_callback_success",
|
|
716
691
|
actorType: "user",
|
|
@@ -782,12 +757,11 @@ var PlaudOAuthProvider = class extends ProxyOAuthServerProvider {
|
|
|
782
757
|
throw new McpServerError("Refresh endpoint returned non-JSON response");
|
|
783
758
|
}
|
|
784
759
|
oauthTokenRefresh.inc({ result: "success" });
|
|
785
|
-
const refreshedUserId = await this.resolveRealUserId(data.access_token);
|
|
786
760
|
this._tracker?.track({
|
|
787
761
|
name: "auth.token_refresh_success",
|
|
788
762
|
actorType: "user",
|
|
789
|
-
userId:
|
|
790
|
-
distinctId:
|
|
763
|
+
userId: subFromJwt(data.access_token),
|
|
764
|
+
distinctId: subFromJwt(data.access_token) ?? client.client_id
|
|
791
765
|
});
|
|
792
766
|
logger.info({
|
|
793
767
|
event: "oauth_token_refresh_ok",
|
|
@@ -1231,8 +1205,8 @@ function startHttpServer() {
|
|
|
1231
1205
|
common: {
|
|
1232
1206
|
serviceName: process.env.PLAUD_WAREHOUSE_SERVICE_NAME ?? "plaudmcp",
|
|
1233
1207
|
// matches §1.3 key label `plaudmcp:TRACKING_KEY_PLAUDMCP`; confirm exact string with David
|
|
1234
|
-
serviceVersion: "0.3.
|
|
1235
|
-
buildId: "
|
|
1208
|
+
serviceVersion: "0.3.6",
|
|
1209
|
+
buildId: "3c993f6",
|
|
1236
1210
|
// mcp tsup TODO: inject git short SHA (like CLI)
|
|
1237
1211
|
region: process.env.PLAUD_REGION ?? "US",
|
|
1238
1212
|
env: process.env.PLAUD_ENV ?? process.env.NODE_ENV ?? "development"
|
|
@@ -1473,7 +1447,7 @@ function startHttpServer() {
|
|
|
1473
1447
|
apiBase,
|
|
1474
1448
|
staticToken: token
|
|
1475
1449
|
});
|
|
1476
|
-
const mcpServer = new McpServer({ name: "plaud", version: "0.3.
|
|
1450
|
+
const mcpServer = new McpServer({ name: "plaud", version: "0.3.6" });
|
|
1477
1451
|
registerTools(mcpServer, client, warehouseToolHooks);
|
|
1478
1452
|
const transport = new StreamableHTTPServerTransport({
|
|
1479
1453
|
sessionIdGenerator: void 0,
|
|
@@ -1481,7 +1455,8 @@ function startHttpServer() {
|
|
|
1481
1455
|
enableDnsRebindingProtection: true,
|
|
1482
1456
|
allowedOrigins: ALLOWED_ORIGINS
|
|
1483
1457
|
});
|
|
1484
|
-
const
|
|
1458
|
+
const fallbackClientId = req.auth.clientId && req.auth.clientId !== "unknown" ? req.auth.clientId : void 0;
|
|
1459
|
+
const ctxUserId = subFromJwt(token) ?? fallbackClientId;
|
|
1485
1460
|
const telemetryCtx = {
|
|
1486
1461
|
userId: ctxUserId,
|
|
1487
1462
|
requestId: reqId,
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -22,7 +22,7 @@ metadata:
|
|
|
22
22
|
| User wants | Tool | Notes |
|
|
23
23
|
|---|---|---|
|
|
24
24
|
| AI summary, TL;DR, action items | `get_note` | Returns Markdown; usually enough — try this before `get_transcript` |
|
|
25
|
-
| Verbatim quotes, full dialogue | `get_transcript` | Timestamped;
|
|
25
|
+
| Verbatim quotes, full dialogue | `get_transcript` | Timestamped; paginated — follow `next_cursor` for long recordings |
|
|
26
26
|
| Audio download link | `get_file` then use `presigned_url` | Link expires in 24h |
|
|
27
27
|
| Full metadata + availability flags | `get_file` | Check `source_list` / `note_list` populated before claiming content exists |
|
|
28
28
|
|
|
@@ -47,5 +47,5 @@ Common schemas:
|
|
|
47
47
|
|
|
48
48
|
## Anti-patterns
|
|
49
49
|
|
|
50
|
-
- Do not call `get_transcript` speculatively — it's the largest payload.
|
|
50
|
+
- Do not call `get_transcript` speculatively — it's the largest payload (paged; a long recording needs several `next_cursor` calls).
|
|
51
51
|
- Do not paraphrase the AI summary unless the user asked; quote it verbatim.
|