@writepanda/mcp 1.87.0 → 1.92.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 +182 -9
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -165,7 +165,7 @@ const TOOLS = [
165
165
  {
166
166
  name: "system_get_transcription_language",
167
167
  description:
168
- "Read the active transcription engine selection for the current workspace. Returns { language } where language is one of: 'auto' (Parakeet TDT v3, default — auto-detects English + 25 European languages including Russian/Ukrainian), 'chinese' / 'japanese' / 'korean' / 'hindi' / 'arabic' / 'thai' (each routes to Whisper Large-v3-turbo). Use this when surfacing the active engine to the user or before recommending a switch.",
168
+ "Read the active transcription engine selection for the current workspace. Returns { language } where language is one of: 'auto' (Parakeet TDT v3, default — auto-detects English + 25 European languages including Russian/Ukrainian), or a Whisper Large-v3-turbo language: 'chinese' / 'japanese' / 'korean' / 'hindi' / 'arabic' / 'thai' / 'tamil' / 'telugu' / 'kannada' / 'malayalam' / 'bengali' / 'marathi' / 'gujarati' / 'punjabi'. Use this when surfacing the active engine to the user or before recommending a switch.",
169
169
  inputSchema: { type: "object", properties: {} },
170
170
  command: "system.getTranscriptionLanguage",
171
171
  },
@@ -178,7 +178,23 @@ const TOOLS = [
178
178
  properties: {
179
179
  language: {
180
180
  type: "string",
181
- enum: ["auto", "chinese", "japanese", "korean", "hindi", "arabic", "thai"],
181
+ enum: [
182
+ "auto",
183
+ "chinese",
184
+ "japanese",
185
+ "korean",
186
+ "hindi",
187
+ "arabic",
188
+ "thai",
189
+ "tamil",
190
+ "telugu",
191
+ "kannada",
192
+ "malayalam",
193
+ "bengali",
194
+ "marathi",
195
+ "gujarati",
196
+ "punjabi",
197
+ ],
182
198
  description: "Target language. 'auto' = Parakeet (default); the others route to Whisper.",
183
199
  },
184
200
  },
@@ -444,6 +460,54 @@ const TOOLS = [
444
460
  },
445
461
  command: "export.publish-youtube",
446
462
  },
463
+ {
464
+ name: "instagram_connect",
465
+ description:
466
+ "Start connecting an Instagram account. Opens the user's browser to the Instagram consent page and returns `{ connectionId, redirectUrl }`. INTERACTIVE: the user must finish consent in the browser, then poll `instagram_status` until active before publishing. Only Business/Creator accounts can publish (a Meta restriction). Requires an activated PandaStudio license.",
467
+ inputSchema: { type: "object", properties: {} },
468
+ command: "instagram.connect",
469
+ },
470
+ {
471
+ name: "instagram_status",
472
+ description:
473
+ "Check whether the Instagram connection is active yet. Returns `{ status, active }`. Poll after `instagram_connect` until `active` is true.",
474
+ inputSchema: {
475
+ type: "object",
476
+ properties: {
477
+ connectionId: { type: "string", description: "From instagram_connect (optional)." },
478
+ },
479
+ },
480
+ command: "instagram.status",
481
+ },
482
+ {
483
+ name: "instagram_account",
484
+ description:
485
+ "Read the connected Instagram account: `username`, `account_type`, and `publishable` (true only for Business/Creator accounts). Call before `export_publish_instagram` to warn the user if their account type can't publish.",
486
+ inputSchema: { type: "object", properties: {} },
487
+ command: "instagram.account",
488
+ },
489
+ {
490
+ name: "instagram_disconnect",
491
+ description:
492
+ "Disconnect the user's Instagram account. Already-published Reels stay on Instagram.",
493
+ inputSchema: { type: "object", properties: {} },
494
+ command: "instagram.disconnect",
495
+ },
496
+ {
497
+ name: "export_publish_instagram",
498
+ description:
499
+ "Publish an export-library entry to Instagram as a Reel. The video uploads directly to Composio storage (never through our server) and publishes via the broker. Returns `{ mediaId, permalink }`. LONG-RUNNING: Instagram processes the Reel for ~30-120 s before it goes live. Requires a connected Business/Creator account (check `instagram_account` first). Instagram only accepts short-form video — export a 9:16 clip up to ~90 s.",
500
+ inputSchema: {
501
+ type: "object",
502
+ properties: {
503
+ id: { type: "string", description: "Export entry id." },
504
+ caption: { type: "string", description: "Reel caption, max 2200 chars; hashtags go here." },
505
+ shareToFeed: { type: "boolean", description: "Also show in the feed grid. Default true." },
506
+ },
507
+ required: ["id"],
508
+ },
509
+ command: "export.publish-instagram",
510
+ },
447
511
  {
448
512
  name: "export_list_youtube",
449
513
  description:
@@ -668,13 +732,18 @@ const TOOLS = [
668
732
  {
669
733
  name: "project_add_clip",
670
734
  description:
671
- "Append (or insert) a video clip into the project's main track. Probes duration via FFmpeg.",
735
+ "Append (or insert) a video clip into the project's main track. Probes duration via FFmpeg. Pass `webcam` (a second, separately-recorded source) to make the clip a two-speaker podcast composite (media=host, webcam=guest, kind=podcast) — for 3+ speakers or audio auto-sync use project_add_podcast_clip instead.",
672
736
  inputSchema: {
673
737
  type: "object",
674
738
  properties: {
675
739
  id: { type: "string" },
676
740
  path: { type: "string" },
677
- media: { type: "string", description: "Absolute path to video file" },
741
+ media: { type: "string", description: "Absolute path to video file (the host source)" },
742
+ webcam: {
743
+ type: "string",
744
+ description:
745
+ "Optional absolute path to a SECOND, separately-recorded source (the guest). When set, the clip becomes a two-speaker podcast composite. Use project_add_podcast_clip for 3+ participants or audio auto-sync.",
746
+ },
678
747
  atIndex: {
679
748
  type: "number",
680
749
  description: "Insert before this zero-based clip index. Omit to append at the end.",
@@ -685,6 +754,83 @@ const TOOLS = [
685
754
  },
686
755
  command: "project.add-clip",
687
756
  },
757
+ {
758
+ name: "project_add_podcast_clip",
759
+ description:
760
+ "Assemble 2-4 separately-recorded sources (e.g. host + guest exported from Zoom/Riverside) into ONE podcast composite clip — the import equivalent of an in-app/remote podcast recording. media=host (the authoritative clock); guests=ordered guest paths (1-3). Speakers are assigned host/guest/guest-2/guest-3 and stored as participants[]. By default auto-syncs each guest to the host via audio cross-correlation (set autoSync=false to skip); fine-tune later with project_set_participant_offset. After this, run transcript_transcribe (it transcribes each speaker separately and merges into one speaker-attributed transcript), then edit by transcript exactly like a recorded podcast. Preview + export render every participant via the existing N-party podcast layout.",
761
+ inputSchema: {
762
+ type: "object",
763
+ properties: {
764
+ id: { type: "string" },
765
+ path: { type: "string" },
766
+ media: { type: "string", description: "Absolute path to the HOST video (reference clock)" },
767
+ guests: {
768
+ type: "array",
769
+ items: { type: "string" },
770
+ description:
771
+ "Absolute paths to the 1-3 guest videos in participant order (guest, guest-2, guest-3).",
772
+ },
773
+ labels: {
774
+ type: "array",
775
+ items: { type: "string" },
776
+ description:
777
+ "Optional display names parallel to [host, ...guests] (index 0 = host). Falls back to the speaker id.",
778
+ },
779
+ autoSync: {
780
+ type: "boolean",
781
+ description:
782
+ "Audio-align each guest to the host before assembling (default true). Set false to assemble with zero offsets.",
783
+ },
784
+ atIndex: {
785
+ type: "number",
786
+ description: "0-based insert position in the clips array. Omit to append.",
787
+ },
788
+ expectedRevision: { type: "number" },
789
+ },
790
+ required: ["media", "guests"],
791
+ },
792
+ command: "project.add-podcast-clip",
793
+ },
794
+ {
795
+ name: "project_set_participant_offset",
796
+ description:
797
+ "Fine-tune ONE participant's A/V sync on a multi-party podcast composite. speaker = guest | guest-2 | guest-3 (the host is the reference clock and can't be offset). offsetMs shifts that participant's video AND audio vs the host: positive delays, negative advances, 0 clears (±10000). The N-party analogue of project_set_webcam_offset (which only reaches guest-1). Honored in preview + export.",
798
+ inputSchema: {
799
+ type: "object",
800
+ properties: {
801
+ id: { type: "string" },
802
+ path: { type: "string" },
803
+ clipId: { type: "string", description: "Clip id of the podcast composite" },
804
+ speaker: {
805
+ type: "string",
806
+ description: "Which participant to nudge: guest | guest-2 | guest-3",
807
+ },
808
+ offsetMs: {
809
+ type: "number",
810
+ description: "Time nudge in ms (±10000). Positive delays this guest; 0 clears.",
811
+ },
812
+ expectedRevision: { type: "number" },
813
+ },
814
+ required: ["clipId", "speaker", "offsetMs"],
815
+ },
816
+ command: "project.set-participant-offset",
817
+ },
818
+ {
819
+ name: "project_auto_sync_podcast",
820
+ description:
821
+ "Re-run audio auto-sync on an existing podcast composite clip: cross-correlates each guest's audio against the host and stores the resulting offset on each participant. Use after project_add_podcast_clip with autoSync=false, or to re-align if a manual nudge drifted. Returns the per-guest offsets + confidence; fine-tune afterward with project_set_participant_offset.",
822
+ inputSchema: {
823
+ type: "object",
824
+ properties: {
825
+ id: { type: "string" },
826
+ path: { type: "string" },
827
+ clipId: { type: "string", description: "Clip id of the podcast composite" },
828
+ expectedRevision: { type: "number" },
829
+ },
830
+ required: ["clipId"],
831
+ },
832
+ command: "project.auto-sync-podcast",
833
+ },
688
834
  {
689
835
  name: "project_add_motion_graphic",
690
836
  description:
@@ -1301,19 +1447,22 @@ const TOOLS = [
1301
1447
  {
1302
1448
  name: "project_save",
1303
1449
  description:
1304
- "Overwrite a project with the full JSON you supply. Use this when you've built or mutated a project object client-side and want to persist it. Always pass `expectedRevision` (the revision you read) — the server will reject the write if a concurrent edit happened between your read and save, returning { ok: false, details: { code: 'revision_conflict' } }.",
1450
+ "Persist a project. Two modes: (1) pass a full `project` JSON to OVERWRITE the file (use after building/mutating a project object client-side) — always pass `expectedRevision` so a concurrent edit returns { ok:false, details:{ code:'revision_conflict' } }; (2) OMIT `project` to simply confirm the current on-disk state. The add_*/set_* verbs already autosave, so a bodyless save is a no-op confirmation that returns the current revision — NOT an error. You usually don't need an explicit save at all.",
1305
1451
  inputSchema: {
1306
1452
  type: "object",
1307
1453
  properties: {
1308
1454
  id: { type: "string" },
1309
1455
  path: { type: "string" },
1310
- project: { type: "object", description: "Full project JSON to persist" },
1456
+ project: {
1457
+ type: "object",
1458
+ description:
1459
+ "Full project JSON to persist (overwrite mode). Omit to just confirm current on-disk state — edits via add_*/set_* verbs already autosave.",
1460
+ },
1311
1461
  expectedRevision: {
1312
1462
  type: "number",
1313
- description: "Revision from your last project_read. Strongly recommended.",
1463
+ description: "Revision from your last project_read. Strongly recommended in overwrite mode.",
1314
1464
  },
1315
1465
  },
1316
- required: ["project"],
1317
1466
  },
1318
1467
  command: "project.save",
1319
1468
  },
@@ -1686,6 +1835,16 @@ const TOOLS = [
1686
1835
  description: "Fallback duration when neither endMs nor maxDurationMs is given.",
1687
1836
  },
1688
1837
  volume: { type: "number", description: "Volume multiplier 0–2. Default 0.8." },
1838
+ fadeIn: {
1839
+ type: "number",
1840
+ description:
1841
+ "Fade-in ramp at the overlay's audible start, in ms (e.g. 1000 = 1s). Applied by the export mixer. Omit for a hard start.",
1842
+ },
1843
+ fadeOut: {
1844
+ type: "number",
1845
+ description:
1846
+ "Fade-out ramp ending at the overlay's audible end, in ms. Applied for bounded overlays (endMs/maxDurationMs set); ignored on uncapped full-length overlays. Useful for seam-crossfading a looped music bed. Omit for a hard stop.",
1847
+ },
1689
1848
  maxDurationMs: {
1690
1849
  type: "number",
1691
1850
  description:
@@ -2497,6 +2656,20 @@ const TOOLS = [
2497
2656
  },
2498
2657
  command: "llm.generate-description",
2499
2658
  },
2659
+ {
2660
+ name: "llm_generate_caption",
2661
+ description:
2662
+ "Generate an Instagram Reel caption (1-3 punchy sentences + a few hashtags) from the merged transcript, on the local model. Feed the result into export_publish_instagram's caption.",
2663
+ inputSchema: {
2664
+ type: "object",
2665
+ properties: {
2666
+ id: { type: "string" },
2667
+ path: { type: "string" },
2668
+ maxChars: { type: "number", description: "Default 2200" },
2669
+ },
2670
+ },
2671
+ command: "llm.generate-caption",
2672
+ },
2500
2673
  {
2501
2674
  name: "llm_generate_timestamps",
2502
2675
  description: "Generate YouTube-style chapter timestamps from the merged transcript.",
@@ -2760,7 +2933,7 @@ const TOOLS = [
2760
2933
  const server = new Server(
2761
2934
  {
2762
2935
  name: "pandastudio",
2763
- version: "1.70.0",
2936
+ version: "1.71.0",
2764
2937
  },
2765
2938
  {
2766
2939
  capabilities: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.87.0",
3
+ "version": "1.92.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",