@writepanda/mcp 1.210.0 → 1.211.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.
Files changed (2) hide show
  1. package/bin/server.mjs +61 -16
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -212,6 +212,7 @@ async function callPandastudio(command, args = {}) {
212
212
  } catch {
213
213
  body = { raw: text };
214
214
  }
215
+ if (res.status === 409 && body?.details?.code === "revision_conflict") return body;
215
216
  if (res.status >= 400) {
216
217
  throw new Error(`HTTP ${res.status}: ${body?.error ?? text.slice(0, 200)}`);
217
218
  }
@@ -1479,7 +1480,8 @@ const TOOLS = [
1479
1480
  },
1480
1481
  {
1481
1482
  name: "podcast_open_host",
1482
- description: "Open or focus the live podcast host window. Call podcast_status for the session code and guest link once it loads.",
1483
+ description:
1484
+ "Open or focus the live podcast host window. Call podcast_status for the session code and guest link once it loads.",
1483
1485
  inputSchema: {
1484
1486
  type: "object",
1485
1487
  properties: {
@@ -1490,10 +1492,22 @@ const TOOLS = [
1490
1492
  command: "podcast.open-host",
1491
1493
  },
1492
1494
  ...[
1493
- ["status", "Read the live podcast session code, guest link, participants, recording, and sharing state."],
1494
- ["start-recording", "Start recording the host and signal connected guests to record. Returns once recording has really started; fails with the reason otherwise."],
1495
- ["stop-recording", "Stop recording and finalize podcast media. Check podcast_status for finalization and guest uploads."],
1496
- ["start-screen-share", "Open the desktop picker to share and record the host screen during a podcast. Waits for the host to choose; fails if the picker is cancelled or capture fails."],
1495
+ [
1496
+ "status",
1497
+ "Read the live podcast session code, guest link, participants, recording, and sharing state.",
1498
+ ],
1499
+ [
1500
+ "start-recording",
1501
+ "Start recording the host and signal connected guests to record. Returns once recording has really started; fails with the reason otherwise.",
1502
+ ],
1503
+ [
1504
+ "stop-recording",
1505
+ "Stop recording and finalize podcast media. Check podcast_status for finalization and guest uploads.",
1506
+ ],
1507
+ [
1508
+ "start-screen-share",
1509
+ "Open the desktop picker to share and record the host screen during a podcast. Waits for the host to choose; fails if the picker is cancelled or capture fails.",
1510
+ ],
1497
1511
  ["stop-screen-share", "Stop and finalize the host's podcast screen share."],
1498
1512
  ["open-in-editor", "Open the finished podcast in the editor when guest media has arrived."],
1499
1513
  ].map(([noun, description]) => ({
@@ -1573,7 +1587,7 @@ const TOOLS = [
1573
1587
  {
1574
1588
  name: "project_update_motion_graphic",
1575
1589
  description:
1576
- "Edit a generated graphic (overlay with generatedFrom) and re-render it in place, keeping timing and position. ASYNC: { jobId }.",
1590
+ "Edit a generated graphic (overlay with generatedFrom) and re-render it in place, keeping timing and position. ASYNC: { jobId }. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
1577
1591
  inputSchema: {
1578
1592
  type: "object",
1579
1593
  properties: {
@@ -1597,7 +1611,7 @@ const TOOLS = [
1597
1611
  {
1598
1612
  name: "project_add_emoji",
1599
1613
  description:
1600
- "Place an animated emoji overlay. ASYNC: { jobId }; job_wait gives result.overlayId.",
1614
+ "Place an animated emoji overlay. ASYNC: { jobId }; job_wait gives result.overlayId. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
1601
1615
  inputSchema: {
1602
1616
  type: "object",
1603
1617
  properties: {
@@ -1803,7 +1817,7 @@ const TOOLS = [
1803
1817
  {
1804
1818
  name: "project_add_lower_third",
1805
1819
  description:
1806
- "Render a nameplate and place it at atMs in one call, laid out for the project's aspect. ASYNC: { jobId }; job_wait gives result.overlayId once placed.",
1820
+ "Render a nameplate and place it at atMs in one call, laid out for the project's aspect. ASYNC: { jobId }; job_wait gives result.overlayId once placed. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
1807
1821
  inputSchema: {
1808
1822
  type: "object",
1809
1823
  properties: {
@@ -1840,7 +1854,7 @@ const TOOLS = [
1840
1854
  {
1841
1855
  name: "project_add_title_behind",
1842
1856
  description:
1843
- "1-3 huge words at head height BEHIND the presenter. ASYNC: job_wait gives regionId, face.",
1857
+ "1-3 huge words at head height BEHIND the presenter. ASYNC: job_wait gives regionId, face. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
1844
1858
  inputSchema: {
1845
1859
  type: "object",
1846
1860
  properties: {
@@ -2587,7 +2601,7 @@ const TOOLS = [
2587
2601
  {
2588
2602
  name: "audio_probe",
2589
2603
  description:
2590
- "Per-clip audio stats: hasAudio, mean/max dB, silence spans (source time), plus audio overlays. A few seconds per clip.",
2604
+ "Per-clip audio stats: hasAudio, integratedLufs (null if unavailable/silent), tooQuiet (< -30 LUFS), mean/max dB, silence spans (source time), plus audio overlays. A few seconds per clip.",
2591
2605
  inputSchema: {
2592
2606
  type: "object",
2593
2607
  properties: {
@@ -2987,7 +3001,7 @@ const TOOLS = [
2987
3001
  {
2988
3002
  name: "project_center_camera_on_face",
2989
3003
  description:
2990
- "Keep the presenter's face centred in the camera card by tracking it (can take a minute). clear: true turns it off.",
3004
+ "Keep the presenter's face centred in the camera card by tracking it (can take a minute). clear: true turns it off. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
2991
3005
  inputSchema: {
2992
3006
  type: "object",
2993
3007
  properties: {
@@ -3004,7 +3018,7 @@ const TOOLS = [
3004
3018
  {
3005
3019
  name: "project_batch",
3006
3020
  description:
3007
- "Run up to 200 verb.noun commands on one project in order (id / path filled in; not atomic; stops at the first failure by default). Returns { ran, failed, results[] }.",
3021
+ "Run up to 200 verb.noun commands on one project in order (id / path filled in; not atomic; stops at the first failure by default). Returns { ran, failed, results[] }. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
3008
3022
  inputSchema: {
3009
3023
  type: "object",
3010
3024
  properties: {
@@ -3225,7 +3239,7 @@ const TOOLS = [
3225
3239
  {
3226
3240
  name: "project_auto_reframe",
3227
3241
  description:
3228
- "Active-speaker auto-reframe of landscape multi-person footage for vertical / square: frames whoever talks, shot by shot. split=true stacks two people (podcast / interview wide) one above the other on a 9:16 canvas. Slow (minutes). clear reverts.",
3242
+ "Active-speaker auto-reframe of landscape multi-person footage for vertical / square: frames whoever talks, shot by shot. split=true stacks two people (podcast / interview wide) one above the other on a 9:16 canvas. Slow (minutes). clear reverts. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
3229
3243
  inputSchema: {
3230
3244
  type: "object",
3231
3245
  properties: {
@@ -3689,7 +3703,7 @@ const TOOLS = [
3689
3703
  {
3690
3704
  name: "transcript_transcribe",
3691
3705
  description:
3692
- "Transcribe clips with word timestamps (default: only clips without words). To REDO a transcript (wrong language, bad run) fix the language first, then force: true (all clips) or clipId. Word fixes are re-applied; tell the user about result droppedWordEdits[] (fixes that may need redoing). Each clip is transcribed only over the part it plays (head trim to out-point, e.g. one half of a split; result transcribedRanges[]). ASYNC: { jobId }, job_wait.",
3706
+ "Transcribe clips with word timestamps (default: only clips without words). To REDO a transcript (wrong language, bad run) fix the language first, then force: true (all clips) or clipId. Word fixes are re-applied; tell the user about result droppedWordEdits[] (fixes that may need redoing). Each clip is transcribed only over the part it plays (head trim to out-point, e.g. one half of a split; result transcribedRanges[]). ASYNC: { jobId }, job_wait. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
3693
3707
  inputSchema: {
3694
3708
  type: "object",
3695
3709
  properties: {
@@ -3854,7 +3868,7 @@ const TOOLS = [
3854
3868
  {
3855
3869
  name: "audio_clean",
3856
3870
  description:
3857
- "Denoise each clip's audio, optionally reducing room echo (original kept). ASYNC: { jobId }, job_wait.",
3871
+ "Denoise each clip’s audio, optionally reducing room echo. Speech below -30 LUFS is boosted to -16 LUFS / -1.5 dBTP by default; warnings explain boosts. Original kept; audio_reset_clean restores it. ASYNC: { jobId }, job_wait. Concurrent saves are merged and retried (three attempts); exhausted conflicts report code revision_conflict / status 409.",
3858
3872
  inputSchema: {
3859
3873
  type: "object",
3860
3874
  properties: {
@@ -3862,11 +3876,42 @@ const TOOLS = [
3862
3876
  path: { type: "string" },
3863
3877
  clipId: { type: "string" },
3864
3878
  echo: { type: "boolean", description: "Only when the user mentions echo / reverb." },
3879
+ normalize: {
3880
+ type: "boolean",
3881
+ description: "Default true: boost very quiet speech. Set false to opt out.",
3882
+ },
3865
3883
  },
3866
3884
  },
3867
3885
  command: "audio.clean",
3868
3886
  },
3869
3887
 
3888
+ {
3889
+ name: "audio_normalize",
3890
+ description:
3891
+ "Normalize a clip’s active audio without denoising. Default -16 LUFS, -1.5 dBTP ceiling. Silent audio skipped with warnings. Original kept. ASYNC: job_wait.",
3892
+ inputSchema: {
3893
+ type: "object",
3894
+ properties: {
3895
+ id: { type: "string" },
3896
+ path: { type: "string" },
3897
+ clipId: { type: "string" },
3898
+ targetLufs: { type: "number", minimum: -70, maximum: -5, default: -16 },
3899
+ },
3900
+ required: ["clipId"],
3901
+ },
3902
+ command: "audio.normalize",
3903
+ },
3904
+ {
3905
+ name: "audio_reset_clean",
3906
+ description:
3907
+ "Restore original clip audio by clearing cleaned/normalized audio and echo metadata. Optional clipId; otherwise all clips. Keeps generated files. Synchronous.",
3908
+ inputSchema: {
3909
+ type: "object",
3910
+ properties: { id: { type: "string" }, path: { type: "string" }, clipId: { type: "string" } },
3911
+ },
3912
+ command: "audio.reset-clean",
3913
+ },
3914
+
3870
3915
  // ── captions ────────────────────────────────────────────────────
3871
3916
  {
3872
3917
  name: "caption_toggle",
@@ -4573,7 +4618,7 @@ const TOOLS = [
4573
4618
  {
4574
4619
  name: "media_download_url",
4575
4620
  description:
4576
- "Download a video or its audio from a page link (YouTube, Vimeo...). Only content the user has rights to. ASYNC: job_wait gives { path, title, durationMs }.",
4621
+ "Download a video or its audio from a page link (YouTube, Vimeo...). Only content the user has rights to. ASYNC: job_wait gives { path, title, durationMs }. Project placement retries concurrent-save conflicts (three attempts); exhaustion is job.errorDetails with revision_conflict / status 409.",
4577
4622
  inputSchema: {
4578
4623
  type: "object",
4579
4624
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.210.0",
3
+ "version": "1.211.0",
4
4
  "description": "Model Context Protocol server for PandaStudio. Exposes the desktop video editor's automation surface to Cursor, Continue, Cline, Claude Desktop, and any MCP-compliant client.",
5
5
  "keywords": [
6
6
  "pandastudio",