@writepanda/mcp 1.251.0 → 1.259.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 +764 -18
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -1274,6 +1274,29 @@ const TOOLS = [
1274
1274
  borderRadius: { type: "number" },
1275
1275
  showBlur: { type: "boolean" },
1276
1276
  motionBlurAmount: { type: "number" },
1277
+ wallpaperMotion: { type: "number", description: "mesh: background speed 0-2." },
1278
+ screenFrame: {
1279
+ type: "object",
1280
+ description: "Device frame for screen recordings.",
1281
+ properties: {
1282
+ kind: {
1283
+ type: "string",
1284
+ enum: ["browser", "window", "laptop", "phone", "tablet"],
1285
+ },
1286
+ theme: { type: "string", enum: ["light", "dark"] },
1287
+ url: { type: "string" },
1288
+ },
1289
+ },
1290
+ screenTilt: {
1291
+ type: "object",
1292
+ description: "3D tilt of screen recordings.",
1293
+ properties: {
1294
+ angle: { type: "string", enum: ["flat", "left", "right", "above"] },
1295
+ followCursor: { type: "boolean" },
1296
+ clickBounce: { type: "boolean" },
1297
+ strength: { type: "number", description: "0-1." },
1298
+ },
1299
+ },
1277
1300
  },
1278
1301
  },
