@plaud-ai/mcp 0.3.5 → 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-SEOLTKA5.js → chunk-3K3M2X74.js} +1 -1
- package/dist/{chunk-FDWRBOUI.js → chunk-OYI4PXYP.js} +18 -0
- package/dist/{chunk-YI4KJEAG.js → chunk-YOMRLHNX.js} +91 -8
- package/dist/index.js +7 -7
- package/dist/{install-OHOKLBHH.js → install-3E63G6QX.js} +2 -2
- package/dist/{server-XUKJZCFN.js → server-PJTOHPPJ.js} +5 -5
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/plaud-read/SKILL.md +2 -2
|
@@ -429,6 +429,23 @@ function runOAuthCallback(opts) {
|
|
|
429
429
|
});
|
|
430
430
|
}
|
|
431
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
|
+
|
|
432
449
|
// ../telemetry/dist/client.js
|
|
433
450
|
import { PostHog } from "posthog-node";
|
|
434
451
|
|
|
@@ -841,6 +858,7 @@ export {
|
|
|
841
858
|
clientUserIdFromAccessToken,
|
|
842
859
|
PlaudClient,
|
|
843
860
|
runOAuthCallback,
|
|
861
|
+
loadBlockContent,
|
|
844
862
|
shutdown,
|
|
845
863
|
initTelemetry,
|
|
846
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,14 +1,14 @@
|
|
|
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,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
setMcpHost,
|
|
22
22
|
setUser,
|
|
23
23
|
shutdown
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-OYI4PXYP.js";
|
|
25
25
|
import "./chunk-NPCCDRWQ.js";
|
|
26
26
|
import "./chunk-RUFCT6DQ.js";
|
|
27
27
|
|
|
@@ -31,7 +31,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
31
31
|
import open from "open";
|
|
32
32
|
var server = new McpServer({
|
|
33
33
|
name: "plaud",
|
|
34
|
-
version: "0.3.
|
|
34
|
+
version: "0.3.6"
|
|
35
35
|
});
|
|
36
36
|
var CALLBACK_PORT = 8199;
|
|
37
37
|
var LOGIN_TIMEOUT_MS = 12e4;
|
|
@@ -158,7 +158,7 @@ async function main() {
|
|
|
158
158
|
const sub = process.argv[2];
|
|
159
159
|
const sub2 = process.argv[3];
|
|
160
160
|
if (sub === "install") {
|
|
161
|
-
const { runInstall } = await import("./install-
|
|
161
|
+
const { runInstall } = await import("./install-3E63G6QX.js");
|
|
162
162
|
const args = process.argv.slice(3);
|
|
163
163
|
const yes = args.some((a) => a === "--yes" || a === "-y");
|
|
164
164
|
const noLogin = args.some((a) => a === "--no-login");
|
|
@@ -191,7 +191,7 @@ async function main() {
|
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
193
|
if (sub === "http") {
|
|
194
|
-
const { startHttpServer } = await import("./server-
|
|
194
|
+
const { startHttpServer } = await import("./server-PJTOHPPJ.js");
|
|
195
195
|
const { startMetricsServer } = await import("./server-NKCNUA6P.js");
|
|
196
196
|
startMetricsServer();
|
|
197
197
|
startHttpServer();
|
|
@@ -217,7 +217,7 @@ Usage:
|
|
|
217
217
|
try {
|
|
218
218
|
await initTelemetry({
|
|
219
219
|
surface: "mcp",
|
|
220
|
-
appVersion: "0.3.
|
|
220
|
+
appVersion: "0.3.6",
|
|
221
221
|
transport: "stdio"
|
|
222
222
|
});
|
|
223
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";
|
|
@@ -1205,8 +1205,8 @@ function startHttpServer() {
|
|
|
1205
1205
|
common: {
|
|
1206
1206
|
serviceName: process.env.PLAUD_WAREHOUSE_SERVICE_NAME ?? "plaudmcp",
|
|
1207
1207
|
// matches §1.3 key label `plaudmcp:TRACKING_KEY_PLAUDMCP`; confirm exact string with David
|
|
1208
|
-
serviceVersion: "0.3.
|
|
1209
|
-
buildId: "
|
|
1208
|
+
serviceVersion: "0.3.6",
|
|
1209
|
+
buildId: "3c993f6",
|
|
1210
1210
|
// mcp tsup TODO: inject git short SHA (like CLI)
|
|
1211
1211
|
region: process.env.PLAUD_REGION ?? "US",
|
|
1212
1212
|
env: process.env.PLAUD_ENV ?? process.env.NODE_ENV ?? "development"
|
|
@@ -1447,7 +1447,7 @@ function startHttpServer() {
|
|
|
1447
1447
|
apiBase,
|
|
1448
1448
|
staticToken: token
|
|
1449
1449
|
});
|
|
1450
|
-
const mcpServer = new McpServer({ name: "plaud", version: "0.3.
|
|
1450
|
+
const mcpServer = new McpServer({ name: "plaud", version: "0.3.6" });
|
|
1451
1451
|
registerTools(mcpServer, client, warehouseToolHooks);
|
|
1452
1452
|
const transport = new StreamableHTTPServerTransport({
|
|
1453
1453
|
sessionIdGenerator: void 0,
|
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.
|