@writepanda/mcp 1.251.0 → 1.262.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.
- package/bin/apps/clips.mjs +401 -0
- package/bin/apps/editPreview.mjs +389 -0
- package/bin/apps/index.mjs +242 -0
- package/bin/apps/viewKit.mjs +506 -0
- package/bin/server.mjs +854 -28
- package/package.json +1 -1
package/bin/server.mjs
CHANGED
|
@@ -35,7 +35,13 @@ import path from "node:path";
|
|
|
35
35
|
import process from "node:process";
|
|
36
36
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
37
37
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
CallToolRequestSchema,
|
|
40
|
+
ListResourcesRequestSchema,
|
|
41
|
+
ListResourceTemplatesRequestSchema,
|
|
42
|
+
ListToolsRequestSchema,
|
|
43
|
+
ReadResourceRequestSchema,
|
|
44
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
39
45
|
import {
|
|
40
46
|
appLaunchEnv,
|
|
41
47
|
callWithReconnect,
|
|
@@ -47,6 +53,14 @@ import {
|
|
|
47
53
|
IN_APP_UNAVAILABLE,
|
|
48
54
|
isInAppCaller,
|
|
49
55
|
} from "./appConnection.mjs";
|
|
56
|
+
import {
|
|
57
|
+
APP_TOOLS,
|
|
58
|
+
clientSupportsUi,
|
|
59
|
+
listUiResources,
|
|
60
|
+
MediaRegistry,
|
|
61
|
+
readUiResource,
|
|
62
|
+
toolUiMeta,
|
|
63
|
+
} from "./apps/index.mjs";
|
|
50
64
|
import { portableSchema } from "./portableSchema.mjs";
|
|
51
65
|
|
|
52
66
|
// ── Credentials + transport ───────────────────────────────────────────
|
|
@@ -1274,6 +1288,29 @@ const TOOLS = [
|
|
|
1274
1288
|
borderRadius: { type: "number" },
|
|
1275
1289
|
showBlur: { type: "boolean" },
|
|
1276
1290
|
motionBlurAmount: { type: "number" },
|
|
1291
|
+
wallpaperMotion: { type: "number", description: "mesh: background speed 0-2." },
|
|
1292
|
+
screenFrame: {
|
|
1293
|
+
type: "object",
|
|
1294
|
+
description: "Device frame for screen recordings.",
|
|
1295
|
+
properties: {
|
|
1296
|
+
kind: {
|
|
1297
|
+
type: "string",
|
|
1298
|
+
enum: ["browser", "window", "laptop", "phone", "tablet"],
|
|
1299
|
+
},
|
|
1300
|
+
theme: { type: "string", enum: ["light", "dark"] },
|
|
1301
|
+
url: { type: "string" },
|
|
1302
|
+
},
|
|
1303
|
+
},
|
|
1304
|
+
screenTilt: {
|
|
1305
|
+
type: "object",
|
|
1306
|
+
description: "3D tilt of screen recordings.",
|
|
1307
|
+
properties: {
|
|
1308
|
+
angle: { type: "string", enum: ["flat", "left", "right", "above"] },
|
|
1309
|
+
followCursor: { type: "boolean" },
|
|
1310
|
+
clickBounce: { type: "boolean" },
|
|
1311
|
+
strength: { type: "number", description: "0-1." },
|
|
1312
|
+
},
|
|
1313
|
+
},
|
|
1277
1314
|
},
|
|
1278
1315
|
},
|
|
1279
1316
|
graphicSound: {
|
|
@@ -1682,6 +1719,19 @@ const TOOLS = [
|
|
|
1682
1719
|
},
|
|
1683
1720
|
command: "project.show",
|
|
1684
1721
|
},
|
|
1722
|
+
{
|
|
1723
|
+
name: "project_edit_summary",
|
|
1724
|
+
description:
|
|
1725
|
+
"What the edit contains now: lengths (editedDurationMs, sourceDurationMs, removedMs cut or sped up), counts (cuts, zooms, motionElements, graphics, transitions, brollBeats, mediaOverlays, captions { on, template }, audio { narration, music, soundEffects }, ...) and highlights[] (plain lines). Cheap; use it to report an editing pass instead of project_read.",
|
|
1726
|
+
inputSchema: {
|
|
1727
|
+
type: "object",
|
|
1728
|
+
properties: {
|
|
1729
|
+
id: { type: "string" },
|
|
1730
|
+
path: { type: "string" },
|
|
1731
|
+
},
|
|
1732
|
+
},
|
|
1733
|
+
command: "project.edit-summary",
|
|
1734
|
+
},
|
|
1685
1735
|
{
|
|
1686
1736
|
name: "matte_status",
|
|
1687
1737
|
description:
|
|
@@ -2412,12 +2462,18 @@ const TOOLS = [
|
|
|
2412
2462
|
{
|
|
2413
2463
|
name: "podcast_draft_rundown",
|
|
2414
2464
|
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.",
|
|
2465
|
+
"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
2466
|
inputSchema: {
|
|
2417
2467
|
type: "object",
|
|
2418
2468
|
properties: {
|
|
2419
2469
|
brief: { type: "string" },
|
|
2420
2470
|
minutes: { type: "number", description: "Total show length (default 45)." },
|
|
2471
|
+
questions: {
|
|
2472
|
+
type: "array",
|
|
2473
|
+
items: { type: "string" },
|
|
2474
|
+
description:
|
|
2475
|
+
"The host's own questions, in order: kept word for word, each with a lower third.",
|
|
2476
|
+
},
|
|
2421
2477
|
guests: {
|
|
2422
2478
|
type: "array",
|
|
2423
2479
|
items: {
|
|
@@ -3016,7 +3072,7 @@ const TOOLS = [
|
|
|
3016
3072
|
type: "boolean",
|
|
3017
3073
|
description: "Follow the recorded cursor (screen recordings).",
|
|
3018
3074
|
},
|
|
3019
|
-
soundUrl: { type: "string", description: 'Default bundled:sound/swoosh-
|
|
3075
|
+
soundUrl: { type: "string", description: 'Default bundled:sound/motion-swoosh-up; "none".' },
|
|
3020
3076
|
soundVolume: { type: "number", description: "0-1." },
|
|
3021
3077
|
anchorSourceMs: ANCHOR_PROP,
|
|
3022
3078
|
anchorSourceEndMs: ANCHOR_END_PROP,
|
|
@@ -4708,7 +4764,7 @@ const TOOLS = [
|
|
|
4708
4764
|
{
|
|
4709
4765
|
name: "project_set_wallpaper",
|
|
4710
4766
|
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).",
|
|
4767
|
+
"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
4768
|
inputSchema: {
|
|
4713
4769
|
type: "object",
|
|
4714
4770
|
properties: {
|
|
@@ -4717,9 +4773,13 @@ const TOOLS = [
|
|
|
4717
4773
|
wallpaper: {
|
|
4718
4774
|
type: "string",
|
|
4719
4775
|
description:
|
|
4720
|
-
"Bundled id (e.g. gradient-blue), color, CSS gradient
|
|
4776
|
+
"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
4777
|
},
|
|
4722
4778
|
source: { type: "string", enum: ["desktop", "edges"] },
|
|
4779
|
+
motion: {
|
|
4780
|
+
type: "number",
|
|
4781
|
+
description: "Animated (mesh:) drift speed 0-2: 0 still, 1 default. Kept when omitted.",
|
|
4782
|
+
},
|
|
4723
4783
|
expectedRevision: { type: "number" },
|
|
4724
4784
|
},
|
|
4725
4785
|
},
|
|
@@ -4779,7 +4839,7 @@ const TOOLS = [
|
|
|
4779
4839
|
{
|
|
4780
4840
|
name: "project_set_style",
|
|
4781
4841
|
description:
|
|
4782
|
-
"Framing style, main-video frame (shape, border ring; drawn when the video is a card)
|
|
4842
|
+
"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
4843
|
inputSchema: {
|
|
4784
4844
|
type: "object",
|
|
4785
4845
|
properties: {
|
|
@@ -4803,6 +4863,27 @@ const TOOLS = [
|
|
|
4803
4863
|
borderWidth: { type: "number", description: "px 0-40." },
|
|
4804
4864
|
borderColor: { type: "string" },
|
|
4805
4865
|
resetFrame: { type: "boolean", description: "Clear shape + border first." },
|
|
4866
|
+
deviceFrame: {
|
|
4867
|
+
type: "string",
|
|
4868
|
+
enum: ["browser", "window", "laptop", "phone", "tablet", "none"],
|
|
4869
|
+
description: "Frame around the screen; the video shrinks to fit inside it.",
|
|
4870
|
+
},
|
|
4871
|
+
deviceFrameTheme: { type: "string", enum: ["light", "dark"] },
|
|
4872
|
+
deviceFrameUrl: {
|
|
4873
|
+
type: "string",
|
|
4874
|
+
description: "Browser address / window title; '' clears.",
|
|
4875
|
+
},
|
|
4876
|
+
tilt: {
|
|
4877
|
+
type: "string",
|
|
4878
|
+
enum: ["flat", "left", "right", "above"],
|
|
4879
|
+
description: "3D angle (left / right: that edge goes back; above: seen from above).",
|
|
4880
|
+
},
|
|
4881
|
+
tiltFollowCursor: {
|
|
4882
|
+
type: "boolean",
|
|
4883
|
+
description: "Lean toward the cursor (needs cursor data).",
|
|
4884
|
+
},
|
|
4885
|
+
tiltClickBounce: { type: "boolean", description: "Rock slightly on each click." },
|
|
4886
|
+
tiltStrength: { type: "number", description: "0-100, default 100." },
|
|
4806
4887
|
expectedRevision: { type: "number" },
|
|
4807
4888
|
},
|
|
4808
4889
|
},
|
|
@@ -4953,7 +5034,7 @@ const TOOLS = [
|
|
|
4953
5034
|
{
|
|
4954
5035
|
name: "project_align_cursor",
|
|
4955
5036
|
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.",
|
|
5037
|
+
"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
5038
|
inputSchema: {
|
|
4958
5039
|
type: "object",
|
|
4959
5040
|
properties: {
|
|
@@ -4966,7 +5047,7 @@ const TOOLS = [
|
|
|
4966
5047
|
points: {
|
|
4967
5048
|
type: "array",
|
|
4968
5049
|
description:
|
|
4969
|
-
"[{atMs (edited timeline ms) or sourceMs (clip's own ms), x 0-1, y 0-1}] where the real pointer is.",
|
|
5050
|
+
"[{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
5051
|
items: {
|
|
4971
5052
|
type: "object",
|
|
4972
5053
|
properties: {
|
|
@@ -5638,7 +5719,7 @@ const TOOLS = [
|
|
|
5638
5719
|
{
|
|
5639
5720
|
name: "audio_enhance",
|
|
5640
5721
|
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:
|
|
5722
|
+
"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
5723
|
inputSchema: {
|
|
5643
5724
|
type: "object",
|
|
5644
5725
|
properties: {
|
|
@@ -5652,7 +5733,7 @@ const TOOLS = [
|
|
|
5652
5733
|
strength: {
|
|
5653
5734
|
type: "string",
|
|
5654
5735
|
enum: ["light", "natural", "strong"],
|
|
5655
|
-
default: "
|
|
5736
|
+
default: "light",
|
|
5656
5737
|
},
|
|
5657
5738
|
keepBackground: {
|
|
5658
5739
|
type: "boolean",
|
|
@@ -5667,9 +5748,8 @@ const TOOLS = [
|
|
|
5667
5748
|
},
|
|
5668
5749
|
restore: {
|
|
5669
5750
|
type: "boolean",
|
|
5670
|
-
default: false,
|
|
5671
5751
|
description:
|
|
5672
|
-
"Studio mic sound
|
|
5752
|
+
"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
5753
|
},
|
|
5674
5754
|
reference: { type: "string", description: "Clip id or audio/video file to sound like." },
|
|
5675
5755
|
off: { type: "boolean", description: "Remove the enhancement (original audio)." },
|
|
@@ -5748,7 +5828,7 @@ const TOOLS = [
|
|
|
5748
5828
|
{
|
|
5749
5829
|
name: "caption_set_template",
|
|
5750
5830
|
description:
|
|
5751
|
-
"Pick a caption template (
|
|
5831
|
+
"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
5832
|
inputSchema: {
|
|
5753
5833
|
type: "object",
|
|
5754
5834
|
properties: {
|
|
@@ -6081,10 +6161,55 @@ const TOOLS = [
|
|
|
6081
6161
|
description:
|
|
6082
6162
|
"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
6163
|
},
|
|
6164
|
+
windowSize: {
|
|
6165
|
+
type: "string",
|
|
6166
|
+
enum: ["16:9", "16:10", "4:3", "1:1", "9:16", "21:9", "720p", "1080p", "1440p", "4K"],
|
|
6167
|
+
description:
|
|
6168
|
+
"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.",
|
|
6169
|
+
},
|
|
6170
|
+
centerWindow: {
|
|
6171
|
+
type: "boolean",
|
|
6172
|
+
description:
|
|
6173
|
+
"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.",
|
|
6174
|
+
},
|
|
6084
6175
|
},
|
|
6085
6176
|
},
|
|
6086
6177
|
command: "recording.start",
|
|
6087
6178
|
},
|
|
6179
|
+
{
|
|
6180
|
+
name: "recording_resize_window",
|
|
6181
|
+
description:
|
|
6182
|
+
"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 }.",
|
|
6183
|
+
inputSchema: {
|
|
6184
|
+
type: "object",
|
|
6185
|
+
properties: {
|
|
6186
|
+
source: { type: "string", description: "'window:N:0' from recording_list_sources." },
|
|
6187
|
+
size: {
|
|
6188
|
+
type: "string",
|
|
6189
|
+
enum: ["16:9", "16:10", "4:3", "1:1", "9:16", "21:9", "720p", "1080p", "1440p", "4K"],
|
|
6190
|
+
},
|
|
6191
|
+
},
|
|
6192
|
+
required: ["source", "size"],
|
|
6193
|
+
},
|
|
6194
|
+
command: "recording.resize-window",
|
|
6195
|
+
},
|
|
6196
|
+
{
|
|
6197
|
+
name: "recording_get_window_options",
|
|
6198
|
+
description: "Window-recording options: { centerWindow, supported }.",
|
|
6199
|
+
inputSchema: { type: "object", properties: {} },
|
|
6200
|
+
command: "recording.get-window-options",
|
|
6201
|
+
},
|
|
6202
|
+
{
|
|
6203
|
+
name: "recording_set_center_window",
|
|
6204
|
+
description:
|
|
6205
|
+
"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 }.",
|
|
6206
|
+
inputSchema: {
|
|
6207
|
+
type: "object",
|
|
6208
|
+
properties: { enabled: { type: "boolean" } },
|
|
6209
|
+
required: ["enabled"],
|
|
6210
|
+
},
|
|
6211
|
+
command: "recording.set-center-window",
|
|
6212
|
+
},
|
|
6088
6213
|
{
|
|
6089
6214
|
name: "recording_get_clean_desktop",
|
|
6090
6215
|
description: "Clean-desktop recording setting. Returns { cleanDesktop, supported }.",
|
|
@@ -6217,7 +6342,7 @@ const TOOLS = [
|
|
|
6217
6342
|
{
|
|
6218
6343
|
name: "project_compose_soundtrack",
|
|
6219
6344
|
description:
|
|
6220
|
-
"Score this edit: music
|
|
6345
|
+
"Score this edit: music composed FROM THE PROJECT'S OWN TIMELINE (zooms, graphics, pops, cuts, speech, stressed words) placed under the voice with ducking, plus sound effects as recorded library cues (group score-sfx, sparse under a voice); takes over zoom/overlay/graphic sounds; replaces the previous score and its cues on re-run. Call LAST, after cuts/zooms/graphics, and again after changing them (project_read composedScore.stale / doubledSounds). dryRun returns the score and sfxCues. Returns { audioPath, scorePath, events, heroMoments, acts, soundsTakenOver, sfxCues, paperSfx, replaced }.",
|
|
6221
6346
|
inputSchema: {
|
|
6222
6347
|
type: "object",
|
|
6223
6348
|
properties: {
|
|
@@ -6229,7 +6354,8 @@ const TOOLS = [
|
|
|
6229
6354
|
sfx: { type: "boolean", description: "Default true." },
|
|
6230
6355
|
useSpeech: {
|
|
6231
6356
|
type: "boolean",
|
|
6232
|
-
description:
|
|
6357
|
+
description:
|
|
6358
|
+
"Hits on the most stressed words even with nothing on screen. Default true for promo only.",
|
|
6233
6359
|
},
|
|
6234
6360
|
cues: {
|
|
6235
6361
|
type: "array",
|
|
@@ -6639,16 +6765,409 @@ const TOOLS = [
|
|
|
6639
6765
|
{
|
|
6640
6766
|
name: "memory_save",
|
|
6641
6767
|
description:
|
|
6642
|
-
"Save a durable
|
|
6768
|
+
"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
6769
|
inputSchema: {
|
|
6644
6770
|
type: "object",
|
|
6645
6771
|
properties: {
|
|
6646
6772
|
note: { type: "string", description: "One concise fact." },
|
|
6773
|
+
kind: {
|
|
6774
|
+
type: "string",
|
|
6775
|
+
enum: ["notes", "profile"],
|
|
6776
|
+
description: "notes (default): preferences. profile: who the creator is.",
|
|
6777
|
+
},
|
|
6647
6778
|
},
|
|
6648
6779
|
required: ["note"],
|
|
6649
6780
|
},
|
|
6650
6781
|
command: "memory.save",
|
|
6651
6782
|
},
|
|
6783
|
+
{
|
|
6784
|
+
name: "memory_replace",
|
|
6785
|
+
description: "Rewrite one memory entry by id: correct it, or merge two to stay under budget.",
|
|
6786
|
+
inputSchema: {
|
|
6787
|
+
type: "object",
|
|
6788
|
+
properties: {
|
|
6789
|
+
entryId: { type: "string", description: "Entry id from memory_list." },
|
|
6790
|
+
note: { type: "string", description: "The new text." },
|
|
6791
|
+
kind: {
|
|
6792
|
+
type: "string",
|
|
6793
|
+
enum: ["notes", "profile"],
|
|
6794
|
+
description: "notes (default): preferences. profile: who the creator is.",
|
|
6795
|
+
},
|
|
6796
|
+
},
|
|
6797
|
+
required: ["entryId", "note"],
|
|
6798
|
+
},
|
|
6799
|
+
command: "memory.replace",
|
|
6800
|
+
},
|
|
6801
|
+
{
|
|
6802
|
+
name: "memory_suggest",
|
|
6803
|
+
description:
|
|
6804
|
+
"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.",
|
|
6805
|
+
inputSchema: {
|
|
6806
|
+
type: "object",
|
|
6807
|
+
properties: {
|
|
6808
|
+
note: { type: "string", description: "The preference, worded as it would be saved." },
|
|
6809
|
+
reason: { type: "string", description: "One line on why you think so." },
|
|
6810
|
+
kind: {
|
|
6811
|
+
type: "string",
|
|
6812
|
+
enum: ["notes", "profile"],
|
|
6813
|
+
description: "notes (default): preferences. profile: who the creator is.",
|
|
6814
|
+
},
|
|
6815
|
+
},
|
|
6816
|
+
required: ["note"],
|
|
6817
|
+
},
|
|
6818
|
+
command: "memory.suggest",
|
|
6819
|
+
},
|
|
6820
|
+
{
|
|
6821
|
+
name: "chat_ask",
|
|
6822
|
+
description:
|
|
6823
|
+
"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.",
|
|
6824
|
+
inputSchema: {
|
|
6825
|
+
type: "object",
|
|
6826
|
+
properties: {
|
|
6827
|
+
question: { type: "string", description: "One short sentence." },
|
|
6828
|
+
options: {
|
|
6829
|
+
type: "array",
|
|
6830
|
+
description: "2-5 choices: strings, or { label, detail } with detail one short line.",
|
|
6831
|
+
items: {
|
|
6832
|
+
anyOf: [
|
|
6833
|
+
{ type: "string" },
|
|
6834
|
+
{
|
|
6835
|
+
type: "object",
|
|
6836
|
+
properties: { label: { type: "string" }, detail: { type: "string" } },
|
|
6837
|
+
required: ["label"],
|
|
6838
|
+
},
|
|
6839
|
+
],
|
|
6840
|
+
},
|
|
6841
|
+
},
|
|
6842
|
+
recommended: { type: "string", description: "Label of the choice you'd pick." },
|
|
6843
|
+
allowOther: { type: "boolean", description: "Also allow a typed answer." },
|
|
6844
|
+
},
|
|
6845
|
+
required: ["question", "options"],
|
|
6846
|
+
},
|
|
6847
|
+
command: "chat.ask",
|
|
6848
|
+
},
|
|
6849
|
+
{
|
|
6850
|
+
name: "motion_propose",
|
|
6851
|
+
description:
|
|
6852
|
+
"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.",
|
|
6853
|
+
inputSchema: {
|
|
6854
|
+
type: "object",
|
|
6855
|
+
properties: {
|
|
6856
|
+
id: { type: "string", description: "Project id" },
|
|
6857
|
+
path: { type: "string" },
|
|
6858
|
+
fromJob: { type: "string", description: "Finished render job (preferred)." },
|
|
6859
|
+
file: { type: "string", description: "Rendered MP4/WebM when there's no job." },
|
|
6860
|
+
atMs: { type: "number", description: "Edited timeline start." },
|
|
6861
|
+
durationMs: { type: "number", description: "Default: the render's length." },
|
|
6862
|
+
title: { type: "string", description: "What it shows, a few words." },
|
|
6863
|
+
reason: { type: "string", description: "Why at this moment." },
|
|
6864
|
+
},
|
|
6865
|
+
required: ["atMs", "title"],
|
|
6866
|
+
},
|
|
6867
|
+
command: "motion.propose",
|
|
6868
|
+
},
|
|
6869
|
+
{
|
|
6870
|
+
name: "post_plan",
|
|
6871
|
+
description:
|
|
6872
|
+
"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.",
|
|
6873
|
+
inputSchema: {
|
|
6874
|
+
type: "object",
|
|
6875
|
+
properties: {
|
|
6876
|
+
summary: { type: "string", description: "One line on the plan." },
|
|
6877
|
+
rows: {
|
|
6878
|
+
type: "array",
|
|
6879
|
+
items: {
|
|
6880
|
+
type: "object",
|
|
6881
|
+
properties: {
|
|
6882
|
+
exportId: { type: "string" },
|
|
6883
|
+
channelIds: { type: "array", items: { type: "string" } },
|
|
6884
|
+
networks: {
|
|
6885
|
+
type: "array",
|
|
6886
|
+
items: {
|
|
6887
|
+
type: "string",
|
|
6888
|
+
enum: ["instagram", "tiktok", "facebook", "linkedin", "x"],
|
|
6889
|
+
},
|
|
6890
|
+
},
|
|
6891
|
+
caption: { type: "string" },
|
|
6892
|
+
when: {
|
|
6893
|
+
type: "string",
|
|
6894
|
+
description: "now, or ISO with offset (e.g. 2026-11-03T18:30:00+00:00).",
|
|
6895
|
+
},
|
|
6896
|
+
},
|
|
6897
|
+
required: ["exportId", "caption"],
|
|
6898
|
+
},
|
|
6899
|
+
},
|
|
6900
|
+
},
|
|
6901
|
+
required: ["rows"],
|
|
6902
|
+
},
|
|
6903
|
+
command: "post.plan",
|
|
6904
|
+
},
|
|
6905
|
+
{
|
|
6906
|
+
name: "plan_propose",
|
|
6907
|
+
description:
|
|
6908
|
+
"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.",
|
|
6909
|
+
inputSchema: {
|
|
6910
|
+
type: "object",
|
|
6911
|
+
properties: {
|
|
6912
|
+
id: { type: "string", description: "Project id" },
|
|
6913
|
+
path: { type: "string", description: "Absolute .pandastudio path (alternative to id)" },
|
|
6914
|
+
summary: { type: "string", description: "One line: what the footage is and the angle." },
|
|
6915
|
+
steps: {
|
|
6916
|
+
type: "array",
|
|
6917
|
+
description: "Steps in order.",
|
|
6918
|
+
items: {
|
|
6919
|
+
type: "object",
|
|
6920
|
+
properties: {
|
|
6921
|
+
title: { type: "string" },
|
|
6922
|
+
stage: { type: "string", enum: ["prep", "edit", "clip", "post"] },
|
|
6923
|
+
detail: { type: "string" },
|
|
6924
|
+
needsApproval: { type: "boolean" },
|
|
6925
|
+
},
|
|
6926
|
+
required: ["title"],
|
|
6927
|
+
},
|
|
6928
|
+
},
|
|
6929
|
+
questions: {
|
|
6930
|
+
type: "array",
|
|
6931
|
+
description: "Decisions only the user can make (max 3).",
|
|
6932
|
+
items: {
|
|
6933
|
+
type: "object",
|
|
6934
|
+
properties: {
|
|
6935
|
+
text: { type: "string" },
|
|
6936
|
+
options: { type: "array", items: { type: "string" } },
|
|
6937
|
+
recommended: { type: "string" },
|
|
6938
|
+
},
|
|
6939
|
+
required: ["text", "options"],
|
|
6940
|
+
},
|
|
6941
|
+
},
|
|
6942
|
+
recipeId: { type: "string", description: "Recipe the plan follows (recipe_pick)." },
|
|
6943
|
+
source: { type: "string", enum: ["chat", "recording-finished", "routine"] },
|
|
6944
|
+
},
|
|
6945
|
+
required: ["summary", "steps"],
|
|
6946
|
+
},
|
|
6947
|
+
command: "plan.propose",
|
|
6948
|
+
},
|
|
6949
|
+
{
|
|
6950
|
+
name: "plan_get",
|
|
6951
|
+
description:
|
|
6952
|
+
"The project's open plan (proposed or running) with step statuses and the next step; plan: null when none.",
|
|
6953
|
+
inputSchema: {
|
|
6954
|
+
type: "object",
|
|
6955
|
+
properties: {
|
|
6956
|
+
id: { type: "string" },
|
|
6957
|
+
path: { type: "string" },
|
|
6958
|
+
includeHistory: {
|
|
6959
|
+
type: "boolean",
|
|
6960
|
+
description: "Also return finished and dismissed plans.",
|
|
6961
|
+
},
|
|
6962
|
+
},
|
|
6963
|
+
},
|
|
6964
|
+
command: "plan.get",
|
|
6965
|
+
},
|
|
6966
|
+
{
|
|
6967
|
+
name: "plan_answer",
|
|
6968
|
+
description:
|
|
6969
|
+
"Record the user's answer to a plan question (option id or label). Only before the plan starts.",
|
|
6970
|
+
inputSchema: {
|
|
6971
|
+
type: "object",
|
|
6972
|
+
properties: {
|
|
6973
|
+
id: { type: "string" },
|
|
6974
|
+
path: { type: "string" },
|
|
6975
|
+
planId: { type: "string" },
|
|
6976
|
+
questionId: { type: "string" },
|
|
6977
|
+
optionId: { type: "string" },
|
|
6978
|
+
},
|
|
6979
|
+
required: ["questionId", "optionId"],
|
|
6980
|
+
},
|
|
6981
|
+
command: "plan.answer",
|
|
6982
|
+
},
|
|
6983
|
+
{
|
|
6984
|
+
name: "plan_start",
|
|
6985
|
+
description:
|
|
6986
|
+
"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.",
|
|
6987
|
+
inputSchema: {
|
|
6988
|
+
type: "object",
|
|
6989
|
+
properties: { id: { type: "string" }, path: { type: "string" }, planId: { type: "string" } },
|
|
6990
|
+
},
|
|
6991
|
+
command: "plan.start",
|
|
6992
|
+
},
|
|
6993
|
+
{
|
|
6994
|
+
name: "plan_update_step",
|
|
6995
|
+
description:
|
|
6996
|
+
"Mark a step of the running plan: running, then done (result with the real counts), skipped or failed (say why).",
|
|
6997
|
+
inputSchema: {
|
|
6998
|
+
type: "object",
|
|
6999
|
+
properties: {
|
|
7000
|
+
id: { type: "string" },
|
|
7001
|
+
path: { type: "string" },
|
|
7002
|
+
planId: { type: "string" },
|
|
7003
|
+
stepId: { type: "string" },
|
|
7004
|
+
status: { type: "string", enum: ["running", "done", "skipped", "failed"] },
|
|
7005
|
+
result: { type: "string" },
|
|
7006
|
+
},
|
|
7007
|
+
required: ["stepId", "status"],
|
|
7008
|
+
},
|
|
7009
|
+
command: "plan.update-step",
|
|
7010
|
+
},
|
|
7011
|
+
{
|
|
7012
|
+
name: "plan_finish",
|
|
7013
|
+
description: "Close a plan: done (unreached steps become skipped) or dismissed.",
|
|
7014
|
+
inputSchema: {
|
|
7015
|
+
type: "object",
|
|
7016
|
+
properties: {
|
|
7017
|
+
id: { type: "string" },
|
|
7018
|
+
path: { type: "string" },
|
|
7019
|
+
planId: { type: "string" },
|
|
7020
|
+
status: { type: "string", enum: ["done", "dismissed"] },
|
|
7021
|
+
},
|
|
7022
|
+
required: ["status"],
|
|
7023
|
+
},
|
|
7024
|
+
command: "plan.finish",
|
|
7025
|
+
},
|
|
7026
|
+
{
|
|
7027
|
+
name: "routine_templates",
|
|
7028
|
+
description: "Ready-made routines with the exact settings for routine_propose.",
|
|
7029
|
+
inputSchema: { type: "object", properties: {} },
|
|
7030
|
+
command: "routine.templates",
|
|
7031
|
+
},
|
|
7032
|
+
{
|
|
7033
|
+
name: "routine_list",
|
|
7034
|
+
description:
|
|
7035
|
+
"This workspace's routines (proposed / on / paused), triggers in plain words, next run and recent runs with reports.",
|
|
7036
|
+
inputSchema: { type: "object", properties: {} },
|
|
7037
|
+
command: "routine.list",
|
|
7038
|
+
},
|
|
7039
|
+
{
|
|
7040
|
+
name: "routine_propose",
|
|
7041
|
+
description:
|
|
7042
|
+
"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.",
|
|
7043
|
+
inputSchema: {
|
|
7044
|
+
type: "object",
|
|
7045
|
+
properties: {
|
|
7046
|
+
name: { type: "string", description: "A few words, e.g. 'Monday competitor scan'." },
|
|
7047
|
+
prompt: {
|
|
7048
|
+
type: "string",
|
|
7049
|
+
description: "What the agent does each run (unattended: no questions, read-only shell).",
|
|
7050
|
+
},
|
|
7051
|
+
schedule: {
|
|
7052
|
+
type: "string",
|
|
7053
|
+
description:
|
|
7054
|
+
"'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.",
|
|
7055
|
+
},
|
|
7056
|
+
timezone: { type: "string", description: "IANA zone; default this Mac's." },
|
|
7057
|
+
event: {
|
|
7058
|
+
type: "string",
|
|
7059
|
+
enum: ["recording.finished", "import.finished", "podcast.ended", "export.finished"],
|
|
7060
|
+
description: "Run when this happens instead of on a schedule.",
|
|
7061
|
+
},
|
|
7062
|
+
precheck: {
|
|
7063
|
+
type: "object",
|
|
7064
|
+
description:
|
|
7065
|
+
"Free check before the AI turn; nothing found = run skipped. {kind:'ideas-breakouts',minMultiplier} | {kind:'new-exports'} | {kind:'connector-connected',connector}.",
|
|
7066
|
+
},
|
|
7067
|
+
autonomy: {
|
|
7068
|
+
type: "string",
|
|
7069
|
+
enum: ["suggest", "act"],
|
|
7070
|
+
description: "suggest (default): no edits. act: edits that need no approval.",
|
|
7071
|
+
},
|
|
7072
|
+
notify: { type: "string", enum: ["desktop", "none"] },
|
|
7073
|
+
template: { type: "string", description: "Start from a routine_templates id." },
|
|
7074
|
+
},
|
|
7075
|
+
required: ["name", "prompt"],
|
|
7076
|
+
},
|
|
7077
|
+
command: "routine.propose",
|
|
7078
|
+
},
|
|
7079
|
+
{
|
|
7080
|
+
name: "routine_update",
|
|
7081
|
+
description:
|
|
7082
|
+
"Change a routine. name/notify apply now; prompt, schedule/event, precheck or autonomy become a pending change the user approves on the card.",
|
|
7083
|
+
inputSchema: {
|
|
7084
|
+
type: "object",
|
|
7085
|
+
properties: {
|
|
7086
|
+
routineId: { type: "string" },
|
|
7087
|
+
name: { type: "string", description: "A few words, e.g. 'Monday competitor scan'." },
|
|
7088
|
+
prompt: {
|
|
7089
|
+
type: "string",
|
|
7090
|
+
description: "What the agent does each run (unattended: no questions, read-only shell).",
|
|
7091
|
+
},
|
|
7092
|
+
schedule: {
|
|
7093
|
+
type: "string",
|
|
7094
|
+
description:
|
|
7095
|
+
"'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.",
|
|
7096
|
+
},
|
|
7097
|
+
timezone: { type: "string", description: "IANA zone; default this Mac's." },
|
|
7098
|
+
event: {
|
|
7099
|
+
type: "string",
|
|
7100
|
+
enum: ["recording.finished", "import.finished", "podcast.ended", "export.finished"],
|
|
7101
|
+
description: "Run when this happens instead of on a schedule.",
|
|
7102
|
+
},
|
|
7103
|
+
precheck: {
|
|
7104
|
+
type: "object",
|
|
7105
|
+
description:
|
|
7106
|
+
"Free check before the AI turn; nothing found = run skipped. {kind:'ideas-breakouts',minMultiplier} | {kind:'new-exports'} | {kind:'connector-connected',connector}.",
|
|
7107
|
+
},
|
|
7108
|
+
autonomy: {
|
|
7109
|
+
type: "string",
|
|
7110
|
+
enum: ["suggest", "act"],
|
|
7111
|
+
description: "suggest (default): no edits. act: edits that need no approval.",
|
|
7112
|
+
},
|
|
7113
|
+
notify: { type: "string", enum: ["desktop", "none"] },
|
|
7114
|
+
},
|
|
7115
|
+
required: ["routineId"],
|
|
7116
|
+
},
|
|
7117
|
+
command: "routine.update",
|
|
7118
|
+
},
|
|
7119
|
+
{
|
|
7120
|
+
name: "routine_pause",
|
|
7121
|
+
description: "Pause a routine.",
|
|
7122
|
+
inputSchema: {
|
|
7123
|
+
type: "object",
|
|
7124
|
+
properties: { routineId: { type: "string" } },
|
|
7125
|
+
required: ["routineId"],
|
|
7126
|
+
},
|
|
7127
|
+
command: "routine.pause",
|
|
7128
|
+
},
|
|
7129
|
+
{
|
|
7130
|
+
name: "routine_resume",
|
|
7131
|
+
description: "Resume a paused routine (not a proposal).",
|
|
7132
|
+
inputSchema: {
|
|
7133
|
+
type: "object",
|
|
7134
|
+
properties: { routineId: { type: "string" } },
|
|
7135
|
+
required: ["routineId"],
|
|
7136
|
+
},
|
|
7137
|
+
command: "routine.resume",
|
|
7138
|
+
},
|
|
7139
|
+
{
|
|
7140
|
+
name: "routine_run",
|
|
7141
|
+
description:
|
|
7142
|
+
"Run a routine now in the background; the report lands in the Routines thread and routine_runs.",
|
|
7143
|
+
inputSchema: {
|
|
7144
|
+
type: "object",
|
|
7145
|
+
properties: { routineId: { type: "string" } },
|
|
7146
|
+
required: ["routineId"],
|
|
7147
|
+
},
|
|
7148
|
+
command: "routine.run",
|
|
7149
|
+
},
|
|
7150
|
+
{
|
|
7151
|
+
name: "routine_runs",
|
|
7152
|
+
description:
|
|
7153
|
+
"A routine's runs, newest first: done, silent, skipped, failed, missed, interrupted; with reports.",
|
|
7154
|
+
inputSchema: {
|
|
7155
|
+
type: "object",
|
|
7156
|
+
properties: { routineId: { type: "string" }, limit: { type: "number" } },
|
|
7157
|
+
required: ["routineId"],
|
|
7158
|
+
},
|
|
7159
|
+
command: "routine.runs",
|
|
7160
|
+
},
|
|
7161
|
+
{
|
|
7162
|
+
name: "routine_delete",
|
|
7163
|
+
description: "Delete a routine and its history (asks the user on a confirm card).",
|
|
7164
|
+
inputSchema: {
|
|
7165
|
+
type: "object",
|
|
7166
|
+
properties: { routineId: { type: "string" } },
|
|
7167
|
+
required: ["routineId"],
|
|
7168
|
+
},
|
|
7169
|
+
command: "routine.delete",
|
|
7170
|
+
},
|
|
6652
7171
|
{
|
|
6653
7172
|
name: "recipe_list",
|
|
6654
7173
|
description: "List edit recipes (proven styles): the user's saved ones first, then starters.",
|
|
@@ -6682,13 +7201,17 @@ const TOOLS = [
|
|
|
6682
7201
|
{
|
|
6683
7202
|
name: "recipe_pick",
|
|
6684
7203
|
description:
|
|
6685
|
-
"No style named ('edit my video', 'make this a Short')? The recipes that fit this project's format and footage, each with pickWhen, plus defaultId. Choose by what's said, then recipe_apply_style + recipe_render.",
|
|
7204
|
+
"No style named ('edit my video', 'make this a Short')? The recipes that fit this project's format and footage, each with pickWhen, plus defaultId. Choose by what's said (defaultId when nothing clearly fits, never a plain clean-up), then recipe_apply_style + recipe_render. A Short cut from a landscape long-form video (a project_fork_from_shot fork, a landscape source, or format short on a 16:9 project) comes back fromLongForm with its own default. On a 16:9 project the user wants as a Short, pass format short.",
|
|
6686
7205
|
inputSchema: {
|
|
6687
7206
|
type: "object",
|
|
6688
7207
|
properties: {
|
|
6689
7208
|
id: { type: "string", description: "Project id (default: the open project)." },
|
|
6690
7209
|
path: { type: "string" },
|
|
6691
|
-
format: {
|
|
7210
|
+
format: {
|
|
7211
|
+
type: "string",
|
|
7212
|
+
enum: ["short", "long"],
|
|
7213
|
+
description: "Default from the project's aspect ratio (9:16 = short).",
|
|
7214
|
+
},
|
|
6692
7215
|
footage: {
|
|
6693
7216
|
type: "string",
|
|
6694
7217
|
enum: ["camera", "screen", "podcast", "audio", "none"],
|
|
@@ -6791,6 +7314,11 @@ const TOOLS = [
|
|
|
6791
7314
|
type: "object",
|
|
6792
7315
|
properties: {
|
|
6793
7316
|
query: { type: "string", description: "Entry id, or text removing every match." },
|
|
7317
|
+
kind: {
|
|
7318
|
+
type: "string",
|
|
7319
|
+
enum: ["notes", "profile"],
|
|
7320
|
+
description: "notes (default): preferences. profile: who the creator is.",
|
|
7321
|
+
},
|
|
6794
7322
|
},
|
|
6795
7323
|
required: ["query"],
|
|
6796
7324
|
},
|
|
@@ -6798,8 +7326,17 @@ const TOOLS = [
|
|
|
6798
7326
|
},
|
|
6799
7327
|
{
|
|
6800
7328
|
name: "memory_list",
|
|
6801
|
-
description: "
|
|
6802
|
-
inputSchema: {
|
|
7329
|
+
description: "Both memory lists (notes, profile) with ids and budget use; kind for one list.",
|
|
7330
|
+
inputSchema: {
|
|
7331
|
+
type: "object",
|
|
7332
|
+
properties: {
|
|
7333
|
+
kind: {
|
|
7334
|
+
type: "string",
|
|
7335
|
+
enum: ["notes", "profile"],
|
|
7336
|
+
description: "notes (default): preferences. profile: who the creator is.",
|
|
7337
|
+
},
|
|
7338
|
+
},
|
|
7339
|
+
},
|
|
6803
7340
|
command: "memory.list",
|
|
6804
7341
|
},
|
|
6805
7342
|
{
|
|
@@ -7423,7 +7960,7 @@ const TOOLS = [
|
|
|
7423
7960
|
},
|
|
7424
7961
|
captionTemplate: {
|
|
7425
7962
|
type: "string",
|
|
7426
|
-
description: "Caption template id (default
|
|
7963
|
+
description: "Caption template id (default hormoziEmphasis), or none.",
|
|
7427
7964
|
},
|
|
7428
7965
|
},
|
|
7429
7966
|
required: ["file"],
|
|
@@ -7496,6 +8033,265 @@ const TOOLS = [
|
|
|
7496
8033
|
},
|
|
7497
8034
|
command: "clips.delete",
|
|
7498
8035
|
},
|
|
8036
|
+
{
|
|
8037
|
+
name: "clips_remove_clip",
|
|
8038
|
+
description:
|
|
8039
|
+
"Remove ONE clip from a clip set (the Clips screen's Remove on a clip card). Its stills go and its moment is remembered so clips.retry won't make it again; its project moves to the Trash (recoverable) unless keepProject is true. Exported files stay in My Exports. A clip still being made is dropped by the running job. Returns { clipSetId, removed, projectDeleted, clipsLeft }.",
|
|
8040
|
+
inputSchema: {
|
|
8041
|
+
type: "object",
|
|
8042
|
+
properties: {
|
|
8043
|
+
id: { type: "string", description: "Clip set id." },
|
|
8044
|
+
clipId: { type: "string", description: "The clip's id (from clips_get)." },
|
|
8045
|
+
keepProject: {
|
|
8046
|
+
type: "boolean",
|
|
8047
|
+
description: "Keep the clip's project in Projects instead of the Trash. Default false.",
|
|
8048
|
+
},
|
|
8049
|
+
},
|
|
8050
|
+
required: ["id", "clipId"],
|
|
8051
|
+
},
|
|
8052
|
+
command: "clips.remove-clip",
|
|
8053
|
+
},
|
|
8054
|
+
{
|
|
8055
|
+
name: "ideas_research",
|
|
8056
|
+
description:
|
|
8057
|
+
"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.",
|
|
8058
|
+
inputSchema: {
|
|
8059
|
+
type: "object",
|
|
8060
|
+
properties: {
|
|
8061
|
+
query: {
|
|
8062
|
+
type: "string",
|
|
8063
|
+
description: 'Topic(s), comma-separated: "ai video editing, descript alternatives".',
|
|
8064
|
+
},
|
|
8065
|
+
seedChannel: {
|
|
8066
|
+
type: "string",
|
|
8067
|
+
description:
|
|
8068
|
+
"A channel the target audience watches: @handle, channel URL or UC id. Give this, query, or both.",
|
|
8069
|
+
},
|
|
8070
|
+
format: { type: "string", description: "videos (long-form, default) | shorts." },
|
|
8071
|
+
minMultiplier: {
|
|
8072
|
+
type: "number",
|
|
8073
|
+
description:
|
|
8074
|
+
"Keep videos at or above this multiple of their channel's median (default 3).",
|
|
8075
|
+
},
|
|
8076
|
+
maxAgeDays: {
|
|
8077
|
+
type: "number",
|
|
8078
|
+
description: "Only outliers published in the last N days (default 365, 0 = any age).",
|
|
8079
|
+
},
|
|
8080
|
+
maxChannels: {
|
|
8081
|
+
type: "number",
|
|
8082
|
+
description: "Channels to read, 3-40 (default 15). More is slower and broader.",
|
|
8083
|
+
},
|
|
8084
|
+
period: {
|
|
8085
|
+
type: "string",
|
|
8086
|
+
description:
|
|
8087
|
+
"Limit the topic search to uploads from the last: any (default) | day | week | month | year.",
|
|
8088
|
+
},
|
|
8089
|
+
},
|
|
8090
|
+
},
|
|
8091
|
+
command: "ideas.research",
|
|
8092
|
+
},
|
|
8093
|
+
{
|
|
8094
|
+
name: "ideas_list",
|
|
8095
|
+
description:
|
|
8096
|
+
"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.",
|
|
8097
|
+
inputSchema: {
|
|
8098
|
+
type: "object",
|
|
8099
|
+
properties: {},
|
|
8100
|
+
},
|
|
8101
|
+
command: "ideas.list",
|
|
8102
|
+
},
|
|
8103
|
+
{
|
|
8104
|
+
name: "ideas_get",
|
|
8105
|
+
description:
|
|
8106
|
+
"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).",
|
|
8107
|
+
inputSchema: {
|
|
8108
|
+
type: "object",
|
|
8109
|
+
properties: {
|
|
8110
|
+
researchId: { type: "string", description: "From ideas.research or ideas.list." },
|
|
8111
|
+
limit: { type: "number", description: "Max outliers returned, 1-200 (default 25)." },
|
|
8112
|
+
offset: { type: "number", description: "Skip this many outliers (to page)." },
|
|
8113
|
+
onTopicOnly: {
|
|
8114
|
+
type: "boolean",
|
|
8115
|
+
description: "Only outliers whose titles mention the topic.",
|
|
8116
|
+
},
|
|
8117
|
+
},
|
|
8118
|
+
required: ["researchId"],
|
|
8119
|
+
},
|
|
8120
|
+
command: "ideas.get",
|
|
8121
|
+
},
|
|
8122
|
+
{
|
|
8123
|
+
name: "ideas_cancel",
|
|
8124
|
+
description: "Stop a running research.",
|
|
8125
|
+
inputSchema: {
|
|
8126
|
+
type: "object",
|
|
8127
|
+
properties: {
|
|
8128
|
+
researchId: { type: "string", description: "The research to stop." },
|
|
8129
|
+
},
|
|
8130
|
+
required: ["researchId"],
|
|
8131
|
+
},
|
|
8132
|
+
command: "ideas.cancel",
|
|
8133
|
+
},
|
|
8134
|
+
{
|
|
8135
|
+
name: "ideas_delete",
|
|
8136
|
+
description: "Forget a research.",
|
|
8137
|
+
inputSchema: {
|
|
8138
|
+
type: "object",
|
|
8139
|
+
properties: {
|
|
8140
|
+
researchId: { type: "string", description: "The research to delete." },
|
|
8141
|
+
},
|
|
8142
|
+
required: ["researchId"],
|
|
8143
|
+
},
|
|
8144
|
+
command: "ideas.delete",
|
|
8145
|
+
},
|
|
8146
|
+
{
|
|
8147
|
+
name: "ideas_channel",
|
|
8148
|
+
description:
|
|
8149
|
+
"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.",
|
|
8150
|
+
inputSchema: {
|
|
8151
|
+
type: "object",
|
|
8152
|
+
properties: {
|
|
8153
|
+
channel: { type: "string", description: "@handle, channel URL or UC id." },
|
|
8154
|
+
format: { type: "string", description: "videos (long-form, default) | shorts." },
|
|
8155
|
+
minMultiplier: { type: "number", description: "Breakout threshold (default 1.5)." },
|
|
8156
|
+
limit: { type: "number", description: "Recent uploads to read, 10-100 (default 40)." },
|
|
8157
|
+
recent: {
|
|
8158
|
+
type: "boolean",
|
|
8159
|
+
description: "Also return every upload read, newest first (default false).",
|
|
8160
|
+
},
|
|
8161
|
+
},
|
|
8162
|
+
required: ["channel"],
|
|
8163
|
+
},
|
|
8164
|
+
command: "ideas.channel",
|
|
8165
|
+
},
|
|
8166
|
+
{
|
|
8167
|
+
name: "ideas_similar",
|
|
8168
|
+
description:
|
|
8169
|
+
"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.",
|
|
8170
|
+
inputSchema: {
|
|
8171
|
+
type: "object",
|
|
8172
|
+
properties: {
|
|
8173
|
+
channel: { type: "string", description: "@handle, channel URL or UC id." },
|
|
8174
|
+
format: { type: "string", description: "videos (long-form, default) | shorts." },
|
|
8175
|
+
max: { type: "number", description: "Channels to return, 3-25 (default 12)." },
|
|
8176
|
+
},
|
|
8177
|
+
required: ["channel"],
|
|
8178
|
+
},
|
|
8179
|
+
command: "ideas.similar",
|
|
8180
|
+
},
|
|
8181
|
+
{
|
|
8182
|
+
name: "ideas_video",
|
|
8183
|
+
description:
|
|
8184
|
+
"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.",
|
|
8185
|
+
inputSchema: {
|
|
8186
|
+
type: "object",
|
|
8187
|
+
properties: {
|
|
8188
|
+
video: { type: "string", description: "YouTube URL or 11-character id." },
|
|
8189
|
+
baseline: {
|
|
8190
|
+
type: "boolean",
|
|
8191
|
+
description:
|
|
8192
|
+
"Also read its channel's recent uploads and return multiplier and channelMedian. Adds ~5 s.",
|
|
8193
|
+
},
|
|
8194
|
+
comments: {
|
|
8195
|
+
type: "number",
|
|
8196
|
+
description: "Top comments to read, 0-500 (default 0). 100 takes ~10-20 s.",
|
|
8197
|
+
},
|
|
8198
|
+
},
|
|
8199
|
+
required: ["video"],
|
|
8200
|
+
},
|
|
8201
|
+
command: "ideas.video",
|
|
8202
|
+
},
|
|
8203
|
+
{
|
|
8204
|
+
name: "ideas_transcript",
|
|
8205
|
+
description:
|
|
8206
|
+
"A YouTube video's transcript from its captions (uploaded first, else automatic): to study an outlier's hook, structure and pacing.",
|
|
8207
|
+
inputSchema: {
|
|
8208
|
+
type: "object",
|
|
8209
|
+
properties: {
|
|
8210
|
+
video: { type: "string", description: "YouTube URL or 11-character id." },
|
|
8211
|
+
language: { type: "string", description: "Caption language code (default en)." },
|
|
8212
|
+
timestamps: {
|
|
8213
|
+
type: "boolean",
|
|
8214
|
+
description: "Return timed segments instead of plain text (default false).",
|
|
8215
|
+
},
|
|
8216
|
+
},
|
|
8217
|
+
required: ["video"],
|
|
8218
|
+
},
|
|
8219
|
+
command: "ideas.transcript",
|
|
8220
|
+
},
|
|
8221
|
+
{
|
|
8222
|
+
name: "ideas_keywords",
|
|
8223
|
+
description:
|
|
8224
|
+
"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.",
|
|
8225
|
+
inputSchema: {
|
|
8226
|
+
type: "object",
|
|
8227
|
+
properties: {
|
|
8228
|
+
seed: { type: "string", description: 'Seed phrase, e.g. "notion template".' },
|
|
8229
|
+
expand: {
|
|
8230
|
+
type: "boolean",
|
|
8231
|
+
description: "Expand with a-z and question prefixes (default true).",
|
|
8232
|
+
},
|
|
8233
|
+
},
|
|
8234
|
+
required: ["seed"],
|
|
8235
|
+
},
|
|
8236
|
+
command: "ideas.keywords",
|
|
8237
|
+
},
|
|
8238
|
+
{
|
|
8239
|
+
name: "ideas_demand",
|
|
8240
|
+
description:
|
|
8241
|
+
"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.",
|
|
8242
|
+
inputSchema: {
|
|
8243
|
+
type: "object",
|
|
8244
|
+
properties: {
|
|
8245
|
+
keywords: {
|
|
8246
|
+
type: "array",
|
|
8247
|
+
items: { type: "string" },
|
|
8248
|
+
description: "Search terms to check (1-15).",
|
|
8249
|
+
},
|
|
8250
|
+
},
|
|
8251
|
+
required: ["keywords"],
|
|
8252
|
+
},
|
|
8253
|
+
command: "ideas.demand",
|
|
8254
|
+
},
|
|
8255
|
+
{
|
|
8256
|
+
name: "ideas_competitors",
|
|
8257
|
+
description:
|
|
8258
|
+
"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.",
|
|
8259
|
+
inputSchema: {
|
|
8260
|
+
type: "object",
|
|
8261
|
+
properties: {},
|
|
8262
|
+
},
|
|
8263
|
+
command: "ideas.competitors",
|
|
8264
|
+
},
|
|
8265
|
+
{
|
|
8266
|
+
name: "ideas_track",
|
|
8267
|
+
description:
|
|
8268
|
+
"Start tracking a channel as a competitor (reads it once), or stop with tracked: false. Up to 40 per workspace.",
|
|
8269
|
+
inputSchema: {
|
|
8270
|
+
type: "object",
|
|
8271
|
+
properties: {
|
|
8272
|
+
channel: {
|
|
8273
|
+
type: "string",
|
|
8274
|
+
description: "@handle, channel URL or UC id (to stop: the UC id from ideas.competitors).",
|
|
8275
|
+
},
|
|
8276
|
+
tracked: { type: "boolean", description: "true (default) to track, false to stop." },
|
|
8277
|
+
format: { type: "string", description: "videos (long-form, default) | shorts." },
|
|
8278
|
+
},
|
|
8279
|
+
required: ["channel"],
|
|
8280
|
+
},
|
|
8281
|
+
command: "ideas.track",
|
|
8282
|
+
},
|
|
8283
|
+
{
|
|
8284
|
+
name: "ideas_scan",
|
|
8285
|
+
description:
|
|
8286
|
+
"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.",
|
|
8287
|
+
inputSchema: {
|
|
8288
|
+
type: "object",
|
|
8289
|
+
properties: {
|
|
8290
|
+
limit: { type: "number", description: "Max breakouts returned, 1-100 (default 20)." },
|
|
8291
|
+
},
|
|
8292
|
+
},
|
|
8293
|
+
command: "ideas.scan",
|
|
8294
|
+
},
|
|
7499
8295
|
{
|
|
7500
8296
|
name: "export_generate_shots",
|
|
7501
8297
|
description:
|
|
@@ -7902,14 +8698,15 @@ Read-only tools (project_read, transcript_get / _search / _find_issues, audio_pr
|
|
|
7902
8698
|
|
|
7903
8699
|
Before an editing pass, read the skill's "Which tool for which moment" section (skill_read section="Which tool for which moment").
|
|
7904
8700
|
|
|
7905
|
-
"Edit my video" / "make this a Short" with no specifics = this pipeline, in order.
|
|
8701
|
+
"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
8702
|
1. transcript_transcribe clips not yet transcribed (project_read clipStates).
|
|
7907
8703
|
2. transcript_remove_fillers.
|
|
7908
8704
|
3. Fix misheard names / brands with transcript_find_replace (transcript_insert_words for dropped words).
|
|
7909
8705
|
4. transcript_find_issues, then transcript_delete_words on each duplicate take / false start (keep the LAST take; keep severity "low" ones).
|
|
7910
8706
|
5. transcript_remove_silences.
|
|
7911
|
-
|
|
7912
|
-
|
|
8707
|
+
5b. A Short from a landscape video (a long-form recording, the project isn't 9:16): cut the Short out now. export_generate_shots { projectId }, then project_fork_from_shot on the best shot (offer the top shots when the user is there) and run steps 6-9 on the fork. Only a clip already under about 90 s goes vertical in place (project_set_aspect_ratio 9:16).
|
|
8708
|
+
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)).
|
|
8709
|
+
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; a Short cut from a long-form video comes back fromLongForm and its defaultId is made for that), 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. Never trade the default for a plain edit because no candidate fits perfectly. 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
8710
|
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
8711
|
9. llm_generate_title / description / timestamps, then preview_show and ask before export_start. Never export before the user has seen the preview.
|
|
7915
8712
|
Report the real count each step returned. No music, intro/outro cards or FX overlays unless the recipe or the user asks for them.
|
|
@@ -7923,6 +8720,8 @@ Publishing has daily limits (YouTube uploads and updates per license, social pos
|
|
|
7923
8720
|
|
|
7924
8721
|
PandaStudio problems (a capability gap like an imported font the export can't draw, a command failing unexpectedly, a render not matching the project): call system_report_issue yourself, then tell the user you reported it. Not for user mistakes, missing keys or taste.
|
|
7925
8722
|
|
|
8723
|
+
Views: in chat apps that show MCP views, preview_show, project_render_frame / _render_sheet and project_edit_summary also show the frames and what the edit contains, and clips_get / clips_make show the clips as cards the user can open, export or remove. Summarise in a line instead of describing each frame or clip; what the user does in a view arrives as context, don't redo it.
|
|
8724
|
+
|
|
7926
8725
|
Review items: the user can pin notes and drawings on the preview and send them in one turn (listed in the editor context and the message, crops attached in item order). Apply each one, then review_resolve with its id. review_list shows the open ones with a crop image path.
|
|
7927
8726
|
|
|
7928
8727
|
Destination decides everything: infer it (Shorts/TikTok/vertical/<=90s: shorts; YouTube/tutorial/>8 min: youtube-long; LinkedIn; Loom/internal), else ask once.
|
|
@@ -7937,23 +8736,49 @@ const server = new Server(
|
|
|
7937
8736
|
{
|
|
7938
8737
|
capabilities: {
|
|
7939
8738
|
tools: {},
|
|
8739
|
+
// Only the ui:// views (MCP Apps), listed for clients that render them.
|
|
8740
|
+
resources: {},
|
|
7940
8741
|
},
|
|
7941
8742
|
instructions: INSTRUCTIONS,
|
|
7942
8743
|
},
|
|
7943
8744
|
);
|
|
7944
8745
|
|
|
8746
|
+
/** Whether the connected client renders MCP Apps views (apps/index.mjs). */
|
|
8747
|
+
const uiEnabled = () => clientSupportsUi(server.getClientCapabilities(), { inApp: IN_APP });
|
|
8748
|
+
/** Image paths the app reported this session, served to views by ui_read_media. */
|
|
8749
|
+
const media = new MediaRegistry();
|
|
8750
|
+
|
|
7945
8751
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
7946
|
-
|
|
7947
|
-
|
|
8752
|
+
const ui = uiEnabled();
|
|
8753
|
+
const tools = TOOLS.map((t) => {
|
|
8754
|
+
const meta = ui ? toolUiMeta(t.name) : undefined;
|
|
8755
|
+
return {
|
|
7948
8756
|
name: t.name,
|
|
7949
8757
|
description: t.description,
|
|
7950
8758
|
// Gemini rejects multi-type fields that JSON Schema allows.
|
|
7951
8759
|
inputSchema: portableSchema(t.inputSchema),
|
|
7952
|
-
|
|
7953
|
-
|
|
8760
|
+
...(meta ? { _meta: meta } : {}),
|
|
8761
|
+
};
|
|
8762
|
+
});
|
|
8763
|
+
return { tools: ui ? [...tools, ...APP_TOOLS] : tools };
|
|
8764
|
+
});
|
|
8765
|
+
|
|
8766
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
8767
|
+
resources: uiEnabled() ? listUiResources() : [],
|
|
8768
|
+
}));
|
|
8769
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
8770
|
+
resourceTemplates: [],
|
|
8771
|
+
}));
|
|
8772
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
8773
|
+
const res = readUiResource(request.params.uri, mcpVersion());
|
|
8774
|
+
if (!res) throw new Error(`unknown resource: ${request.params.uri}`);
|
|
8775
|
+
return res;
|
|
7954
8776
|
});
|
|
7955
8777
|
|
|
7956
8778
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
8779
|
+
if (request.params.name === "ui_read_media") {
|
|
8780
|
+
return media.read(request.params.arguments?.paths);
|
|
8781
|
+
}
|
|
7957
8782
|
const tool = TOOLS.find((t) => t.name === request.params.name);
|
|
7958
8783
|
if (!tool) {
|
|
7959
8784
|
return {
|
|
@@ -8007,6 +8832,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
8007
8832
|
];
|
|
8008
8833
|
try {
|
|
8009
8834
|
const data = result?.data ?? result;
|
|
8835
|
+
media.note(data);
|
|
8010
8836
|
const previews = collectInlinePreviewPaths(command, data);
|
|
8011
8837
|
for (const previewPath of previews) {
|
|
8012
8838
|
const fs = await import("node:fs/promises");
|