1279
1302
  graphicSound: {
@@ -2412,12 +2435,18 @@ const TOOLS = [
2412
2435
  {
2413
2436
  name: "podcast_draft_rundown",
2414
2437
  description:
2415
- "Have the studio's producer draft the run of show from a short brief: segments with questions, guest lower thirds, and a YouTube title, description and thumbnail idea, on the model the user picked for the assistant. Replaces the current run of show unless apply=false.",
2438
+ "Have the studio's producer draft the run of show from a short brief: segments with questions, guest lower thirds, and a YouTube title, description and thumbnail idea, on the model the user picked for the assistant. Pass the host's own questions as questions: each goes in word for word (on the prompter card) with a lower third. Replaces the current run of show unless apply=false.",
2416
2439
  inputSchema: {
2417
2440
  type: "object",
2418
2441
  properties: {
2419
2442
  brief: { type: "string" },
2420
2443
  minutes: { type: "number", description: "Total show length (default 45)." },
2444
+ questions: {
2445
+ type: "array",
2446
+ items: { type: "string" },
2447
+ description:
2448
+ "The host's own questions, in order: kept word for word, each with a lower third.",
2449
+ },
2421
2450
  guests: {
2422
2451
  type: "array",
2423
2452
  items: {
@@ -4708,7 +4737,7 @@ const TOOLS = [
4708
4737
  {
4709
4738
  name: "project_set_wallpaper",
4710
4739
  description:
4711
- "Set the background: a wallpaper value, or source=desktop (a kept still of the user's desktop wallpaper) / source=edges (Match video edges: a soft gradient from the video's border colours, slowly following the content; same in preview and export).",
4740
+ "Set the background: a wallpaper value, or source=desktop (a kept still of the user's desktop wallpaper) / source=edges (Match video edges: a soft gradient from the video's border colours, slowly following the content; same in preview and export). Animated: 'mesh:<id>' = a living mesh gradient (blobs drift in a seamless 24 s loop); motion = drift speed.",
4712
4741
  inputSchema: {
4713
4742
  type: "object",
4714
4743
  properties: {
@@ -4717,9 +4746,13 @@ const TOOLS = [
4717
4746
  wallpaper: {
4718
4747
  type: "string",
4719
4748
  description:
4720
- "Bundled id (e.g. gradient-blue), color, CSS gradient or 'none'. Omit with source.",
4749
+ "Bundled id (e.g. gradient-blue), color, CSS gradient, 'none', or an animated mesh:aurora | mesh:sunset | mesh:ocean | mesh:candy | mesh:forest | mesh:midnight | mesh:peach | mesh:graphite. Omit with source.",
4721
4750
  },
4722
4751
  source: { type: "string", enum: ["desktop", "edges"] },
4752
+ motion: {
4753
+ type: "number",
4754
+ description: "Animated (mesh:) drift speed 0-2: 0 still, 1 default. Kept when omitted.",
4755
+ },
4723
4756
  expectedRevision: { type: "number" },
4724
4757
  },
4725
4758
  },
@@ -4779,7 +4812,7 @@ const TOOLS = [
4779
4812
  {
4780
4813
  name: "project_set_style",
4781
4814
  description:
4782
- "Framing style, main-video frame (shape, border ring; drawn when the video is a card) and drawn-cursor behaviour. Only passed fields change. Camera tile of screen+camera: project_set_webcam_style.",
4815
+ "Framing style, main-video frame (shape, border ring; drawn when the video is a card), drawn-cursor behaviour, and the screen's device frame + 3D tilt (screen recordings / images only). Only passed fields change. Camera tile of screen+camera: project_set_webcam_style.",
4783
4816
  inputSchema: {
4784
4817
  type: "object",
4785
4818
  properties: {
@@ -4803,6 +4836,27 @@ const TOOLS = [
4803
4836
  borderWidth: { type: "number", description: "px 0-40." },
4804
4837
  borderColor: { type: "string" },
4805
4838
  resetFrame: { type: "boolean", description: "Clear shape + border first." },
4839
+ deviceFrame: {
4840
+ type: "string",
4841
+ enum: ["browser", "window", "laptop", "phone", "tablet", "none"],
4842
+ description: "Frame around the screen; the video shrinks to fit inside it.",
4843
+ },
4844
+ deviceFrameTheme: { type: "string", enum: ["light", "dark"] },
4845
+ deviceFrameUrl: {
4846
+ type: "string",
4847
+ description: "Browser address / window title; '' clears.",
4848
+ },
4849
+ tilt: {
4850
+ type: "string",
4851
+ enum: ["flat", "left", "right", "above"],
4852
+ description: "3D angle (left / right: that edge goes back; above: seen from above).",
4853
+ },
4854
+ tiltFollowCursor: {
4855
+ type: "boolean",
4856
+ description: "Lean toward the cursor (needs cursor data).",
4857
+ },
4858
+ tiltClickBounce: { type: "boolean", description: "Rock slightly on each click." },
4859
+ tiltStrength: { type: "number", description: "0-100, default 100." },
4806
4860
  expectedRevision: { type: "number" },
4807
4861
  },
4808
4862
  },
@@ -4953,7 +5007,7 @@ const TOOLS = [
4953
5007
  {
4954
5008
  name: "project_align_cursor",
4955
5009
  description:
4956
- "Fix a screen recording whose drawn cursor / cursor auto-zooms land in the wrong place (old recordings saved the pointer with the wrong mapping) without re-recording. Pins: at atMs the REAL pointer (or what was clicked) is at x,y, fractions 0-1 of the recorded frame. 1 pin = offset; 2+ pins far apart also fix scale per axis. Stored on the clip (non-destructive), used by preview, export and auto-zoom; cursor auto-zooms follow unless moveZooms false. reset removes it. Check with project_render_frame.",
5010
+ "Fix a screen recording whose drawn cursor / cursor auto-zooms land in the wrong place (old recordings saved the pointer with the wrong mapping) without re-recording. Pins: at atMs the REAL pointer (or what was clicked) is at x,y, fractions 0-1 of the recorded frame. 1 pin = offset; 2+ pins far apart also fix scale per axis; the LAST pin is matched exactly (earlier ones steer the scale). Stored on the clip (non-destructive), used by preview, export and auto-zoom; cursor auto-zooms follow unless moveZooms false. reset removes it. Check with project_render_frame.",
4957
5011
  inputSchema: {
4958
5012
  type: "object",
4959
5013
  properties: {
@@ -4966,7 +5020,7 @@ const TOOLS = [
4966
5020
  points: {
4967
5021
  type: "array",
4968
5022
  description:
4969
- "[{atMs (edited timeline ms) or sourceMs (clip's own ms), x 0-1, y 0-1}] where the real pointer is.",
5023
+ "[{atMs (edited timeline ms) or sourceMs (clip's own ms), x 0-1, y 0-1}] where the real pointer is. Last pin is exact.",
4970
5024
  items: {
4971
5025
  type: "object",
4972
5026
  properties: {
@@ -5638,7 +5692,7 @@ const TOOLS = [
5638
5692
  {
5639
5693
  name: "audio_enhance",
5640
5694
  description:
5641
- "Enhance voice: THE audio cleanup step (one call). Runs the studio chain on speech, per source (a podcast's host and each guest separately): noise removal, coughs/bumps out of pauses, mouth-click repair, broadcast tone, boom control, level riding (lifts very quiet speech too), de-esser, quieter pauses, -16 LUFS / -1 dBTP. strength: natural (default), light (gentle tidy), strong (assertive broadcast). keepBackground true for vlogs / ambience where the room is part of the video (no noise removal, event remover or pause expander; level, EQ and loudness still run). Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback) (denoiser deepfilternet3, or keepBackground); when the model runs, echo is skipped and the result says echoSkipped: ai-model-removes-echo (relay echoSkippedReason, do not retry). restore true = Studio mic sound (the app's name) for thin, muffled or call-quality mics only (never on a good mic): TF-Restormer on Apple silicon, AP-BWE on Windows and Intel Macs (CPU, faster than real time); restoreEngine in the result says which ran. reference = a clip id or file whose voice tone to match. off true restores the original audio. The noise-removal engine is automatic. Every run processes the original media, so running again replaces the result (never stacks). ASYNC: { jobId }, job_wait; the result reports before/after loudness (LUFS, LRA, true peak, speech over pauses, tone deviation) and the engine per source: tell the user.",
5695
+ "Enhance voice: THE audio cleanup step (one call). Runs the studio chain on speech, per source (a podcast's host and each guest separately): noise removal, coughs/bumps out of pauses, mouth-click repair, broadcast tone, boom control, level riding (lifts very quiet speech too), de-esser, quieter pauses, -16 LUFS / -1 dBTP. strength: light (default, the best-sounding setup with Studio mic sound), natural (fuller processing), strong (assertive broadcast; only when the user asks, never by your own choice). keepBackground true for vlogs / ambience / gameplay where the room is part of the video (no noise removal, event remover or pause expander; level, EQ and loudness still run). A stereo source keeps its stereo (result channels: 2; else channels: 1 with monoReason one-channel / dual-mono / silent / studio-mic), and Studio mic sound stays off unless restore is true (it rebuilds the voice without the room). Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback) (denoiser deepfilternet3, or keepBackground); when the model runs, echo is skipped and the result says echoSkipped: ai-model-removes-echo (relay echoSkippedReason, do not retry). restore = Studio mic sound (the app's name): leave it unset and it is ON on Apple silicon with macOS 14+ (TF-Restormer; Light + Studio mic sound is the default there) and off on Windows and Intel Macs (AP-BWE; pass true there only for thin, muffled or call-quality mics); false only when the user asks. restored / restoreEngine in the result say what ran. reference = a clip id or file whose voice tone to match. off true restores the original audio. The noise-removal engine is automatic. Every run processes the original media, so running again replaces the result (never stacks). ASYNC: { jobId }, job_wait; the result reports before/after loudness (LUFS, LRA, true peak, speech over pauses, tone deviation) and the engine per source: tell the user.",
5642
5696
  inputSchema: {
5643
5697
  type: "object",
5644
5698
  properties: {
@@ -5652,7 +5706,7 @@ const TOOLS = [
5652
5706
  strength: {
5653
5707
  type: "string",
5654
5708
  enum: ["light", "natural", "strong"],
5655
- default: "natural",
5709
+ default: "light",
5656
5710
  },
5657
5711
  keepBackground: {
5658
5712
  type: "boolean",
@@ -5667,9 +5721,8 @@ const TOOLS = [
5667
5721
  },
5668
5722
  restore: {
5669
5723
  type: "boolean",
5670
- default: false,
5671
5724
  description:
5672
- "Studio mic sound for a thin / low-bandwidth mic (TF-Restormer on Apple silicon, AP-BWE elsewhere).",
5725
+ "Studio mic sound. Leave unset: ON on Apple silicon with macOS 14+ (TF-Restormer), off on Windows and Intel Macs (AP-BWE). false only when the user asks.",
5673
5726
  },
5674
5727
  reference: { type: "string", description: "Clip id or audio/video file to sound like." },
5675
5728
  off: { type: "boolean", description: "Remove the enhancement (original audio)." },
@@ -5748,7 +5801,7 @@ const TOOLS = [
5748
5801
  {
5749
5802
  name: "caption_set_template",
5750
5803
  description:
5751
- "Pick a caption template (default glowStack); clears style overrides. Emphasis templates (hormoziEmphasis, tiltedBox, serifItalic, condensedCaps, scriptKeyword) style the IMPORTANT word of each phrase, not the spoken one; emphasis is detected on apply when missing (result.emphasis).",
5804
+ "Pick a caption template (new projects start with hormoziEmphasis when vertical, classic otherwise; use those when the user names no style, never glowStack unless asked); clears style overrides. Emphasis templates (hormoziEmphasis, tiltedBox, serifItalic, condensedCaps, scriptKeyword) style the IMPORTANT word of each phrase, not the spoken one; emphasis is detected on apply when missing (result.emphasis).",
5752
5805
  inputSchema: {
5753
5806
  type: "object",
5754
5807
  properties: {
@@ -6081,10 +6134,55 @@ const TOOLS = [
6081
6134
  description:
6082
6135
  "Hide desktop icons for a whole-display recording (restored on stop/crash/quit). Default: the user's setting. Result tip = ask the user to turn on Do Not Disturb.",
6083
6136
  },
6137
+ windowSize: {
6138
+ type: "string",
6139
+ enum: ["16:9", "16:10", "4:3", "1:1", "9:16", "21:9", "720p", "1080p", "1440p", "4K"],
6140
+ description:
6141
+ "Window sources only: resize the window first. Ratio = same area, new shape; resolution = exact recorded pixels (fails if the display is too small). macOS needs Accessibility access.",
6142
+ },
6143
+ centerWindow: {
6144
+ type: "boolean",
6145
+ description:
6146
+ "Window sources only: move the window to the middle of its display for the recording, back on stop/failure/crash/quit. Default: the user's setting.",
6147
+ },
6084
6148
  },
6085
6149
  },
6086
6150
  command: "recording.start",
6087
6151
  },
6152
+ {
6153
+ name: "recording_resize_window",
6154
+ description:
6155
+ "Resize a window before recording it (the picker's size chips): a ratio keeps its area in the new shape, a resolution makes the recording exactly that size (1080p on a 2x display = a 960 x 540 point window). macOS needs Accessibility access (the error says how). Returns { frame, pixels }.",
6156
+ inputSchema: {
6157
+ type: "object",
6158
+ properties: {
6159
+ source: { type: "string", description: "'window:N:0' from recording_list_sources." },
6160
+ size: {
6161
+ type: "string",
6162
+ enum: ["16:9", "16:10", "4:3", "1:1", "9:16", "21:9", "720p", "1080p", "1440p", "4K"],
6163
+ },
6164
+ },
6165
+ required: ["source", "size"],
6166
+ },
6167
+ command: "recording.resize-window",
6168
+ },
6169
+ {
6170
+ name: "recording_get_window_options",
6171
+ description: "Window-recording options: { centerWindow, supported }.",
6172
+ inputSchema: { type: "object", properties: {} },
6173
+ command: "recording.get-window-options",
6174
+ },
6175
+ {
6176
+ name: "recording_set_center_window",
6177
+ description:
6178
+ "Turn Center on screen on/off for window recordings (window moves to the middle of its display during the recording, back after). Off by default. Returns { centerWindow }.",
6179
+ inputSchema: {
6180
+ type: "object",
6181
+ properties: { enabled: { type: "boolean" } },
6182
+ required: ["enabled"],
6183
+ },
6184
+ command: "recording.set-center-window",
6185
+ },
6088
6186
  {
6089
6187
  name: "recording_get_clean_desktop",
6090
6188
  description: "Clean-desktop recording setting. Returns { cleanDesktop, supported }.",
@@ -6639,16 +6737,409 @@ const TOOLS = [
6639
6737
  {
6640
6738
  name: "memory_save",
6641
6739
  description:
6642
- "Save a durable workspace fact or preference for all future chats (not one-off requests).",
6740
+ "Save a durable fact or preference the user STATED, for all future chats (not one-off requests). kind profile = who they are. Over budget it's refused with the current entries: merge (memory_replace) or forget first. Noticed but not stated: memory_suggest.",
6643
6741
  inputSchema: {
6644
6742
  type: "object",
6645
6743
  properties: {
6646
6744
  note: { type: "string", description: "One concise fact." },
6745
+ kind: {
6746
+ type: "string",
6747
+ enum: ["notes", "profile"],
6748
+ description: "notes (default): preferences. profile: who the creator is.",
6749
+ },
6647
6750
  },
6648
6751
  required: ["note"],
6649
6752
  },
6650
6753
  command: "memory.save",
6651
6754
  },
6755
+ {
6756
+ name: "memory_replace",
6757
+ description: "Rewrite one memory entry by id: correct it, or merge two to stay under budget.",
6758
+ inputSchema: {
6759
+ type: "object",
6760
+ properties: {
6761
+ entryId: { type: "string", description: "Entry id from memory_list." },
6762
+ note: { type: "string", description: "The new text." },
6763
+ kind: {
6764
+ type: "string",
6765
+ enum: ["notes", "profile"],
6766
+ description: "notes (default): preferences. profile: who the creator is.",
6767
+ },
6768
+ },
6769
+ required: ["entryId", "note"],
6770
+ },
6771
+ command: "memory.replace",
6772
+ },
6773
+ {
6774
+ name: "memory_suggest",
6775
+ description:
6776
+ "PandaStudio's own chat only: a 'Remember this?' card for a preference you noticed but the user didn't state. Their Save writes it; don't save it yourself.",
6777
+ inputSchema: {
6778
+ type: "object",
6779
+ properties: {
6780
+ note: { type: "string", description: "The preference, worded as it would be saved." },
6781
+ reason: { type: "string", description: "One line on why you think so." },
6782
+ kind: {
6783
+ type: "string",
6784
+ enum: ["notes", "profile"],
6785
+ description: "notes (default): preferences. profile: who the creator is.",
6786
+ },
6787
+ },
6788
+ required: ["note"],
6789
+ },
6790
+ command: "memory.suggest",
6791
+ },
6792
+ {
6793
+ name: "chat_ask",
6794
+ description:
6795
+ "PandaStudio's own chat only: ask the user a multiple-choice question as a card with 2-5 buttons. Doesn't wait; END YOUR TURN after calling it and the choice arrives as the user's next message. For real decisions (light or heavy edit, which opening), not things you can decide yourself. Other agents get not_supported: ask in your own chat.",
6796
+ inputSchema: {
6797
+ type: "object",
6798
+ properties: {
6799
+ question: { type: "string", description: "One short sentence." },
6800
+ options: {
6801
+ type: "array",
6802
+ description: "2-5 choices: strings, or { label, detail } with detail one short line.",
6803
+ items: {
6804
+ anyOf: [
6805
+ { type: "string" },
6806
+ {
6807
+ type: "object",
6808
+ properties: { label: { type: "string" }, detail: { type: "string" } },
6809
+ required: ["label"],
6810
+ },
6811
+ ],
6812
+ },
6813
+ },
6814
+ recommended: { type: "string", description: "Label of the choice you'd pick." },
6815
+ allowOther: { type: "boolean", description: "Also allow a typed answer." },
6816
+ },
6817
+ required: ["question", "options"],
6818
+ },
6819
+ command: "chat.ask",
6820
+ },
6821
+ {
6822
+ name: "motion_propose",
6823
+ description:
6824
+ "PandaStudio's own chat: show a rendered graphic playing in the chat with Add to timeline / Try another before placing it. Render first (motion_generate or motion_render_html, job_wait), then pass the jobId and where it goes. For graphic ideas and judgment calls; a graphic the user asked for goes straight in with project_add_motion_graphic.",
6825
+ inputSchema: {
6826
+ type: "object",
6827
+ properties: {
6828
+ id: { type: "string", description: "Project id" },
6829
+ path: { type: "string" },
6830
+ fromJob: { type: "string", description: "Finished render job (preferred)." },
6831
+ file: { type: "string", description: "Rendered MP4/WebM when there's no job." },
6832
+ atMs: { type: "number", description: "Edited timeline start." },
6833
+ durationMs: { type: "number", description: "Default: the render's length." },
6834
+ title: { type: "string", description: "What it shows, a few words." },
6835
+ reason: { type: "string", description: "Why at this moment." },
6836
+ },
6837
+ required: ["atMs", "title"],
6838
+ },
6839
+ command: "motion.propose",
6840
+ },
6841
+ {
6842
+ name: "post_plan",
6843
+ description:
6844
+ "PandaStudio's own chat: suggest where and when to post finished exports, a caption each, as an editable schedule card. Nothing posts until the user approves; then each row is queued via export_publish_social and you get the job ids. Export ids from export_list, accounts from social_channels.",
6845
+ inputSchema: {
6846
+ type: "object",
6847
+ properties: {
6848
+ summary: { type: "string", description: "One line on the plan." },
6849
+ rows: {
6850
+ type: "array",
6851
+ items: {
6852
+ type: "object",
6853
+ properties: {
6854
+ exportId: { type: "string" },
6855
+ channelIds: { type: "array", items: { type: "string" } },
6856
+ networks: {
6857
+ type: "array",
6858
+ items: {
6859
+ type: "string",
6860
+ enum: ["instagram", "tiktok", "facebook", "linkedin", "x"],
6861
+ },
6862
+ },
6863
+ caption: { type: "string" },
6864
+ when: {
6865
+ type: "string",
6866
+ description: "now, or ISO with offset (e.g. 2026-11-03T18:30:00+00:00).",
6867
+ },
6868
+ },
6869
+ required: ["exportId", "caption"],
6870
+ },
6871
+ },
6872
+ },
6873
+ required: ["rows"],
6874
+ },
6875
+ command: "post.plan",
6876
+ },
6877
+ {
6878
+ name: "plan_propose",
6879
+ description:
6880
+ "Write an editing plan for the user to approve BEFORE editing (summary, up to 3 questions, up to 20 steps). Shown as a plan card with Start editing; nothing changes yet. End your turn after proposing; when they press Start you get the planId and answers, then do the steps with plan_update_step after each. Mark needsApproval on steps that spend money, publish or reach outside the app.",
6881
+ inputSchema: {
6882
+ type: "object",
6883
+ properties: {
6884
+ id: { type: "string", description: "Project id" },
6885
+ path: { type: "string", description: "Absolute .pandastudio path (alternative to id)" },
6886
+ summary: { type: "string", description: "One line: what the footage is and the angle." },
6887
+ steps: {
6888
+ type: "array",
6889
+ description: "Steps in order.",
6890
+ items: {
6891
+ type: "object",
6892
+ properties: {
6893
+ title: { type: "string" },
6894
+ stage: { type: "string", enum: ["prep", "edit", "clip", "post"] },
6895
+ detail: { type: "string" },
6896
+ needsApproval: { type: "boolean" },
6897
+ },
6898
+ required: ["title"],
6899
+ },
6900
+ },
6901
+ questions: {
6902
+ type: "array",
6903
+ description: "Decisions only the user can make (max 3).",
6904
+ items: {
6905
+ type: "object",
6906
+ properties: {
6907
+ text: { type: "string" },
6908
+ options: { type: "array", items: { type: "string" } },
6909
+ recommended: { type: "string" },
6910
+ },
6911
+ required: ["text", "options"],
6912
+ },
6913
+ },
6914
+ recipeId: { type: "string", description: "Recipe the plan follows (recipe_pick)." },
6915
+ source: { type: "string", enum: ["chat", "recording-finished", "routine"] },
6916
+ },
6917
+ required: ["summary", "steps"],
6918
+ },
6919
+ command: "plan.propose",
6920
+ },
6921
+ {
6922
+ name: "plan_get",
6923
+ description:
6924
+ "The project's open plan (proposed or running) with step statuses and the next step; plan: null when none.",
6925
+ inputSchema: {
6926
+ type: "object",
6927
+ properties: {
6928
+ id: { type: "string" },
6929
+ path: { type: "string" },
6930
+ includeHistory: {
6931
+ type: "boolean",
6932
+ description: "Also return finished and dismissed plans.",
6933
+ },
6934
+ },
6935
+ },
6936
+ command: "plan.get",
6937
+ },
6938
+ {
6939
+ name: "plan_answer",
6940
+ description:
6941
+ "Record the user's answer to a plan question (option id or label). Only before the plan starts.",
6942
+ inputSchema: {
6943
+ type: "object",
6944
+ properties: {
6945
+ id: { type: "string" },
6946
+ path: { type: "string" },
6947
+ planId: { type: "string" },
6948
+ questionId: { type: "string" },
6949
+ optionId: { type: "string" },
6950
+ },
6951
+ required: ["questionId", "optionId"],
6952
+ },
6953
+ command: "plan.answer",
6954
+ },
6955
+ {
6956
+ name: "plan_start",
6957
+ description:
6958
+ "Start a proposed plan (unanswered questions take the recommended choice). The card's Start editing does this; call it only when the user told you to go ahead.",
6959
+ inputSchema: {
6960
+ type: "object",
6961
+ properties: { id: { type: "string" }, path: { type: "string" }, planId: { type: "string" } },
6962
+ },
6963
+ command: "plan.start",
6964
+ },
6965
+ {
6966
+ name: "plan_update_step",
6967
+ description:
6968
+ "Mark a step of the running plan: running, then done (result with the real counts), skipped or failed (say why).",
6969
+ inputSchema: {
6970
+ type: "object",
6971
+ properties: {
6972
+ id: { type: "string" },
6973
+ path: { type: "string" },
6974
+ planId: { type: "string" },
6975
+ stepId: { type: "string" },
6976
+ status: { type: "string", enum: ["running", "done", "skipped", "failed"] },
6977
+ result: { type: "string" },
6978
+ },
6979
+ required: ["stepId", "status"],
6980
+ },
6981
+ command: "plan.update-step",
6982
+ },
6983
+ {
6984
+ name: "plan_finish",
6985
+ description: "Close a plan: done (unreached steps become skipped) or dismissed.",
6986
+ inputSchema: {
6987
+ type: "object",
6988
+ properties: {
6989
+ id: { type: "string" },
6990
+ path: { type: "string" },
6991
+ planId: { type: "string" },
6992
+ status: { type: "string", enum: ["done", "dismissed"] },
6993
+ },
6994
+ required: ["status"],
6995
+ },
6996
+ command: "plan.finish",
6997
+ },
6998
+ {
6999
+ name: "routine_templates",
7000
+ description: "Ready-made routines with the exact settings for routine_propose.",
7001
+ inputSchema: { type: "object", properties: {} },
7002
+ command: "routine.templates",
7003
+ },
7004
+ {
7005
+ name: "routine_list",
7006
+ description:
7007
+ "This workspace's routines (proposed / on / paused), triggers in plain words, next run and recent runs with reports.",
7008
+ inputSchema: { type: "object", properties: {} },
7009
+ command: "routine.list",
7010
+ },
7011
+ {
7012
+ name: "routine_propose",
7013
+ description:
7014
+ "Propose a routine: recurring or event-triggered work done on its own. Created OFF; the user turns it on from a routine card. Say what it does, how often, and that it runs on their AI account.",
7015
+ inputSchema: {
7016
+ type: "object",
7017
+ properties: {
7018
+ name: { type: "string", description: "A few words, e.g. 'Monday competitor scan'." },
7019
+ prompt: {
7020
+ type: "string",
7021
+ description: "What the agent does each run (unattended: no questions, read-only shell).",
7022
+ },
7023
+ schedule: {
7024
+ type: "string",
7025
+ description:
7026
+ "'every 2h', 'daily 09:00', 'weekdays 09:00', 'mon,thu 18:30', 'monthly 1 09:00' or an ISO time. Schedule OR event; min every 15 min.",
7027
+ },
7028
+ timezone: { type: "string", description: "IANA zone; default this Mac's." },
7029
+ event: {
7030
+ type: "string",
7031
+ enum: ["recording.finished", "import.finished", "podcast.ended", "export.finished"],
7032
+ description: "Run when this happens instead of on a schedule.",
7033
+ },
7034
+ precheck: {
7035
+ type: "object",
7036
+ description:
7037
+ "Free check before the AI turn; nothing found = run skipped. {kind:'ideas-breakouts',minMultiplier} | {kind:'new-exports'} | {kind:'connector-connected',connector}.",
7038
+ },
7039
+ autonomy: {
7040
+ type: "string",
7041
+ enum: ["suggest", "act"],
7042
+ description: "suggest (default): no edits. act: edits that need no approval.",
7043
+ },
7044
+ notify: { type: "string", enum: ["desktop", "none"] },
7045
+ template: { type: "string", description: "Start from a routine_templates id." },
7046
+ },
7047
+ required: ["name", "prompt"],
7048
+ },
7049
+ command: "routine.propose",
7050
+ },
7051
+ {
7052
+ name: "routine_update",
7053
+ description:
7054
+ "Change a routine. name/notify apply now; prompt, schedule/event, precheck or autonomy become a pending change the user approves on the card.",
7055
+ inputSchema: {
7056
+ type: "object",
7057
+ properties: {
7058
+ routineId: { type: "string" },
7059
+ name: { type: "string", description: "A few words, e.g. 'Monday competitor scan'." },
7060
+ prompt: {
7061
+ type: "string",
7062
+ description: "What the agent does each run (unattended: no questions, read-only shell).",
7063
+ },
7064
+ schedule: {
7065
+ type: "string",
7066
+ description:
7067
+ "'every 2h', 'daily 09:00', 'weekdays 09:00', 'mon,thu 18:30', 'monthly 1 09:00' or an ISO time. Schedule OR event; min every 15 min.",
7068
+ },
7069
+ timezone: { type: "string", description: "IANA zone; default this Mac's." },
7070
+ event: {
7071
+ type: "string",
7072
+ enum: ["recording.finished", "import.finished", "podcast.ended", "export.finished"],
7073
+ description: "Run when this happens instead of on a schedule.",
7074
+ },
7075
+ precheck: {
7076
+ type: "object",
7077
+ description:
7078
+ "Free check before the AI turn; nothing found = run skipped. {kind:'ideas-breakouts',minMultiplier} | {kind:'new-exports'} | {kind:'connector-connected',connector}.",
7079
+ },
7080
+ autonomy: {
7081
+ type: "string",
7082
+ enum: ["suggest", "act"],
7083
+ description: "suggest (default): no edits. act: edits that need no approval.",
7084
+ },
7085
+ notify: { type: "string", enum: ["desktop", "none"] },
7086
+ },
7087
+ required: ["routineId"],
7088
+ },
7089
+ command: "routine.update",
7090
+ },
7091
+ {
7092
+ name: "routine_pause",
7093
+ description: "Pause a routine.",
7094
+ inputSchema: {
7095
+ type: "object",
7096
+ properties: { routineId: { type: "string" } },
7097
+ required: ["routineId"],
7098
+ },
7099
+ command: "routine.pause",
7100
+ },
7101
+ {
7102
+ name: "routine_resume",
7103
+ description: "Resume a paused routine (not a proposal).",
7104
+ inputSchema: {
7105
+ type: "object",
7106
+ properties: { routineId: { type: "string" } },
7107
+ required: ["routineId"],
7108
+ },
7109
+ command: "routine.resume",
7110
+ },
7111
+ {
7112
+ name: "routine_run",
7113
+ description:
7114
+ "Run a routine now in the background; the report lands in the Routines thread and routine_runs.",
7115
+ inputSchema: {
7116
+ type: "object",
7117
+ properties: { routineId: { type: "string" } },
7118
+ required: ["routineId"],
7119
+ },
7120
+ command: "routine.run",
7121
+ },
7122
+ {
7123
+ name: "routine_runs",
7124
+ description:
7125
+ "A routine's runs, newest first: done, silent, skipped, failed, missed, interrupted; with reports.",
7126
+ inputSchema: {
7127
+ type: "object",
7128
+ properties: { routineId: { type: "string" }, limit: { type: "number" } },
7129
+ required: ["routineId"],
7130
+ },
7131
+ command: "routine.runs",
7132
+ },
7133
+ {
7134
+ name: "routine_delete",
7135
+ description: "Delete a routine and its history (asks the user on a confirm card).",
7136
+ inputSchema: {
7137
+ type: "object",
7138
+ properties: { routineId: { type: "string" } },
7139
+ required: ["routineId"],
7140
+ },
7141
+ command: "routine.delete",
7142
+ },
6652
7143
  {
6653
7144
  name: "recipe_list",
6654
7145
  description: "List edit recipes (proven styles): the user's saved ones first, then starters.",
@@ -6791,6 +7282,11 @@ const TOOLS = [
6791
7282
  type: "object",
6792
7283
  properties: {
6793
7284
  query: { type: "string", description: "Entry id, or text removing every match." },
7285
+ kind: {
7286
+ type: "string",
7287
+ enum: ["notes", "profile"],
7288
+ description: "notes (default): preferences. profile: who the creator is.",
7289
+ },
6794
7290
  },
6795
7291
  required: ["query"],
6796
7292
  },
@@ -6798,8 +7294,17 @@ const TOOLS = [
6798
7294
  },
6799
7295
  {
6800
7296
  name: "memory_list",
6801
- description: "List durable memory entries (ids for memory_forget).",
6802
- inputSchema: { type: "object", properties: {} },
7297
+ description: "Both memory lists (notes, profile) with ids and budget use; kind for one list.",
7298
+ inputSchema: {
7299
+ type: "object",
7300
+ properties: {
7301
+ kind: {
7302
+ type: "string",
7303
+ enum: ["notes", "profile"],
7304
+ description: "notes (default): preferences. profile: who the creator is.",
7305
+ },
7306
+ },
7307
+ },
6803
7308
  command: "memory.list",
6804
7309
  },
6805
7310
  {
@@ -7423,7 +7928,7 @@ const TOOLS = [
7423
7928
  },
7424
7929
  captionTemplate: {
7425
7930
  type: "string",
7426
- description: "Caption template id (default glowStack), or none.",
7931
+ description: "Caption template id (default hormoziEmphasis), or none.",
7427
7932
  },
7428
7933
  },
7429
7934
  required: ["file"],
@@ -7496,6 +8001,247 @@ const TOOLS = [
7496
8001
  },
7497
8002
  command: "clips.delete",
7498
8003
  },
8004
+ {
8005
+ name: "ideas_research",
8006
+ description:
8007
+ "Find outliers (videos beating their own channel's median views) across a niche: searches YouTube for the topic (comma-separate several angles) and/or expands from a seed channel the target audience watches, reads every channel found and ranks their recent uploads by multiplier. For what's trending, pass period month and maxAgeDays 30. Async: returns { researchId, jobId }; job.wait the jobId (~20-40 s), then ideas.get.",
8008
+ inputSchema: {
8009
+ type: "object",
8010
+ properties: {
8011
+ query: {
8012
+ type: "string",
8013
+ description: 'Topic(s), comma-separated: "ai video editing, descript alternatives".',
8014
+ },
8015
+ seedChannel: {
8016
+ type: "string",
8017
+ description:
8018
+ "A channel the target audience watches: @handle, channel URL or UC id. Give this, query, or both.",
8019
+ },
8020
+ format: { type: "string", description: "videos (long-form, default) | shorts." },
8021
+ minMultiplier: {
8022
+ type: "number",
8023
+ description:
8024
+ "Keep videos at or above this multiple of their channel's median (default 3).",
8025
+ },
8026
+ maxAgeDays: {
8027
+ type: "number",
8028
+ description: "Only outliers published in the last N days (default 365, 0 = any age).",
8029
+ },
8030
+ maxChannels: {
8031
+ type: "number",
8032
+ description: "Channels to read, 3-40 (default 15). More is slower and broader.",
8033
+ },
8034
+ period: {
8035
+ type: "string",
8036
+ description:
8037
+ "Limit the topic search to uploads from the last: any (default) | day | week | month | year.",
8038
+ },
8039
+ },
8040
+ },
8041
+ command: "ideas.research",
8042
+ },
8043
+ {
8044
+ name: "ideas_list",
8045
+ description:
8046
+ "Every outlier research in this workspace, newest first: id, title, options, status, stats and top multiplier. Reuse a recent one instead of researching the same niche again.",
8047
+ inputSchema: {
8048
+ type: "object",
8049
+ properties: {},
8050
+ },
8051
+ command: "ideas.list",
8052
+ },
8053
+ {
8054
+ name: "ideas_get",
8055
+ description:
8056
+ "One research: status, outliers best first (title, url, multiplier, views, channel median, age; onTopic false marks titles that don't mention the topic), the channels read (median views, hit rate, uploads a week) and title patterns (recurring phrases, hook-style shares, title formulas).",
8057
+ inputSchema: {
8058
+ type: "object",
8059
+ properties: {
8060
+ researchId: { type: "string", description: "From ideas.research or ideas.list." },
8061
+ limit: { type: "number", description: "Max outliers returned, 1-200 (default 25)." },
8062
+ offset: { type: "number", description: "Skip this many outliers (to page)." },
8063
+ onTopicOnly: {
8064
+ type: "boolean",
8065
+ description: "Only outliers whose titles mention the topic.",
8066
+ },
8067
+ },
8068
+ required: ["researchId"],
8069
+ },
8070
+ command: "ideas.get",
8071
+ },
8072
+ {
8073
+ name: "ideas_cancel",
8074
+ description: "Stop a running research.",
8075
+ inputSchema: {
8076
+ type: "object",
8077
+ properties: {
8078
+ researchId: { type: "string", description: "The research to stop." },
8079
+ },
8080
+ required: ["researchId"],
8081
+ },
8082
+ command: "ideas.cancel",
8083
+ },
8084
+ {
8085
+ name: "ideas_delete",
8086
+ description: "Forget a research.",
8087
+ inputSchema: {
8088
+ type: "object",
8089
+ properties: {
8090
+ researchId: { type: "string", description: "The research to delete." },
8091
+ },
8092
+ required: ["researchId"],
8093
+ },
8094
+ command: "ideas.delete",
8095
+ },
8096
+ {
8097
+ name: "ideas_channel",
8098
+ description:
8099
+ "One channel's baseline (median views of its recent uploads, hit rate, uploads a week, subscribers) and its uploads that beat it, best first. Use on a competitor, or on the user's own channel to see what already works for them. ~10 s.",
8100
+ inputSchema: {
8101
+ type: "object",
8102
+ properties: {
8103
+ channel: { type: "string", description: "@handle, channel URL or UC id." },
8104
+ format: { type: "string", description: "videos (long-form, default) | shorts." },
8105
+ minMultiplier: { type: "number", description: "Breakout threshold (default 1.5)." },
8106
+ limit: { type: "number", description: "Recent uploads to read, 10-100 (default 40)." },
8107
+ recent: {
8108
+ type: "boolean",
8109
+ description: "Also return every upload read, newest first (default false).",
8110
+ },
8111
+ },
8112
+ required: ["channel"],
8113
+ },
8114
+ command: "ideas.channel",
8115
+ },
8116
+ {
8117
+ name: "ideas_similar",
8118
+ description:
8119
+ "Channels competing for a channel's audience: searches the titles of its best uploads and ranks the channels that keep showing up (matches), each with its baseline (median views, hit rate, uploads a week, subscribers). ~30-60 s.",
8120
+ inputSchema: {
8121
+ type: "object",
8122
+ properties: {
8123
+ channel: { type: "string", description: "@handle, channel URL or UC id." },
8124
+ format: { type: "string", description: "videos (long-form, default) | shorts." },
8125
+ max: { type: "number", description: "Channels to return, 3-25 (default 12)." },
8126
+ },
8127
+ required: ["channel"],
8128
+ },
8129
+ command: "ideas.similar",
8130
+ },
8131
+ {
8132
+ name: "ideas_video",
8133
+ description:
8134
+ "A YouTube video's stats (views, likes, comment count, subscribers), description, tags and chapters. With baseline, also its multiplier against its channel's median: did it break out? With comments > 0, its top comments by likes: what viewers ask for and push back on.",
8135
+ inputSchema: {
8136
+ type: "object",
8137
+ properties: {
8138
+ video: { type: "string", description: "YouTube URL or 11-character id." },
8139
+ baseline: {
8140
+ type: "boolean",
8141
+ description:
8142
+ "Also read its channel's recent uploads and return multiplier and channelMedian. Adds ~5 s.",
8143
+ },
8144
+ comments: {
8145
+ type: "number",
8146
+ description: "Top comments to read, 0-500 (default 0). 100 takes ~10-20 s.",
8147
+ },
8148
+ },
8149
+ required: ["video"],
8150
+ },
8151
+ command: "ideas.video",
8152
+ },
8153
+ {
8154
+ name: "ideas_transcript",
8155
+ description:
8156
+ "A YouTube video's transcript from its captions (uploaded first, else automatic): to study an outlier's hook, structure and pacing.",
8157
+ inputSchema: {
8158
+ type: "object",
8159
+ properties: {
8160
+ video: { type: "string", description: "YouTube URL or 11-character id." },
8161
+ language: { type: "string", description: "Caption language code (default en)." },
8162
+ timestamps: {
8163
+ type: "boolean",
8164
+ description: "Return timed segments instead of plain text (default false).",
8165
+ },
8166
+ },
8167
+ required: ["video"],
8168
+ },
8169
+ command: "ideas.transcript",
8170
+ },
8171
+ {
8172
+ name: "ideas_keywords",
8173
+ description:
8174
+ "What people type into YouTube search around a seed phrase (autocomplete, expanded a-z and with question prefixes). Then ideas.demand on the promising ones.",
8175
+ inputSchema: {
8176
+ type: "object",
8177
+ properties: {
8178
+ seed: { type: "string", description: 'Seed phrase, e.g. "notion template".' },
8179
+ expand: {
8180
+ type: "boolean",
8181
+ description: "Expand with a-z and question prefixes (default true).",
8182
+ },
8183
+ },
8184
+ required: ["seed"],
8185
+ },
8186
+ command: "ideas.keywords",
8187
+ },
8188
+ {
8189
+ name: "ideas_demand",
8190
+ description:
8191
+ "How much a few YouTube search terms pull: for each, the median and top views of its top 20 results, the share under a year old, how many channels, and the 3 most-viewed. Pick the term worth a video after ideas.keywords. ~30 s for 10 terms.",
8192
+ inputSchema: {
8193
+ type: "object",
8194
+ properties: {
8195
+ keywords: {
8196
+ type: "array",
8197
+ items: { type: "string" },
8198
+ description: "Search terms to check (1-15).",
8199
+ },
8200
+ },
8201
+ required: ["keywords"],
8202
+ },
8203
+ command: "ideas.demand",
8204
+ },
8205
+ {
8206
+ name: "ideas_competitors",
8207
+ description:
8208
+ "The channels this workspace tracks, each with its baseline at the last scan and its uploads from the last 90 days at 2x or more (breakouts). Remembered across conversations; ideas.scan refreshes them.",
8209
+ inputSchema: {
8210
+ type: "object",
8211
+ properties: {},
8212
+ },
8213
+ command: "ideas.competitors",
8214
+ },
8215
+ {
8216
+ name: "ideas_track",
8217
+ description:
8218
+ "Start tracking a channel as a competitor (reads it once), or stop with tracked: false. Up to 40 per workspace.",
8219
+ inputSchema: {
8220
+ type: "object",
8221
+ properties: {
8222
+ channel: {
8223
+ type: "string",
8224
+ description: "@handle, channel URL or UC id (to stop: the UC id from ideas.competitors).",
8225
+ },
8226
+ tracked: { type: "boolean", description: "true (default) to track, false to stop." },
8227
+ format: { type: "string", description: "videos (long-form, default) | shorts." },
8228
+ },
8229
+ required: ["channel"],
8230
+ },
8231
+ command: "ideas.track",
8232
+ },
8233
+ {
8234
+ name: "ideas_scan",
8235
+ description:
8236
+ "Re-read every tracked competitor and return the breakouts across all of them (uploads from the last 90 days at 2x or more), best first. ~5-10 s per 5 channels.",
8237
+ inputSchema: {
8238
+ type: "object",
8239
+ properties: {
8240
+ limit: { type: "number", description: "Max breakouts returned, 1-100 (default 20)." },
8241
+ },
8242
+ },
8243
+ command: "ideas.scan",
8244
+ },
7499
8245
  {
7500
8246
  name: "export_generate_shots",
7501
8247
  description:
@@ -7902,14 +8648,14 @@ Read-only tools (project_read, transcript_get / _search / _find_issues, audio_pr
7902
8648
 
7903
8649
  Before an editing pass, read the skill's "Which tool for which moment" section (skill_read section="Which tool for which moment").
7904
8650
 
7905
- "Edit my video" / "make this a Short" with no specifics = this pipeline, in order. Confirm scope once ("full polish, or just part of it?"), then run it all:
8651
+ "Edit my video" / "make this a Short" with no specifics = this pipeline, in order. In PandaStudio's own chat: write it as a plan first (plan_propose: the steps below that apply, the scope question on it), end your turn, and run it when the user presses Start editing, with plan_update_step after each step. Other clients: confirm scope once ("full polish, or just part of it?"), then run it all:
7906
8652
  1. transcript_transcribe clips not yet transcribed (project_read clipStates).
7907
8653
  2. transcript_remove_fillers.
7908
8654
  3. Fix misheard names / brands with transcript_find_replace (transcript_insert_words for dropped words).
7909
8655
  4. transcript_find_issues, then transcript_delete_words on each duplicate take / false start (keep the LAST take; keep severity "low" ones).
7910
8656
  5. transcript_remove_silences.
7911
- 6. audio_enhance (Enhance voice) on clips not yet enhanced: strength natural; keepBackground true for vlogs / ambience; no echo (Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback)).
7912
- 7. The style, after steps 2-5 (they shift the timeline). The user's named style, recipe, creator or a saved memory wins. Otherwise call recipe_pick: it returns the recipes that fit this project's format and footage, each with pickWhen, plus defaultId. Read the transcript, choose the ONE candidate whose pickWhen matches what is said (defaultId when none clearly fits), do its "before" step if any, then recipe_apply_style + recipe_render and follow the prompt: the recipe decides the style (captions, pacing, kinds of moves, music, transitions). Graphics inside it are designed by you: a named template is the reference look; author your own (motion_render_html, same palette and type) when a beat needs something the template can't show. (An app without recipe_pick, 2.0.5 and older: recipe_list with format short or long, and choose by description and footage.) Tell the user which recipe and why in one line and name one or two alternatives. Only when no candidate exists or the user asked for a plain clean-up: captions (caption_toggle, template glowStack), a few motion graphics, emphasis zooms.
8657
+ 6. audio_enhance (Enhance voice) on clips not yet enhanced with its defaults (Light, plus Studio mic sound on Apple silicon; don't pass strength or restore unless the user asked); keepBackground true for vlogs / ambience; no echo (Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback)).
8658
+ 7. The style, after steps 2-5 (they shift the timeline). The user's named style, recipe, creator or a saved memory wins. Otherwise call recipe_pick: it returns the recipes that fit this project's format and footage, each with pickWhen, plus defaultId. Read the transcript, choose the ONE candidate whose pickWhen matches what is said (defaultId when none clearly fits), do its "before" step if any, then recipe_apply_style + recipe_render and follow the prompt: the recipe decides the style (captions, pacing, kinds of moves, music, transitions). Graphics inside it are designed by you: a named template is the reference look; author your own (motion_render_html, same palette and type) when a beat needs something the template can't show. (An app without recipe_pick, 2.0.5 and older: recipe_list with format short or long, and choose by description and footage.) Tell the user which recipe and why in one line and name one or two alternatives. Only when no candidate exists or the user asked for a plain clean-up: captions (caption_toggle, then caption_set_template: hormoziEmphasis for vertical, classic for long-form; not glowStack unless asked), a few motion graphics, emphasis zooms.
7913
8659
  8. B-roll, last: pick the moments yourself from what's said (any language), check them with project_check_broll, show the user your moments and ASK: their own images, generated on their image connector (only if one is connected; it uses their credits), or no B-roll. Never generate or invent images without their answer. Place what they give with project_add_broll. Nobody to ask: skip it and say so.
7914
8660
  9. llm_generate_title / description / timestamps, then preview_show and ask before export_start. Never export before the user has seen the preview.
7915
8661
  Report the real count each step returned. No music, intro/outro cards or FX overlays unless the recipe or the user asks for them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.251.0",
3
+ "version": "1.259.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",