@plaud-ai/mcp 0.1.32 → 0.2.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.
@@ -1,58 +1,20 @@
1
1
  import {
2
- SKILLS_COMBINED
3
- } from "./chunk-5JTZ7NKP.js";
2
+ copyToClipboard,
3
+ getMcpEntry,
4
+ removeSkillsFromClaudeCode,
5
+ writeSkillsToClaudeCode
6
+ } from "./chunk-UPEENHCG.js";
7
+ import {
8
+ skillsCombined
9
+ } from "./chunk-4QBEOJPX.js";
4
10
 
5
11
  // src/setup.ts
6
12
  import { readFile, writeFile, mkdir, rm } from "fs/promises";
7
13
  import { join, dirname } from "path";
8
- import { homedir, platform } from "os";
9
- import { fileURLToPath } from "url";
10
- import { spawnSync } from "child_process";
11
- var SKILLS_MARKER_START = "<!-- plaud-skills:start -->";
12
- var SKILLS_MARKER_END = "<!-- plaud-skills:end -->";
13
- var SKILLS_BLOCK = `${SKILLS_MARKER_START}
14
- ${SKILLS_COMBINED}
15
- ${SKILLS_MARKER_END}`;
16
- async function removeSkillsFromClaudeCode() {
17
- const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
18
- let existing = "";
19
- try {
20
- existing = await readFile(claudeMdPath, "utf-8");
21
- } catch {
22
- return;
23
- }
24
- if (!existing.includes(SKILLS_MARKER_START)) {
25
- return;
26
- }
27
- const updated = existing.replace(new RegExp(`\\n?${SKILLS_MARKER_START}[\\s\\S]*?${SKILLS_MARKER_END}\\n?`), "").trimEnd();
28
- await writeFile(claudeMdPath, updated ? updated + "\n" : "", "utf-8");
29
- }
30
- async function writeSkillsToClaudeCode() {
31
- const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
32
- let existing = "";
33
- try {
34
- existing = await readFile(claudeMdPath, "utf-8");
35
- } catch {
36
- }
37
- if (existing.includes(SKILLS_MARKER_START)) {
38
- const updated = existing.replace(
39
- new RegExp(`${SKILLS_MARKER_START}[\\s\\S]*?${SKILLS_MARKER_END}`),
40
- SKILLS_BLOCK
41
- );
42
- await mkdir(dirname(claudeMdPath), { recursive: true });
43
- await writeFile(claudeMdPath, updated, "utf-8");
44
- } else {
45
- await mkdir(dirname(claudeMdPath), { recursive: true });
46
- await writeFile(claudeMdPath, existing + (existing.endsWith("\n") ? "" : "\n") + SKILLS_BLOCK + "\n", "utf-8");
47
- }
48
- }
49
- function copyToClipboard(content) {
50
- const cmd = platform() === "win32" ? "clip" : "pbcopy";
51
- const result = spawnSync(cmd, [], { input: content });
52
- return result.status === 0;
53
- }
54
- function printSkillsPasteGuide() {
55
- const copied = copyToClipboard(SKILLS_COMBINED);
14
+ import { homedir } from "os";
15
+ async function printSkillsPasteGuide() {
16
+ const combined = await skillsCombined();
17
+ const copied = copyToClipboard(combined);
56
18
  console.log("");
57
19
  if (copied) {
58
20
  console.log("Plaud Skills copied to clipboard.");
@@ -63,13 +25,6 @@ function printSkillsPasteGuide() {
63
25
  console.log(" Claude Desktop: Settings \u2192 Profile \u2192 Custom Instructions");
64
26
  console.log(" Codex Desktop: refer to client documentation");
65
27
  }
66
- function getMcpEntry() {
67
- const __dirname = dirname(fileURLToPath(import.meta.url));
68
- return {
69
- command: process.execPath,
70
- args: [join(__dirname, "index.js")]
71
- };
72
- }
73
28
  function getConfigPath() {
74
29
  if (process.platform === "darwin") {
75
30
  return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
@@ -105,10 +60,11 @@ async function runCleanPlugin() {
105
60
  async function runSetupCodex() {
106
61
  const configPath = join(homedir(), ".codex", "config.toml");
107
62
  const { command, args } = getMcpEntry();
63
+ const argsStr = args.map((a) => `"${a}"`).join(", ");
108
64
  const entry = `
109
65
  [mcp_servers.plaud]
110
66
  command = "${command}"
111
- args = ["${args[0]}"]
67
+ args = [${argsStr}]
112
68
  `;
113
69
  let content = "";
114
70
  try {
@@ -126,7 +82,7 @@ args = ["${args[0]}"]
126
82
  console.log("Please restart Codex Desktop to complete the setup.");
127
83
  await writeSkillsToClaudeCode();
128
84
  console.log("Plaud Skills have been added to ~/.claude/CLAUDE.md for Claude Code.");
129
- printSkillsPasteGuide();
85
+ await printSkillsPasteGuide();
130
86
  process.exit(0);
131
87
  }
132
88
  async function runUnsetupCodex() {
@@ -142,7 +98,7 @@ async function runUnsetupCodex() {
142
98
  console.log("Plaud is not configured in Codex Desktop. Nothing to remove.");
143
99
  return;
144
100
  }
145
- const cleaned = content.replace(/\n*\[mcp_servers\.plaud\]\n(?:(?!\[)[^\n]*\n)*/g, "");
101
+ const cleaned = content.replace(/\[mcp_servers\.plaud\]\n(?:(?!\[)[^\n]*\n)*/g, "").replace(/\n{3,}/g, "\n\n");
146
102
  await writeFile(configPath, cleaned, "utf-8");
147
103
  await removeSkillsFromClaudeCode();
148
104
  console.log("Plaud has been removed from Codex Desktop.");
@@ -201,7 +157,7 @@ async function runSetup() {
201
157
  console.log("Please restart Claude Desktop to complete the setup.");
202
158
  await writeSkillsToClaudeCode();
203
159
  console.log("Plaud Skills have been added to ~/.claude/CLAUDE.md for Claude Code.");
204
- printSkillsPasteGuide();
160
+ await printSkillsPasteGuide();
205
161
  process.exit(0);
206
162
  }
207
163
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaud-ai/mcp",
3
- "version": "0.1.32",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -12,21 +12,28 @@
12
12
  ".mcp.json",
13
13
  "skills"
14
14
  ],
15
- "scripts": {
16
- "version:show": "node -p \"require('./package.json').version\"",
17
- "build": "tsup",
18
- "dev": "tsup --watch",
19
- "clean": "rm -rf dist",
20
- "prepublishOnly": "node -e \"const fs=require('fs'),v=require('./package.json').version,p=JSON.parse(fs.readFileSync('./plugin.json','utf8'));p.version=v;fs.writeFileSync('./plugin.json',JSON.stringify(p,null,2)+'\\n');\""
15
+ "publishConfig": {
16
+ "registry": "https://registry.npmjs.org/",
17
+ "access": "public"
21
18
  },
22
19
  "dependencies": {
23
20
  "@modelcontextprotocol/sdk": "^1.12.0",
21
+ "express": "^5.2.1",
24
22
  "open": "^10.2.0",
23
+ "pino": "^10.3.1",
25
24
  "zod": "^4.3.6"
26
25
  },
27
26
  "devDependencies": {
28
- "@plaud-ai/shared": "workspace:*",
27
+ "@types/express": "^5.0.6",
29
28
  "@types/node": "^25.5.0",
30
- "typescript": "^5.7.0"
29
+ "typescript": "^5.7.0",
30
+ "@plaud-ai/shared": "0.1.0"
31
+ },
32
+ "scripts": {
33
+ "version:show": "node -p \"require('./package.json').version\"",
34
+ "prebuild": "node ../../scripts/sync-skills.mjs",
35
+ "build": "tsup",
36
+ "dev": "tsup --watch",
37
+ "clean": "rm -rf dist skills"
31
38
  }
32
- }
39
+ }
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plaud",
3
- "version": "0.1.32",
3
+ "version": "0.2.0",
4
4
  "description": "Access your Plaud recordings in Claude",
5
5
  "author": {
6
6
  "name": "Plaud AI"
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: plaud-browse
3
+ version: 1.0.0
4
+ description: "Browse, list, or paginate through Plaud recordings. Use when the user says 'what recordings do I have', 'show my recent recordings', 'list my recordings', or asks to see the most recent uploads."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-browse
11
+
12
+ **Read [`plaud-shared`](../plaud-shared/SKILL.md) first** for auth and output conventions.
13
+
14
+ ## When to use
15
+
16
+ - User wants to see what is in their library without a specific target in mind.
17
+ - User explicitly asks for a page, or says "next page", "more results".
18
+ - User asks "what's the most recent recording" — fetch page 1 and return the top item.
19
+
20
+ ## Steps
21
+
22
+ 1. Call `list_files` with `page=1` and `page_size=20` (default). No `query` / `date_from` / `date_to` unless the user said something that matches `plaud-find`.
23
+ 2. Present results in a compact table: **ID**, **NAME**, **DATE** (`YYYY-MM-DD`), **DURATION** (`5m23s` style).
24
+ 3. If the page looks like the whole library (fewer than `page_size` returned), tell the user there is no next page.
25
+ 4. If the user asks for more, increment `page` by 1 and call again.
26
+
27
+ ## Anti-patterns
28
+
29
+ - Do **not** fetch every page eagerly; pagination is lazy.
30
+ - Do **not** call `get_note` or `get_transcript` during a browse — that belongs to `plaud-read` and burns tokens.
31
+ - Do **not** expose raw timestamps or durations in milliseconds.
32
+
33
+ ## Example
34
+
35
+ User: "show me my recordings"
36
+
37
+ Agent:
38
+ - `list_files(page=1, page_size=20)`
39
+ - Render table, mention "page 1, say 'next page' for more"
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: plaud-digest
3
+ version: 1.0.0
4
+ description: "Summarize multiple Plaud recordings into a digest. Use when the user says 'weekly report', 'digest of this month', 'what meetings did I have this week', 'recap of last quarter', or asks to roll up multiple recordings into one overview."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-digest
11
+
12
+ **Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
13
+
14
+ ## When to use
15
+
16
+ - User asks for a roll-up across multiple recordings.
17
+ - Time window is explicit ("this week") or implicit ("recap of recent meetings").
18
+ - Scope is "what happened", not "find one specific meeting" (that's `plaud-find`).
19
+
20
+ ## Steps
21
+
22
+ 1. **Resolve the window.** Use the date interpretation table in `plaud-find` for relative phrases.
23
+ 2. **List the corpus.** `list_files` with `date_from` / `date_to`. Cap at 50 recordings — if the window returns more, ask the user to narrow it.
24
+ 3. **Fetch notes in batch.** For each recording, call `get_note`. Do **not** call `get_transcript` unless a specific recording merits a deeper pull.
25
+ 4. **Synthesize.** Produce a structured digest:
26
+ - **Headline** — one-line theme of the window.
27
+ - **By recording** — one bullet per recording: `• name (date, duration) — one-sentence takeaway`.
28
+ - **Recurring themes** — topics that appeared in ≥ 2 recordings.
29
+ - **Open action items** — aggregated across recordings, deduplicated.
30
+ 5. **Cite sources.** Every non-trivial claim must reference the recording it came from, using the file name (not the raw ID unless the user asked).
31
+
32
+ ## Budget
33
+
34
+ - Hard cap: 50 `get_note` calls per digest. If the window has more recordings, compress or ask user to narrow.
35
+ - Skip recordings where `note_list` is empty — mention them at the end under "unsummarized".
36
+
37
+ ## Anti-patterns
38
+
39
+ - Do not load transcripts just to pad the digest.
40
+ - Do not synthesize across windows the user didn't ask for ("while we're at it, here's last month too").
41
+ - Do not invent action items that aren't in the notes — only aggregate what's there.
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: plaud-export
3
+ version: 1.0.0
4
+ description: "Push Plaud content or a generated artifact to Notion, Slack, HubSpot, Linear, Gmail, or a custom webhook. Use when the user says 'save to Notion', 'post to Slack', 'send to webhook', 'file this in HubSpot', or asks to deliver recording content to an external system."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-export
11
+
12
+ **Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
13
+
14
+ ## When to use
15
+
16
+ - User has a ready artifact (from `plaud-followup`) or recording content and wants to **deliver** it somewhere.
17
+ - Destination is an external system (not the chat).
18
+
19
+ ## Out of scope
20
+
21
+ - Generating the artifact — that's `plaud-followup`.
22
+ - Reading recording content — that's `plaud-read`.
23
+
24
+ This skill is the final leg: take content that already exists and send it.
25
+
26
+ ## Steps
27
+
28
+ 1. **Confirm the payload.**
29
+ - Recording summary (raw `get_note` content)?
30
+ - Generated artifact (email, SOAP, brief — already drafted)?
31
+ - Raw transcript excerpt?
32
+ 2. **Confirm the destination + identifiers.** Ask for the exact target. Plaud does not store destination credentials.
33
+ 3. **Deliver using the MCP tool or integration available in the user's environment.** Plaud MCP itself does not expose a `push` tool — this skill assumes another MCP (Notion MCP, Slack MCP, a webhook tool, Gmail send) is available in the session.
34
+ 4. **Report the delivery URL** (Notion page URL, Slack message permalink, webhook HTTP status) back to the user.
35
+
36
+ ## Destination identifier cheat-sheet
37
+
38
+ | Destination | Required identifier | Typical ask |
39
+ |---|---|---|
40
+ | Notion | page ID or database ID | "Which Notion page should this go under?" |
41
+ | Slack | channel name or ID | "Which channel? (e.g., `#sales` or `C0123`)" |
42
+ | HubSpot / Salesforce | CRM object ID (deal / contact / company) | "Which deal/contact should this attach to?" |
43
+ | Linear | team or project ID | "Which Linear team or project?" |
44
+ | Gmail | recipient email(s) | "Who should this email go to?" |
45
+ | Webhook | full URL | "Paste the webhook URL" |
46
+
47
+ ## Anti-patterns
48
+
49
+ - Never persist destination credentials in the conversation or in files. Assume the MCP host provides them.
50
+ - Never send to a default destination ("I'll put it in `#general`") — always confirm.
51
+ - Never alter the artifact content during delivery. If Slack needs mrkdwn, convert format without changing meaning.
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: plaud-find
3
+ version: 1.0.0
4
+ description: "Find a specific Plaud recording by name keyword, date range, or topic. Use when the user says 'find the Weekly Sync', 'the meeting from Monday', 'the call about Q2', 'recordings last week', or describes what they're looking for rather than listing."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-find
11
+
12
+ **Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
13
+
14
+ ## Background
15
+
16
+ Plaud's `list_files` API does **not** accept `query` / `date_from` / `date_to` server-side — unknown params are silently ignored. Filtering happens client-side.
17
+
18
+ The MCP `list_files` tool accepts the same three optional params and performs the filter for you: pass the user's keyword and/or date window and let the tool paginate up to 5 pages.
19
+
20
+ ## Steps
21
+
22
+ 1. **Elicit criteria if vague.** If the user just said "find a recording", ask for at least one of:
23
+ - a name keyword (even a partial match),
24
+ - a rough date or date range,
25
+ - a duration range (less useful, ask only if name and date fail).
26
+ 2. **Call `list_files`** with the filter params you gathered:
27
+ - `query=<keyword>` — case-insensitive substring match on `name`.
28
+ - `date_from=YYYY-MM-DD`, `date_to=YYYY-MM-DD` — inclusive window on `created_at`.
29
+ - Omit any that the user did not specify.
30
+ 3. **If zero matches**, ask the user to broaden one axis (shorter keyword, wider date range).
31
+ 4. **If many matches** (> 10), return the top 10 sorted by `created_at` desc and mention the total.
32
+ 5. **Never auto-load transcripts**. Present the match list and wait for the user to pick one — that triggers `plaud-read`.
33
+
34
+ ## Date interpretation rules
35
+
36
+ | User phrase | Filter |
37
+ |---|---|
38
+ | "today" | `date_from` = today, `date_to` = today |
39
+ | "yesterday" | both = yesterday |
40
+ | "this week" | Monday of this week → today |
41
+ | "last week" | Monday of last week → Sunday of last week |
42
+ | "this month" | 1st of this month → today |
43
+ | "last month" | 1st → last day of previous month |
44
+ | "from Monday" | `date_from` = the most recent Monday, no `date_to` |
45
+
46
+ Resolve relative dates against the **current date** (from conversation context), not the model's training cutoff.
47
+
48
+ ## Example
49
+
50
+ User: "find the customer onboarding call from last week"
51
+
52
+ Agent:
53
+ - `list_files(query="onboarding", date_from="2026-04-13", date_to="2026-04-19")`
54
+ - Return matches: "Found 2 recordings. `abc123` Customer Onboarding — Acme (2026-04-15, 42m), `def456` Onboarding Q&A (2026-04-17, 18m). Which one?"
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: plaud-followup
3
+ version: 1.0.0
4
+ description: "Turn a Plaud recording into a follow-up email, thank-you note, action-item list, SOAP note, or meeting brief. Use when the user says 'draft follow-up', 'what were the action items', 'send thank-you email', 'turn this into a SOAP note', 'write the recap', or names an artifact to generate from a recording."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-followup
11
+
12
+ **Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
13
+
14
+ ## When to use
15
+
16
+ - User wants a **generated document** grounded in one recording.
17
+ - Target format is explicit (email, SOAP note, brief, action-item list) or implicit ("write the follow-up").
18
+ - If the user wants to *send* the output to Notion / Slack / a webhook, chain into `plaud-export` after drafting.
19
+
20
+ ## Steps
21
+
22
+ 1. **Identify the recording.** If the user didn't name one, hand off to `plaud-find` or `plaud-browse`.
23
+ 2. **Fetch source content.**
24
+ - `get_note` first — usually enough for summaries and action items.
25
+ - `get_transcript` only if the artifact needs verbatim quotes (e.g., legal memo) or speaker attribution (e.g., SOAP).
26
+ 3. **Generate the artifact** in the requested format. Ground every claim in the source; do not invent attendees, dates, decisions, or numbers.
27
+ 4. **Present to the user** in the chat, then ask if they want to refine or export.
28
+
29
+ ## Artifact templates
30
+
31
+ ### Follow-up email
32
+ - To: attendees (from notes if listed).
33
+ - Subject: "Follow-up — {recording name}, {date}".
34
+ - Opening line: thanks + one-line meeting summary.
35
+ - Body: 3–5 bullets of key points.
36
+ - Action items: numbered list with owner and due date if mentioned.
37
+ - Closing: "Let me know if I missed anything."
38
+
39
+ ### Thank-you email
40
+ - Short. One paragraph. One concrete thing you learned or appreciated from the call.
41
+
42
+ ### Action-item list
43
+ - Plain markdown: `- [ ] {owner}: {item} (due {date})`.
44
+ - Mark owner as `?` if unclear from notes — do not guess.
45
+
46
+ ### SOAP note (clinical)
47
+ - **Subjective** — patient's words (from transcript).
48
+ - **Objective** — observations (from transcript, not inferred).
49
+ - **Assessment** — summary's diagnosis if present.
50
+ - **Plan** — action items and next appointment.
51
+
52
+ ### Meeting brief
53
+ - Attendees, date, duration, decisions, risks, next steps.
54
+
55
+ ## Anti-patterns
56
+
57
+ - Never invent email recipients. If attendees weren't captured, ask the user.
58
+ - Never invent due dates. Mark as `due: TBD` if not stated.
59
+ - Do not send the email — this skill drafts. Handoff to `plaud-export` for delivery.
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: plaud-read
3
+ version: 1.0.0
4
+ description: "Read the transcript, AI summary, notes, or download audio for a specific Plaud recording. Use when the user says 'show the transcript', 'summarize this', 'what was said', 'get audio', 'the notes from', or names a specific recording to dig into. Also covers extracting structured fields from a recording."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-read
11
+
12
+ **Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
13
+
14
+ ## When to use
15
+
16
+ - User names a specific recording (by name or ID) and wants to read its content.
17
+ - User asks for "transcript", "summary", "action items", "audio", "who said what", or a structured extraction ("action items, decisions, attendees").
18
+ - If the user did **not** specify a recording, hand off to `plaud-find` (by topic) or `plaud-browse` (by recency) first.
19
+
20
+ ## Tool selection matrix
21
+
22
+ | User wants | Tool | Notes |
23
+ |---|---|---|
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; larger |
26
+ | Audio download link | `get_file` then use `presigned_url` | Link expires in 24h |
27
+ | Full metadata + availability flags | `get_file` | Check `source_list` / `note_list` populated before claiming content exists |
28
+
29
+ ## Structured extraction workflow
30
+
31
+ If the user provides a schema (e.g., `{"action_items": [], "decisions": [], "attendees": []}`):
32
+
33
+ 1. Call `get_note` first — the AI summary usually already contains these fields.
34
+ 2. Only call `get_transcript` if the summary is missing a required field.
35
+ 3. Return JSON matching the user's schema. Mark any missing field with `null` and note why.
36
+
37
+ Common schemas:
38
+ - Sales: `{ "pain_points": [], "follow_ups": [], "deal_stage": "" }`
39
+ - Clinical: `{ "diagnoses": [], "medications": [], "next_appointment": "" }`
40
+ - Project: `{ "action_items": [], "decisions": [], "attendees": [] }`
41
+
42
+ ## Output
43
+
44
+ - Transcripts: preserve `[MM:SS - MM:SS] Speaker: content`. Do not reformat timestamps.
45
+ - Summaries: render Markdown directly in the reply.
46
+ - Audio: print the URL and mention "expires in 24h".
47
+
48
+ ## Anti-patterns
49
+
50
+ - Do not call `get_transcript` speculatively — it's the largest payload.
51
+ - Do not paraphrase the AI summary unless the user asked; quote it verbatim.
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: plaud-shared
3
+ version: 1.0.0
4
+ description: "First read before any Plaud operation. Auth flow, error handling, output conventions, token refresh. Use when the user mentions Plaud for the first time in a session, or when any other Plaud skill is invoked."
5
+ metadata:
6
+ requires:
7
+ bins: []
8
+ ---
9
+
10
+ # plaud-shared
11
+
12
+ **CRITICAL — read this before calling any Plaud tool.** Applies to every other `plaud-*` skill.
13
+
14
+ ## Authentication
15
+
16
+ - Plaud uses OAuth. Tokens are stored in `~/.plaud/tokens.json` and refreshed automatically.
17
+ - If any tool returns an auth error (message includes `Not authenticated` or `401`), call the `login` tool and wait for the browser callback. Do **not** retry the original tool until login returns success.
18
+ - Never ask the user to paste tokens. The `login` tool handles the whole flow.
19
+
20
+ ## Tool inventory
21
+
22
+ | Tool | Purpose |
23
+ |---|---|
24
+ | `login` | Open browser for OAuth; blocks until callback or 2-min timeout |
25
+ | `logout` | Revoke and clear tokens |
26
+ | `get_current_user` | Verify who is signed in |
27
+ | `list_files` | Browse, paginate, filter recordings (supports `query`, `date_from`, `date_to`) |
28
+ | `get_file` | Full record incl. `presigned_url`, `source_list`, `note_list` |
29
+ | `get_note` | AI-generated summary and action items |
30
+ | `get_transcript` | Timestamped transcript with speaker labels |
31
+
32
+ ## Error semantics
33
+
34
+ | Pattern in error message | Meaning | What to do |
35
+ |---|---|---|
36
+ | `401` / `Not authenticated` | Token missing or expired | Call `login`, then retry |
37
+ | `404` | File ID does not exist | Tell the user the ID is wrong; do not retry |
38
+ | `500` | Backend error (often an invalid ID too — see §7.1 of proposal) | Retry once; if still 500, treat as NOT_FOUND |
39
+ | `fetch failed` / `ECONNREFUSED` | Network problem | Abort; tell user to check connection |
40
+
41
+ ## Output conventions
42
+
43
+ When presenting recordings to the user:
44
+
45
+ - Always show name, date, duration, and file ID — users need the ID to ask follow-up questions.
46
+ - Format durations human-readable: `23s`, `5m23s`, `1h05m`. Raw milliseconds are for logs only.
47
+ - Format dates as `YYYY-MM-DD` in local time.
48
+ - Transcripts: preserve `[MM:SS - MM:SS] Speaker: content` format.
49
+ - Notes: render Markdown directly.
50
+
51
+ ## Data model quick reference
52
+
53
+ - `duration` field is **milliseconds**.
54
+ - `source_list` — array; each item with `data_type === "transaction"` holds the transcript segments (JSON-encoded string in `data_content`).
55
+ - `note_list` — array; each item with `data_type === "auto_sum_note"` holds the AI summary (Markdown in `data_content`).
56
+ - `presigned_url` — expires in 24 hours; re-fetch with `get_file` if stale.
57
+
58
+ ## When to load which sibling skill
59
+
60
+ | User intent | Skill to follow |
61
+ |---|---|
62
+ | "List / show / browse my recordings" | `plaud-browse` |
63
+ | "Find the meeting about X" / "from Monday" | `plaud-find` |
64
+ | "Show transcript / summary / audio" | `plaud-read` |
65
+ | "Weekly digest" / "what did I have this month" | `plaud-digest` |
66
+ | "Draft follow-up" / "action items" / "thank-you email" | `plaud-followup` |
67
+ | "Save to Notion / Slack / webhook" | `plaud-export` |