@instavar/mcp-server 0.5.0 → 0.6.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/README.md +39 -14
- package/dist/index.js +635 -316
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,283 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/paired-announcement.ts
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
// src/announcement-source.ts
|
|
7
|
+
var htmlEscape = (value) => value.replace(
|
|
8
|
+
/[&<>"']/g,
|
|
9
|
+
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]
|
|
10
|
+
);
|
|
11
|
+
function announcementFiles(project) {
|
|
12
|
+
const copy = project.copy;
|
|
13
|
+
const files = {};
|
|
14
|
+
const addFile = (name, contents) => {
|
|
15
|
+
files[name] = contents;
|
|
16
|
+
};
|
|
17
|
+
const css = `@font-face{font-family:AnnouncementDisplay;src:url(__ANNOUNCEMENT_DISPLAY_FONT__);font-weight:100 900;}
|
|
18
|
+
@font-face{font-family:AnnouncementBody;src:url(__ANNOUNCEMENT_BODY_FONT__);font-weight:400;}
|
|
19
|
+
*{box-sizing:border-box}body{margin:0;background:#f4f0e7;color:#202d29}
|
|
20
|
+
#announcement-root{position:relative;width:1280px;height:720px;overflow:hidden;background:#f4f0e7;color:#202d29}
|
|
21
|
+
.clip{position:absolute;inset:0}
|
|
22
|
+
h1,p{margin:0;overflow-wrap:anywhere}h1{position:absolute;left:88px;top:110px;width:1040px;font-family:AnnouncementDisplay;font-size:${copy.title.length > 45 ? 48 : copy.title.length > 24 ? 64 : 92}px;font-weight:650;line-height:1.08;letter-spacing:-3px}
|
|
23
|
+
#body{position:absolute;left:92px;top:370px;width:1050px;font-family:AnnouncementBody;font-size:${copy.body.length > 60 ? 28 : 34}px;font-weight:400;line-height:1.4}
|
|
24
|
+
#cta{position:absolute;left:92px;top:535px;width:1050px;font-family:AnnouncementBody;font-size:${copy.cta.length > 30 ? 26 : 30}px;font-weight:400;line-height:1.4;color:#345a45}
|
|
25
|
+
#rule{position:absolute;left:92px;top:620px;width:1096px;height:4px;background:#345a45;transform-origin:left center}
|
|
26
|
+
html,body,*{text-rendering:auto}
|
|
27
|
+
`;
|
|
28
|
+
addFile("style.css", css);
|
|
29
|
+
const manifest = {
|
|
30
|
+
framework: project.framework,
|
|
31
|
+
copy,
|
|
32
|
+
width: 1280,
|
|
33
|
+
height: 720,
|
|
34
|
+
fps: 30,
|
|
35
|
+
durationSeconds: 10
|
|
36
|
+
};
|
|
37
|
+
addFile("manifest.json", JSON.stringify(manifest, null, 2) + "\n");
|
|
38
|
+
if (project.framework === "hyperframes") {
|
|
39
|
+
addFile(
|
|
40
|
+
"hyperframes.json",
|
|
41
|
+
JSON.stringify({ authoringSkill: "general-video" })
|
|
42
|
+
);
|
|
43
|
+
addFile(
|
|
44
|
+
"index.html",
|
|
45
|
+
`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Instavar announcement card</title><link rel="stylesheet" href="style.css"><script src="gsap.min.js"></script></head><body>
|
|
46
|
+
<div id="announcement-root" data-composition-id="announcement" data-start="0" data-width="1280" data-height="720" data-duration="10">
|
|
47
|
+
<section id="announcement-scene" class="clip" data-start="0" data-duration="10" data-track-index="0">
|
|
48
|
+
<h1 id="title">${htmlEscape(copy.title)}</h1><p id="body">${htmlEscape(
|
|
49
|
+
copy.body
|
|
50
|
+
)}</p><p id="cta">${htmlEscape(
|
|
51
|
+
copy.cta
|
|
52
|
+
)}</p><div id="rule"></div></section></div>
|
|
53
|
+
<script>
|
|
54
|
+
// Adapted from HyperFrames registry staggered-fade-up: three whole text blocks.
|
|
55
|
+
const tl=gsap.timeline({paused:true});
|
|
56
|
+
tl.fromTo('#title',{opacity:0,y:22},{opacity:1,y:0,duration:0.6,ease:'power3.out'},0.25);
|
|
57
|
+
tl.fromTo('#body',{opacity:0,y:22},{opacity:1,y:0,duration:0.6,ease:'power3.out'},2);
|
|
58
|
+
tl.fromTo('#cta',{opacity:0,y:22},{opacity:1,y:0,duration:0.6,ease:'power3.out'},5);
|
|
59
|
+
tl.fromTo('#rule',{scaleX:0},{scaleX:1,duration:9.5,ease:'none'},0.25);
|
|
60
|
+
window.__timelines['announcement']=tl;
|
|
61
|
+
</script></body></html>`
|
|
62
|
+
);
|
|
63
|
+
addFile(
|
|
64
|
+
"index.motion.json",
|
|
65
|
+
JSON.stringify(
|
|
66
|
+
{
|
|
67
|
+
duration: 10,
|
|
68
|
+
assertions: [
|
|
69
|
+
{ kind: "appearsBy", selector: "#title", bySec: 1 },
|
|
70
|
+
{ kind: "before", a: "#title", b: "#body" },
|
|
71
|
+
{ kind: "before", a: "#body", b: "#cta" },
|
|
72
|
+
{ kind: "staysInFrame", selector: "#title" },
|
|
73
|
+
{ kind: "staysInFrame", selector: "#body" },
|
|
74
|
+
{ kind: "staysInFrame", selector: "#cta" }
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
null,
|
|
78
|
+
2
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
addFile(
|
|
83
|
+
"index.jsx",
|
|
84
|
+
`import React from 'react';
|
|
85
|
+
import {Composition,registerRoot,useCurrentFrame,useVideoConfig} from 'remotion';
|
|
86
|
+
const css=${JSON.stringify(css)};
|
|
87
|
+
const copy=${JSON.stringify(copy)};
|
|
88
|
+
const ease=t=>1-Math.pow(1-t,4);
|
|
89
|
+
const reveal=(t,start)=>{const p=ease(Math.max(0,Math.min(1,(t-start)/0.6)));return {opacity:p,transform:'translateY('+(22*(1-p))+'px)'}};
|
|
90
|
+
function Announcement(){const frame=useCurrentFrame();const {fps}=useVideoConfig();const t=frame/fps;return <div id="announcement-root"><style>{css}</style><section className="clip"><h1 id="title" style={reveal(t,0.25)}>{copy.title}</h1><p id="body" style={reveal(t,2)}>{copy.body}</p><p id="cta" style={reveal(t,5)}>{copy.cta}</p><div id="rule" style={{transform:'scaleX('+Math.max(0,Math.min(1,(t-0.25)/9.5))+')'}}/></section></div>}
|
|
91
|
+
registerRoot(()=> <Composition id="Announcement" component={Announcement} width={1280} height={720} fps={30} durationInFrames={300}/>);
|
|
92
|
+
`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const dependencies = project.framework === "hyperframes" ? { hyperframes: "0.8.38", gsap: "3.14.2" } : {
|
|
96
|
+
remotion: "4.0.496",
|
|
97
|
+
"@remotion/cli": "4.0.496",
|
|
98
|
+
react: "19.1.0",
|
|
99
|
+
"react-dom": "19.1.0"
|
|
100
|
+
};
|
|
101
|
+
addFile(
|
|
102
|
+
"package.json",
|
|
103
|
+
JSON.stringify(
|
|
104
|
+
{
|
|
105
|
+
private: true,
|
|
106
|
+
type: "module",
|
|
107
|
+
scripts: {
|
|
108
|
+
setup: "node setup.mjs",
|
|
109
|
+
preview: project.framework === "hyperframes" ? "hyperframes preview" : "remotion studio index.jsx",
|
|
110
|
+
render: project.framework === "hyperframes" ? "hyperframes render --fps 30 --output video.mp4" : "remotion render index.jsx Announcement video.mp4"
|
|
111
|
+
},
|
|
112
|
+
dependencies
|
|
113
|
+
},
|
|
114
|
+
null,
|
|
115
|
+
2
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
addFile(
|
|
119
|
+
"setup.mjs",
|
|
120
|
+
`import {readFile,writeFile,copyFile} from 'node:fs/promises';
|
|
121
|
+
const fonts = [['__ANNOUNCEMENT_DISPLAY_FONT__','Fraunces-Variable.ttf'],['__ANNOUNCEMENT_BODY_FONT__','Inter-Regular.ttf']];
|
|
122
|
+
for (const [marker,name] of fonts) {
|
|
123
|
+
const response = await fetch('https://instavar.com/fonts/'+name);
|
|
124
|
+
if (!response.ok) throw new Error('Font download failed: '+name);
|
|
125
|
+
const uri = 'data:font/ttf;base64,'+Buffer.from(await response.arrayBuffer()).toString('base64');
|
|
126
|
+
for (const file of ${JSON.stringify(
|
|
127
|
+
project.framework === "hyperframes" ? ["style.css"] : ["style.css", "index.jsx"]
|
|
128
|
+
)}) {
|
|
129
|
+
const source = await readFile(file,'utf8');
|
|
130
|
+
await writeFile(file,source.replaceAll(marker,uri));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
${project.framework === "hyperframes" ? "await copyFile('node_modules/gsap/dist/gsap.min.js','gsap.min.js');" : ""}
|
|
134
|
+
`
|
|
135
|
+
);
|
|
136
|
+
return files;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/paired-announcement.ts
|
|
140
|
+
var framework = z.enum(["remotion", "hyperframes"]);
|
|
141
|
+
var text = (max) => z.string().trim().min(1).max(max);
|
|
142
|
+
var copyFields = { title: text(70), body: text(100), cta: text(40) };
|
|
143
|
+
var copySchema = z.object(copyFields).strict();
|
|
144
|
+
var announcementProjectSchema = z.object({
|
|
145
|
+
templateId: z.literal("announcement-card-v1"),
|
|
146
|
+
framework,
|
|
147
|
+
revision: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER - 1),
|
|
148
|
+
copy: copySchema
|
|
149
|
+
}).strict();
|
|
150
|
+
var pairedAnnouncementInput = {
|
|
151
|
+
framework: z.enum(["remotion", "hyperframes", "no-preference"]).optional().describe(
|
|
152
|
+
"Only supply an explicit user choice. Omit for an ambiguous request or to inherit the existing project framework."
|
|
153
|
+
),
|
|
154
|
+
copy: copySchema.optional().describe("Accepted copy for a new project only."),
|
|
155
|
+
project: announcementProjectSchema.optional().describe(
|
|
156
|
+
"The latest project document returned by this tool. Caller-held, not a saved hosted project."
|
|
157
|
+
),
|
|
158
|
+
expectedRevision: z.number().int().min(1).optional().describe(
|
|
159
|
+
"Required with project. Must match its revision. This checks the supplied document, not a server-side stored version."
|
|
160
|
+
),
|
|
161
|
+
edits: copySchema.partial().optional().describe(
|
|
162
|
+
"Explicit copy edits to apply before switching. Other source or timeline edits cannot be preserved."
|
|
163
|
+
)
|
|
164
|
+
};
|
|
165
|
+
var inputSchema = z.object(pairedAnnouncementInput).strict();
|
|
166
|
+
var PAIRED_ANNOUNCEMENT_CAPABILITY = {
|
|
167
|
+
templateId: "announcement-card-v1",
|
|
168
|
+
frameworks: ["remotion", "hyperframes"],
|
|
169
|
+
tool: "prepare_announcement",
|
|
170
|
+
dimensions: { width: 1280, height: 720, fps: 30, durationSeconds: 10 },
|
|
171
|
+
scope: "One silent announcement card. Returns portable source and caller-held copy state; does not save or render. Only title, body and action survive switching."
|
|
172
|
+
};
|
|
173
|
+
var PAIRED_ANNOUNCEMENT_INSTRUCTIONS = "For the paired announcement-card-v1 or a HyperFrames announcement request, use prepare_announcement. Never infer a framework from creative wording. Omit framework unless the user explicitly chooses one or says no preference. If needs-selection, ask the user. Continue with the latest returned project and expectedRevision; apply edits through edits before switching. Keep the returned project document for later calls. Never describe this caller-held document as a saved hosted project. The existing VideoSpec catalogue remains Remotion-only.";
|
|
174
|
+
function prepareAnnouncement(raw) {
|
|
175
|
+
const input = inputSchema.parse(raw);
|
|
176
|
+
const inputReceipt = {
|
|
177
|
+
contract: "paired-announcement-input-receipt/v1",
|
|
178
|
+
stage: "validated-input",
|
|
179
|
+
normalizedInput: JSON.parse(JSON.stringify(input))
|
|
180
|
+
};
|
|
181
|
+
const old = input.project;
|
|
182
|
+
if (old && input.expectedRevision !== old.revision)
|
|
183
|
+
throw new Error(
|
|
184
|
+
"Supply the latest project document and its matching expectedRevision."
|
|
185
|
+
);
|
|
186
|
+
if (old && input.copy)
|
|
187
|
+
throw new Error(
|
|
188
|
+
"Use edits to change existing copy; do not replace it with new-project copy."
|
|
189
|
+
);
|
|
190
|
+
if (!old && (input.edits || input.expectedRevision !== void 0))
|
|
191
|
+
throw new Error("Edits and expectedRevision require an existing project.");
|
|
192
|
+
const selected = input.framework === "remotion" || input.framework === "hyperframes" ? input.framework : old?.framework ?? (input.framework === "no-preference" ? "remotion" : void 0);
|
|
193
|
+
if (!selected)
|
|
194
|
+
return {
|
|
195
|
+
inputReceipt,
|
|
196
|
+
status: "needs-selection",
|
|
197
|
+
options: ["remotion", "hyperframes", "no-preference"],
|
|
198
|
+
message: "Which framework would you like: Remotion, HyperFrames, or no preference? No source was generated."
|
|
199
|
+
};
|
|
200
|
+
if (!old && !input.copy)
|
|
201
|
+
return {
|
|
202
|
+
inputReceipt,
|
|
203
|
+
status: "needs-copy",
|
|
204
|
+
framework: selected,
|
|
205
|
+
required: ["title", "body", "cta"]
|
|
206
|
+
};
|
|
207
|
+
const copy = copySchema.parse({
|
|
208
|
+
...old?.copy ?? input.copy,
|
|
209
|
+
...input.edits
|
|
210
|
+
});
|
|
211
|
+
const changed = old && Object.keys(copyFields).some(
|
|
212
|
+
(key) => copy[key] !== old.copy[key]
|
|
213
|
+
);
|
|
214
|
+
const project = {
|
|
215
|
+
templateId: "announcement-card-v1",
|
|
216
|
+
framework: selected,
|
|
217
|
+
revision: (old?.revision ?? 1) + (changed ? 1 : 0),
|
|
218
|
+
copy
|
|
219
|
+
};
|
|
220
|
+
return {
|
|
221
|
+
inputReceipt,
|
|
222
|
+
status: "ready",
|
|
223
|
+
reason: input.framework && input.framework !== "no-preference" ? "explicit" : old ? "existing-project" : "visible-default",
|
|
224
|
+
project,
|
|
225
|
+
files: {
|
|
226
|
+
...announcementFiles(project),
|
|
227
|
+
"announcement.project.json": JSON.stringify(project, null, 2) + "\n"
|
|
228
|
+
},
|
|
229
|
+
commands: [
|
|
230
|
+
"npm install",
|
|
231
|
+
"npm run setup",
|
|
232
|
+
...selected === "hyperframes" ? ["npx hyperframes check --samples 15 --strict"] : [],
|
|
233
|
+
"npm run preview",
|
|
234
|
+
"npm run render"
|
|
235
|
+
],
|
|
236
|
+
instructions: "Save these files in a new directory. Install and setup download pinned dependencies and Instavar fonts; setup embeds fonts locally before rendering. Review preview frames before rendering. Keep announcement.project.json as the copy source of truth. This tool has not written files, saved a hosted project or rendered video. Regenerating source preserves only the three copy fields, not custom source edits."
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function registerPairedAnnouncement(server) {
|
|
240
|
+
server.registerTool(
|
|
241
|
+
"prepare_announcement",
|
|
242
|
+
{
|
|
243
|
+
title: "Prepare an announcement in Remotion or HyperFrames",
|
|
244
|
+
description: "Prepare one paired announcement-card-v1 template, continue its caller-held project, edit copy or switch frameworks. Returns source files for local rendering. " + PAIRED_ANNOUNCEMENT_INSTRUCTIONS,
|
|
245
|
+
inputSchema: pairedAnnouncementInput,
|
|
246
|
+
annotations: {
|
|
247
|
+
readOnlyHint: true,
|
|
248
|
+
destructiveHint: false,
|
|
249
|
+
openWorldHint: false
|
|
250
|
+
},
|
|
251
|
+
_meta: { securitySchemes: [{ type: "noauth" }] }
|
|
252
|
+
},
|
|
253
|
+
async (args) => {
|
|
254
|
+
try {
|
|
255
|
+
const result2 = prepareAnnouncement(args);
|
|
256
|
+
return {
|
|
257
|
+
structuredContent: result2,
|
|
258
|
+
content: [{ type: "text", text: JSON.stringify(result2) }]
|
|
259
|
+
};
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
isError: true,
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: error instanceof Error ? error.message : "Invalid announcement input"
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
3
275
|
// src/index.ts
|
|
4
276
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
277
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
278
|
|
|
7
279
|
// src/creative-guides.ts
|
|
8
|
-
import { z } from "zod";
|
|
280
|
+
import { z as z2 } from "zod";
|
|
9
281
|
|
|
10
282
|
// src/creative-guides.generated.ts
|
|
11
283
|
var CREATIVE_GUIDES = [
|
|
@@ -36,7 +308,7 @@ var CREATIVE_GUIDES = [
|
|
|
36
308
|
];
|
|
37
309
|
|
|
38
310
|
// src/creative-guides.ts
|
|
39
|
-
var creativeGuideIdSchema =
|
|
311
|
+
var creativeGuideIdSchema = z2.enum([
|
|
40
312
|
"visual-copy",
|
|
41
313
|
"carousel-stories",
|
|
42
314
|
"visual-design",
|
|
@@ -97,7 +369,7 @@ function registerCreativeGuides(server) {
|
|
|
97
369
|
}
|
|
98
370
|
|
|
99
371
|
// src/public-templates.ts
|
|
100
|
-
import { z as
|
|
372
|
+
import { z as z3 } from "zod";
|
|
101
373
|
var TEMPLATE_REPOSITORY = "https://github.com/instavar/remotion-templates";
|
|
102
374
|
var TEMPLATE_ENDPOINT = "https://instavar.com/api/mcp/templates";
|
|
103
375
|
var VOICE_STUDIO_URL = "https://instavar.com/voice";
|
|
@@ -108,8 +380,8 @@ var templateIds = [
|
|
|
108
380
|
"social-remix",
|
|
109
381
|
"finance-brief"
|
|
110
382
|
];
|
|
111
|
-
var templateIdSchema =
|
|
112
|
-
var aspectSchema =
|
|
383
|
+
var templateIdSchema = z3.enum(templateIds);
|
|
384
|
+
var aspectSchema = z3.enum(["9:16", "4:5", "1:1"]);
|
|
113
385
|
var templates = [
|
|
114
386
|
{
|
|
115
387
|
id: "qa-ad",
|
|
@@ -373,6 +645,7 @@ function scaffoldVideoSpec(args) {
|
|
|
373
645
|
}
|
|
374
646
|
function registerPublicTemplateTools(server) {
|
|
375
647
|
registerCreativeGuides(server);
|
|
648
|
+
registerPairedAnnouncement(server);
|
|
376
649
|
server.registerTool(
|
|
377
650
|
"get_capabilities",
|
|
378
651
|
{
|
|
@@ -388,6 +661,7 @@ function registerPublicTemplateTools(server) {
|
|
|
388
661
|
repository: TEMPLATE_REPOSITORY,
|
|
389
662
|
endpoint: TEMPLATE_ENDPOINT,
|
|
390
663
|
creativeGuides: CREATIVE_GUIDE_INDEX,
|
|
664
|
+
pairedAnnouncement: PAIRED_ANNOUNCEMENT_CAPABILITY,
|
|
391
665
|
localFeatures: [
|
|
392
666
|
"template discovery",
|
|
393
667
|
"VideoSpec schema and examples",
|
|
@@ -486,9 +760,9 @@ ${template?.description ?? ""}`
|
|
|
486
760
|
description: "Return a deterministic VideoSpec skeleton without writing files.",
|
|
487
761
|
inputSchema: {
|
|
488
762
|
templateId: templateIdSchema,
|
|
489
|
-
title:
|
|
763
|
+
title: z3.string().min(1).max(160),
|
|
490
764
|
aspect: aspectSchema.default("9:16"),
|
|
491
|
-
mediaSrc:
|
|
765
|
+
mediaSrc: z3.string().min(1).max(500).optional()
|
|
492
766
|
},
|
|
493
767
|
annotations
|
|
494
768
|
},
|
|
@@ -506,7 +780,7 @@ ${template?.description ?? ""}`
|
|
|
506
780
|
title: "Get local render instructions",
|
|
507
781
|
description: "Return clone, install, validate, preview, and render commands.",
|
|
508
782
|
inputSchema: {
|
|
509
|
-
client:
|
|
783
|
+
client: z3.enum(["codex", "claude-code", "manual"]).default("manual")
|
|
510
784
|
},
|
|
511
785
|
annotations
|
|
512
786
|
},
|
|
@@ -533,12 +807,12 @@ ${template?.description ?? ""}`
|
|
|
533
807
|
{
|
|
534
808
|
title: "Diagnose render error",
|
|
535
809
|
description: "Classify a local validation or render error.",
|
|
536
|
-
inputSchema: { errorText:
|
|
810
|
+
inputSchema: { errorText: z3.string().min(1).max(1e4) },
|
|
537
811
|
annotations
|
|
538
812
|
},
|
|
539
813
|
async ({ errorText }) => {
|
|
540
|
-
const
|
|
541
|
-
const code =
|
|
814
|
+
const text2 = errorText.toLowerCase();
|
|
815
|
+
const code = text2.includes("enoent") || text2.includes("not found") ? "missing-file" : text2.includes("ffmpeg") || text2.includes("codec") ? "media-codec" : text2.includes("schema") || text2.includes("invalid") || text2.includes("zod") ? "invalid-video-spec" : text2.includes("chrome") || text2.includes("browser") ? "browser-runtime" : "unknown-render-error";
|
|
542
816
|
return result(
|
|
543
817
|
{
|
|
544
818
|
code,
|
|
@@ -551,7 +825,7 @@ ${template?.description ?? ""}`
|
|
|
551
825
|
}
|
|
552
826
|
|
|
553
827
|
// src/tools.ts
|
|
554
|
-
import { z as
|
|
828
|
+
import { z as z6 } from "zod";
|
|
555
829
|
|
|
556
830
|
// src/http.ts
|
|
557
831
|
import { randomUUID } from "crypto";
|
|
@@ -597,11 +871,11 @@ function requestFailure(error) {
|
|
|
597
871
|
return error instanceof Error ? error.message : String(error);
|
|
598
872
|
}
|
|
599
873
|
async function parse(res) {
|
|
600
|
-
const
|
|
874
|
+
const text2 = await res.text();
|
|
601
875
|
try {
|
|
602
|
-
return JSON.parse(
|
|
876
|
+
return JSON.parse(text2);
|
|
603
877
|
} catch {
|
|
604
|
-
return
|
|
878
|
+
return text2;
|
|
605
879
|
}
|
|
606
880
|
}
|
|
607
881
|
async function apiGet(path) {
|
|
@@ -625,7 +899,9 @@ async function apiPost(path, payload) {
|
|
|
625
899
|
method: "POST",
|
|
626
900
|
headers: {
|
|
627
901
|
[API_KEY_HEADER]: getApiKey(),
|
|
628
|
-
"content-type": "application/json"
|
|
902
|
+
"content-type": "application/json",
|
|
903
|
+
// Labels person-consent declarations made through MCP (surface only).
|
|
904
|
+
"x-instavar-client": "mcp"
|
|
629
905
|
},
|
|
630
906
|
body: JSON.stringify(clean),
|
|
631
907
|
signal: AbortSignal.timeout(API_REQUEST_TIMEOUT_MS)
|
|
@@ -675,7 +951,7 @@ async function runTool(fn) {
|
|
|
675
951
|
}
|
|
676
952
|
|
|
677
953
|
// src/brief-schema.ts
|
|
678
|
-
import { z as
|
|
954
|
+
import { z as z4 } from "zod";
|
|
679
955
|
var PUBLISH_TARGETS = [
|
|
680
956
|
"linkedin",
|
|
681
957
|
"x",
|
|
@@ -707,16 +983,16 @@ var REFERENCE_ROLES = [
|
|
|
707
983
|
"environment",
|
|
708
984
|
"style"
|
|
709
985
|
];
|
|
710
|
-
var optionalDirectionText =
|
|
711
|
-
var visualDirectionSchema =
|
|
986
|
+
var optionalDirectionText = z4.string().trim().min(1).max(500).optional();
|
|
987
|
+
var visualDirectionSchema = z4.object({
|
|
712
988
|
communicationJob: optionalDirectionText,
|
|
713
989
|
emotionalTarget: optionalDirectionText,
|
|
714
|
-
attentionPath:
|
|
990
|
+
attentionPath: z4.object({
|
|
715
991
|
first: optionalDirectionText,
|
|
716
992
|
next: optionalDirectionText,
|
|
717
993
|
proof: optionalDirectionText
|
|
718
994
|
}).optional(),
|
|
719
|
-
visualLanguage:
|
|
995
|
+
visualLanguage: z4.object({
|
|
720
996
|
space: optionalDirectionText,
|
|
721
997
|
line: optionalDirectionText,
|
|
722
998
|
shape: optionalDirectionText,
|
|
@@ -725,24 +1001,24 @@ var visualDirectionSchema = z3.object({
|
|
|
725
1001
|
movement: optionalDirectionText,
|
|
726
1002
|
rhythm: optionalDirectionText
|
|
727
1003
|
}).optional(),
|
|
728
|
-
camera:
|
|
1004
|
+
camera: z4.object({
|
|
729
1005
|
shotSize: optionalDirectionText,
|
|
730
1006
|
angle: optionalDirectionText,
|
|
731
1007
|
movement: optionalDirectionText,
|
|
732
1008
|
lens: optionalDirectionText
|
|
733
1009
|
}).optional()
|
|
734
1010
|
});
|
|
735
|
-
var generationContractSchema =
|
|
736
|
-
globalInvariants:
|
|
737
|
-
shotDelta:
|
|
738
|
-
referenceBindings:
|
|
739
|
-
|
|
740
|
-
assetId:
|
|
741
|
-
roles:
|
|
1011
|
+
var generationContractSchema = z4.object({
|
|
1012
|
+
globalInvariants: z4.array(z4.string().trim().min(1).max(300)).max(20).optional(),
|
|
1013
|
+
shotDelta: z4.string().trim().min(1).max(500).optional(),
|
|
1014
|
+
referenceBindings: z4.array(
|
|
1015
|
+
z4.object({
|
|
1016
|
+
assetId: z4.string().trim().min(1).max(200),
|
|
1017
|
+
roles: z4.array(z4.enum(REFERENCE_ROLES)).min(1).max(7)
|
|
742
1018
|
})
|
|
743
1019
|
).max(20).optional(),
|
|
744
|
-
prohibitedChanges:
|
|
745
|
-
reviewCriteria:
|
|
1020
|
+
prohibitedChanges: z4.array(z4.string().trim().min(1).max(300)).max(20).optional(),
|
|
1021
|
+
reviewCriteria: z4.array(z4.string().trim().min(1).max(300)).max(20).optional()
|
|
746
1022
|
});
|
|
747
1023
|
var REGION_NAMES = [
|
|
748
1024
|
"background",
|
|
@@ -759,55 +1035,55 @@ var ASSET_ROLES = [
|
|
|
759
1035
|
"lower-third",
|
|
760
1036
|
"diagram-node"
|
|
761
1037
|
];
|
|
762
|
-
var assetSlotStyleSchema =
|
|
763
|
-
opacity:
|
|
764
|
-
fit:
|
|
765
|
-
z:
|
|
1038
|
+
var assetSlotStyleSchema = z4.object({
|
|
1039
|
+
opacity: z4.number().min(0).max(1).optional(),
|
|
1040
|
+
fit: z4.enum(["cover", "contain"]).optional(),
|
|
1041
|
+
z: z4.number().optional()
|
|
766
1042
|
}).strict();
|
|
767
|
-
var assetSlotSchema =
|
|
768
|
-
id:
|
|
769
|
-
sceneId:
|
|
770
|
-
region:
|
|
771
|
-
role:
|
|
772
|
-
kind:
|
|
773
|
-
source:
|
|
774
|
-
intent:
|
|
1043
|
+
var assetSlotSchema = z4.object({
|
|
1044
|
+
id: z4.string().min(1),
|
|
1045
|
+
sceneId: z4.string().min(1).optional(),
|
|
1046
|
+
region: z4.enum(REGION_NAMES),
|
|
1047
|
+
role: z4.enum(ASSET_ROLES),
|
|
1048
|
+
kind: z4.enum(["image", "video"]),
|
|
1049
|
+
source: z4.enum(["auto", "stock", "generate"]),
|
|
1050
|
+
intent: z4.string(),
|
|
775
1051
|
style: assetSlotStyleSchema.optional(),
|
|
776
|
-
timing:
|
|
777
|
-
fromPct:
|
|
778
|
-
toPct:
|
|
1052
|
+
timing: z4.object({
|
|
1053
|
+
fromPct: z4.number().min(0).max(1).optional(),
|
|
1054
|
+
toPct: z4.number().min(0).max(1).optional()
|
|
779
1055
|
}).strict().optional(),
|
|
780
|
-
url:
|
|
1056
|
+
url: z4.string().url().optional()
|
|
781
1057
|
}).strict();
|
|
782
|
-
var briefStepSchema =
|
|
783
|
-
label:
|
|
784
|
-
latex:
|
|
785
|
-
explanation:
|
|
1058
|
+
var briefStepSchema = z4.object({
|
|
1059
|
+
label: z4.string(),
|
|
1060
|
+
latex: z4.string().optional(),
|
|
1061
|
+
explanation: z4.string().optional()
|
|
786
1062
|
});
|
|
787
|
-
var briefAudioLayerSchema =
|
|
788
|
-
id:
|
|
789
|
-
src:
|
|
790
|
-
startFrame:
|
|
791
|
-
durationInFrames:
|
|
792
|
-
volume:
|
|
793
|
-
fadeFrames:
|
|
794
|
-
role:
|
|
1063
|
+
var briefAudioLayerSchema = z4.object({
|
|
1064
|
+
id: z4.string().min(1),
|
|
1065
|
+
src: z4.string().url(),
|
|
1066
|
+
startFrame: z4.number().int().nonnegative().default(0),
|
|
1067
|
+
durationInFrames: z4.number().int().positive().optional(),
|
|
1068
|
+
volume: z4.number().min(0).max(1).default(1),
|
|
1069
|
+
fadeFrames: z4.number().int().nonnegative().default(0),
|
|
1070
|
+
role: z4.enum(["music", "foley", "transition", "effect"])
|
|
795
1071
|
});
|
|
796
|
-
var protectedRegionEffectSchema =
|
|
797
|
-
id:
|
|
798
|
-
kind:
|
|
799
|
-
coordinateSpace:
|
|
800
|
-
region:
|
|
801
|
-
leftPercent:
|
|
802
|
-
topPercent:
|
|
803
|
-
widthPercent:
|
|
804
|
-
heightPercent:
|
|
1072
|
+
var protectedRegionEffectSchema = z4.object({
|
|
1073
|
+
id: z4.string().min(1).max(80),
|
|
1074
|
+
kind: z4.literal("border-glint"),
|
|
1075
|
+
coordinateSpace: z4.literal("output-frame-v1").default("output-frame-v1"),
|
|
1076
|
+
region: z4.object({
|
|
1077
|
+
leftPercent: z4.number().min(0).max(100),
|
|
1078
|
+
topPercent: z4.number().min(0).max(100),
|
|
1079
|
+
widthPercent: z4.number().positive().max(100),
|
|
1080
|
+
heightPercent: z4.number().positive().max(100)
|
|
805
1081
|
}),
|
|
806
|
-
startFrame:
|
|
807
|
-
endFrame:
|
|
808
|
-
speedUpFrame:
|
|
809
|
-
color:
|
|
810
|
-
thicknessPercent:
|
|
1082
|
+
startFrame: z4.number().int().nonnegative().default(0),
|
|
1083
|
+
endFrame: z4.number().int().positive(),
|
|
1084
|
+
speedUpFrame: z4.number().int().nonnegative().optional(),
|
|
1085
|
+
color: z4.string().max(64).default("#ffffff"),
|
|
1086
|
+
thicknessPercent: z4.number().positive().max(20).default(3)
|
|
811
1087
|
}).superRefine((effect2, context) => {
|
|
812
1088
|
if (effect2.region.leftPercent + effect2.region.widthPercent > 100) {
|
|
813
1089
|
context.addIssue({
|
|
@@ -838,13 +1114,13 @@ var protectedRegionEffectSchema = z3.object({
|
|
|
838
1114
|
});
|
|
839
1115
|
}
|
|
840
1116
|
});
|
|
841
|
-
var beatVisualRequestSchema =
|
|
842
|
-
intent:
|
|
843
|
-
media:
|
|
844
|
-
source:
|
|
1117
|
+
var beatVisualRequestSchema = z4.object({
|
|
1118
|
+
intent: z4.string().trim().min(1).optional(),
|
|
1119
|
+
media: z4.enum(["still", "motion"]).default("still"),
|
|
1120
|
+
source: z4.enum(["auto", "stock", "generate"]).default("auto")
|
|
845
1121
|
}).strict();
|
|
846
|
-
var briefSceneSchema =
|
|
847
|
-
type:
|
|
1122
|
+
var briefSceneSchema = z4.object({
|
|
1123
|
+
type: z4.enum([
|
|
848
1124
|
"hero",
|
|
849
1125
|
"points",
|
|
850
1126
|
"equation",
|
|
@@ -867,156 +1143,188 @@ var briefSceneSchema = z3.object({
|
|
|
867
1143
|
"image-window",
|
|
868
1144
|
"custom"
|
|
869
1145
|
]),
|
|
870
|
-
|
|
871
|
-
|
|
1146
|
+
layoutPreset: z4.literal("dark-cafe-announcement").optional().describe(
|
|
1147
|
+
"Built-in portrait coffee illustration with title, subtitle body and text CTA. Use only on a custom scene after the user selects this design. No generated assets."
|
|
1148
|
+
),
|
|
1149
|
+
durationInFrames: z4.number().optional(),
|
|
1150
|
+
narrationText: z4.string().optional(),
|
|
872
1151
|
visualResolution: beatVisualRequestSchema.optional(),
|
|
873
|
-
title:
|
|
874
|
-
subtitle:
|
|
875
|
-
text:
|
|
876
|
-
points:
|
|
877
|
-
latex:
|
|
878
|
-
value:
|
|
879
|
-
max:
|
|
880
|
-
label:
|
|
881
|
-
unit:
|
|
882
|
-
prefix:
|
|
883
|
-
suffix:
|
|
884
|
-
decimals:
|
|
885
|
-
color:
|
|
886
|
-
tone:
|
|
887
|
-
iconKey:
|
|
888
|
-
stepNumber:
|
|
889
|
-
totalSteps:
|
|
890
|
-
items:
|
|
891
|
-
src:
|
|
892
|
-
caption:
|
|
1152
|
+
title: z4.string().optional(),
|
|
1153
|
+
subtitle: z4.string().optional(),
|
|
1154
|
+
text: z4.string().optional(),
|
|
1155
|
+
points: z4.array(z4.string()).optional(),
|
|
1156
|
+
latex: z4.string().optional(),
|
|
1157
|
+
value: z4.union([z4.string(), z4.number()]).optional(),
|
|
1158
|
+
max: z4.number().optional(),
|
|
1159
|
+
label: z4.string().optional(),
|
|
1160
|
+
unit: z4.string().optional(),
|
|
1161
|
+
prefix: z4.string().optional(),
|
|
1162
|
+
suffix: z4.string().optional(),
|
|
1163
|
+
decimals: z4.number().optional(),
|
|
1164
|
+
color: z4.string().optional(),
|
|
1165
|
+
tone: z4.string().optional(),
|
|
1166
|
+
iconKey: z4.string().optional(),
|
|
1167
|
+
stepNumber: z4.number().optional(),
|
|
1168
|
+
totalSteps: z4.number().optional(),
|
|
1169
|
+
items: z4.array(z4.object({ label: z4.string(), value: z4.number() })).optional(),
|
|
1170
|
+
src: z4.string().optional(),
|
|
1171
|
+
caption: z4.string().optional(),
|
|
893
1172
|
// A-roll generation (video-window scenes): source "generate" triggers
|
|
894
1173
|
// flag-gated WaveSpeed image/video gen server-side from generatePrompt;
|
|
895
1174
|
// "url" (default) uses src as-is. Kept in sync with the app's
|
|
896
1175
|
// briefSceneSchema - see the parity test in the app suite.
|
|
897
|
-
source:
|
|
898
|
-
generatePrompt:
|
|
1176
|
+
source: z4.enum(["url", "generate"]).optional(),
|
|
1177
|
+
generatePrompt: z4.string().optional(),
|
|
899
1178
|
generationContract: generationContractSchema.optional(),
|
|
900
|
-
trimStartSeconds:
|
|
901
|
-
trimEndSeconds:
|
|
902
|
-
playbackRate:
|
|
903
|
-
objectFit:
|
|
904
|
-
objectPosition:
|
|
905
|
-
scaleFrom:
|
|
906
|
-
scaleTo:
|
|
907
|
-
panFromX:
|
|
908
|
-
panToX:
|
|
909
|
-
panFromY:
|
|
910
|
-
panToY:
|
|
911
|
-
routeFrom:
|
|
912
|
-
routeTo:
|
|
913
|
-
routeLabel:
|
|
914
|
-
overlayKind:
|
|
915
|
-
overlayPosition:
|
|
916
|
-
accentColor:
|
|
917
|
-
overlayBackgroundColor:
|
|
918
|
-
cutFlash:
|
|
919
|
-
muted:
|
|
920
|
-
protectedRegionEffects:
|
|
921
|
-
flashAtFrame:
|
|
922
|
-
flashDurationFrames:
|
|
1179
|
+
trimStartSeconds: z4.number().nonnegative().optional(),
|
|
1180
|
+
trimEndSeconds: z4.number().positive().optional(),
|
|
1181
|
+
playbackRate: z4.number().min(0.25).max(8).optional(),
|
|
1182
|
+
objectFit: z4.enum(["cover", "contain"]).optional(),
|
|
1183
|
+
objectPosition: z4.string().optional(),
|
|
1184
|
+
scaleFrom: z4.number().min(0.5).max(2).optional(),
|
|
1185
|
+
scaleTo: z4.number().min(0.5).max(2).optional(),
|
|
1186
|
+
panFromX: z4.number().min(-50).max(50).optional(),
|
|
1187
|
+
panToX: z4.number().min(-50).max(50).optional(),
|
|
1188
|
+
panFromY: z4.number().min(-50).max(50).optional(),
|
|
1189
|
+
panToY: z4.number().min(-50).max(50).optional(),
|
|
1190
|
+
routeFrom: z4.string().optional(),
|
|
1191
|
+
routeTo: z4.string().optional(),
|
|
1192
|
+
routeLabel: z4.string().optional(),
|
|
1193
|
+
overlayKind: z4.enum(["none", "hook", "stage", "end-card"]).optional(),
|
|
1194
|
+
overlayPosition: z4.enum(["center", "top", "bottom"]).optional(),
|
|
1195
|
+
accentColor: z4.string().optional(),
|
|
1196
|
+
overlayBackgroundColor: z4.string().optional(),
|
|
1197
|
+
cutFlash: z4.boolean().optional(),
|
|
1198
|
+
muted: z4.boolean().optional(),
|
|
1199
|
+
protectedRegionEffects: z4.array(protectedRegionEffectSchema).max(4).optional(),
|
|
1200
|
+
flashAtFrame: z4.number().int().nonnegative().optional(),
|
|
1201
|
+
flashDurationFrames: z4.number().int().positive().max(15).optional()
|
|
923
1202
|
});
|
|
924
|
-
var directionsBriefSchema =
|
|
925
|
-
subtitle:
|
|
926
|
-
unitLabel:
|
|
927
|
-
distanceTimeChip:
|
|
928
|
-
clips:
|
|
929
|
-
|
|
930
|
-
src:
|
|
931
|
-
trimStartSeconds:
|
|
932
|
-
trimEndSeconds:
|
|
933
|
-
speed:
|
|
934
|
-
mirrorFlip:
|
|
1203
|
+
var directionsBriefSchema = z4.object({
|
|
1204
|
+
subtitle: z4.string().optional(),
|
|
1205
|
+
unitLabel: z4.string().optional(),
|
|
1206
|
+
distanceTimeChip: z4.string().optional(),
|
|
1207
|
+
clips: z4.array(
|
|
1208
|
+
z4.object({
|
|
1209
|
+
src: z4.string(),
|
|
1210
|
+
trimStartSeconds: z4.number().optional(),
|
|
1211
|
+
trimEndSeconds: z4.number(),
|
|
1212
|
+
speed: z4.number().optional(),
|
|
1213
|
+
mirrorFlip: z4.boolean().optional()
|
|
935
1214
|
})
|
|
936
1215
|
).min(1),
|
|
937
|
-
cues:
|
|
938
|
-
|
|
939
|
-
atSeconds:
|
|
940
|
-
text:
|
|
941
|
-
arrow:
|
|
942
|
-
step:
|
|
1216
|
+
cues: z4.array(
|
|
1217
|
+
z4.object({
|
|
1218
|
+
atSeconds: z4.number(),
|
|
1219
|
+
text: z4.string(),
|
|
1220
|
+
arrow: z4.enum(["left", "right", "forward"]).optional(),
|
|
1221
|
+
step: z4.number().optional()
|
|
943
1222
|
})
|
|
944
1223
|
),
|
|
945
|
-
outro:
|
|
946
|
-
heading:
|
|
947
|
-
body:
|
|
948
|
-
seconds:
|
|
1224
|
+
outro: z4.object({
|
|
1225
|
+
heading: z4.string().optional(),
|
|
1226
|
+
body: z4.array(z4.string()).optional(),
|
|
1227
|
+
seconds: z4.number().optional()
|
|
949
1228
|
}).optional(),
|
|
950
|
-
music:
|
|
951
|
-
perSegment:
|
|
952
|
-
|
|
953
|
-
src:
|
|
954
|
-
clipIndexStart:
|
|
955
|
-
hotOffsetSeconds:
|
|
956
|
-
volume:
|
|
1229
|
+
music: z4.object({
|
|
1230
|
+
perSegment: z4.array(
|
|
1231
|
+
z4.object({
|
|
1232
|
+
src: z4.string(),
|
|
1233
|
+
clipIndexStart: z4.number().optional(),
|
|
1234
|
+
hotOffsetSeconds: z4.number().optional(),
|
|
1235
|
+
volume: z4.number().optional()
|
|
957
1236
|
})
|
|
958
1237
|
).optional(),
|
|
959
|
-
crossfadeSeconds:
|
|
1238
|
+
crossfadeSeconds: z4.number().optional()
|
|
960
1239
|
}).optional()
|
|
961
1240
|
});
|
|
1241
|
+
var PERSON_CONSENT_WORDING_VERSION = "2026-10-19.1";
|
|
1242
|
+
var PERSON_CONSENT_WORDING = "I have permission from everyone shown, and no one shown is under 18 unless I am their parent or guardian.";
|
|
1243
|
+
var personConsentSchema = z4.object({
|
|
1244
|
+
confirmed: z4.literal(true).describe("Must be true: the caller confirms the declaration below."),
|
|
1245
|
+
wordingVersion: z4.string().describe(
|
|
1246
|
+
`Version of the declaration the caller confirmed. Current: ${PERSON_CONSENT_WORDING_VERSION}.`
|
|
1247
|
+
)
|
|
1248
|
+
}).strict().describe(
|
|
1249
|
+
`Required when the brief uses an uploaded image or video. Ask the user to confirm: "${PERSON_CONSENT_WORDING}"`
|
|
1250
|
+
);
|
|
1251
|
+
var LIKENESS_WARNING_WORDING_VERSION = "2026-10-19.1";
|
|
1252
|
+
var LIKENESS_WARNING_WORDING = "This photo seems to show someone other than you. Only continue if you have their permission.";
|
|
1253
|
+
var LIKENESS_MINOR_WARNING_WORDING_VERSION = "2026-10-19.1";
|
|
1254
|
+
var LIKENESS_MINOR_WARNING_WORDING = "This may show someone under 18. Continue only if you are their parent or guardian, or have their parent or guardian's permission.";
|
|
1255
|
+
var likenessAcknowledgementSchema = z4.object({
|
|
1256
|
+
confirmed: z4.literal(true).describe("Must be true: the user accepted the warning below."),
|
|
1257
|
+
kind: z4.enum(["other_person", "guardian"]).optional().describe(
|
|
1258
|
+
`Which warning was accepted. "other_person" (default): "${LIKENESS_WARNING_WORDING}" "guardian": "${LIKENESS_MINOR_WARNING_WORDING}"`
|
|
1259
|
+
),
|
|
1260
|
+
wordingVersion: z4.string().describe(
|
|
1261
|
+
`Version of the warning accepted. Current: other_person ${LIKENESS_WARNING_WORDING_VERSION}, guardian ${LIKENESS_MINOR_WARNING_WORDING_VERSION}.`
|
|
1262
|
+
)
|
|
1263
|
+
}).strict().describe(
|
|
1264
|
+
"Optional. Send only after the user has seen and accepted the warning. Without it, a job that animates an uploaded photo of someone other than the account holder stops with likeness_acknowledgement_required, or likeness_guardian_confirmation_required when the person may be under 18."
|
|
1265
|
+
);
|
|
962
1266
|
var createBriefShape = {
|
|
963
|
-
title:
|
|
964
|
-
script:
|
|
965
|
-
caption:
|
|
966
|
-
publishTarget:
|
|
967
|
-
publishTargets:
|
|
968
|
-
objective:
|
|
969
|
-
targetDurationSeconds:
|
|
970
|
-
narrationMode:
|
|
971
|
-
lessonTitle:
|
|
1267
|
+
title: z4.string().min(1).max(120).describe("Short title (max 120 chars)"),
|
|
1268
|
+
script: z4.string().min(1).describe("Full narration script (sent to TTS)"),
|
|
1269
|
+
caption: z4.string().min(1).describe("Social caption with hooks, hashtags, CTA"),
|
|
1270
|
+
publishTarget: z4.enum(PUBLISH_TARGETS).describe("Target social platform"),
|
|
1271
|
+
publishTargets: z4.array(z4.enum(PUBLISH_TARGETS)).max(9).optional().describe("All allowed social destinations for this video"),
|
|
1272
|
+
objective: z4.enum(BRIEF_OBJECTIVES).describe("Content objective (drives composition routing)"),
|
|
1273
|
+
targetDurationSeconds: z4.number().int().min(5).max(600).optional().describe("Requested final video duration in seconds"),
|
|
1274
|
+
narrationMode: z4.enum(["tts", "off"]).optional().describe("Disable synthesized narration for source-audio or silent work"),
|
|
1275
|
+
lessonTitle: z4.string().optional().describe("Display title rendered inside the video"),
|
|
972
1276
|
visualDirection: visualDirectionSchema.optional().describe(
|
|
973
1277
|
"Whole-video communication, attention, visual-language, and camera direction."
|
|
974
1278
|
),
|
|
975
|
-
steps:
|
|
976
|
-
scenes:
|
|
1279
|
+
steps: z4.array(briefStepSchema).optional().describe("Structured steps for educational content (LaTeX)"),
|
|
1280
|
+
scenes: z4.array(briefSceneSchema).optional().describe("Scene graph for the UniversalVideo composition"),
|
|
977
1281
|
// No zod .default() here: a default would materialize into the parsed tool
|
|
978
1282
|
// arguments and relay to /api/v1/jobs as if the caller stated it, corrupting
|
|
979
1283
|
// the resolution provenance the API echoes back. Unset must stay absent;
|
|
980
1284
|
// the API re-applies 9:16 server-side.
|
|
981
|
-
aspectRatio:
|
|
982
|
-
compositionId:
|
|
983
|
-
voiceId:
|
|
1285
|
+
aspectRatio: z4.enum(ASPECT_RATIOS).optional().describe("Aspect ratio (defaults to 9:16 when omitted)"),
|
|
1286
|
+
compositionId: z4.string().optional().describe("Remotion composition ID override (advanced)"),
|
|
1287
|
+
voiceId: z4.string().optional().describe(
|
|
984
1288
|
"TTS narrator voice ID. Unknown IDs are rejected before rendering; when omitted, the org's saved voice preference or the system default is used and reported in the response's resolution block."
|
|
985
1289
|
),
|
|
986
1290
|
directions: directionsBriefSchema.optional().describe(
|
|
987
1291
|
"DirectionsWalkthrough 'find-us' payload: source clips (legs), decision-point cues on the realtime walk timeline, outro, and optional music. Set compositionId 'DirectionsWalkthrough' (or objective 'directions')."
|
|
988
1292
|
),
|
|
989
|
-
assetSlots:
|
|
1293
|
+
assetSlots: z4.array(assetSlotSchema).optional().describe(
|
|
990
1294
|
"Optional storyboard asset slots: region-placed image/video assets or placeholders attached to the whole video or a specific scene."
|
|
991
1295
|
),
|
|
992
|
-
audioLayers:
|
|
1296
|
+
audioLayers: z4.array(briefAudioLayerSchema).optional().describe("Optional typed music, Foley, transition, and effect layers."),
|
|
1297
|
+
personConsent: personConsentSchema.optional(),
|
|
1298
|
+
likenessAcknowledgement: likenessAcknowledgementSchema.optional()
|
|
993
1299
|
};
|
|
994
1300
|
var editBriefShape = {
|
|
995
|
-
jobId:
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1301
|
+
jobId: z4.string().uuid().describe("The job to edit"),
|
|
1302
|
+
personConsent: personConsentSchema.optional(),
|
|
1303
|
+
likenessAcknowledgement: likenessAcknowledgementSchema.optional(),
|
|
1304
|
+
title: z4.string().optional().describe("Updated title"),
|
|
1305
|
+
script: z4.string().optional().describe("Updated narration script"),
|
|
1306
|
+
caption: z4.string().optional().describe("Updated social caption"),
|
|
1307
|
+
lessonTitle: z4.string().optional().describe("Updated lesson title"),
|
|
1308
|
+
narrationMode: z4.enum(["tts", "off"]).optional().describe("Updated synthesized narration mode"),
|
|
1001
1309
|
visualDirection: visualDirectionSchema.optional().describe("Updated whole-video visual direction"),
|
|
1002
|
-
steps:
|
|
1003
|
-
scenes:
|
|
1004
|
-
aspectRatio:
|
|
1005
|
-
objective:
|
|
1310
|
+
steps: z4.array(briefStepSchema).optional().describe("Updated steps (replaces the array)"),
|
|
1311
|
+
scenes: z4.array(briefSceneSchema).optional().describe("Updated scenes (replaces the array)"),
|
|
1312
|
+
aspectRatio: z4.enum(ASPECT_RATIOS).optional().describe("Updated aspect ratio"),
|
|
1313
|
+
objective: z4.enum(BRIEF_OBJECTIVES).optional().describe(
|
|
1006
1314
|
"Updated content objective. Re-routes the composition and triggers a re-render."
|
|
1007
1315
|
),
|
|
1008
|
-
publishTarget:
|
|
1316
|
+
publishTarget: z4.enum(PUBLISH_TARGETS).optional().describe(
|
|
1009
1317
|
"Updated target social platform. Distribution metadata only - does not trigger a re-render."
|
|
1010
1318
|
),
|
|
1011
|
-
publishTargets:
|
|
1012
|
-
targetDurationSeconds:
|
|
1013
|
-
voiceId:
|
|
1319
|
+
publishTargets: z4.array(z4.enum(PUBLISH_TARGETS)).max(9).optional().describe("Replacement allowed social destination list"),
|
|
1320
|
+
targetDurationSeconds: z4.number().int().min(5).max(600).optional().describe("Updated final video duration in seconds"),
|
|
1321
|
+
voiceId: z4.string().optional().describe(
|
|
1014
1322
|
"Updated TTS narrator voice ID. Triggers a re-render with the new voice."
|
|
1015
1323
|
),
|
|
1016
|
-
assetSlots:
|
|
1324
|
+
assetSlots: z4.array(assetSlotSchema).optional().describe(
|
|
1017
1325
|
"Replacement storyboard asset slots. Omit to preserve existing slots; pass [] to clear them."
|
|
1018
1326
|
),
|
|
1019
|
-
audioLayers:
|
|
1327
|
+
audioLayers: z4.array(briefAudioLayerSchema).optional().describe(
|
|
1020
1328
|
"Replacement typed audio layers. Omit to preserve existing layers; pass [] to clear them."
|
|
1021
1329
|
)
|
|
1022
1330
|
};
|
|
@@ -1124,165 +1432,165 @@ function buildRenderDiagnosticSnapshot(params) {
|
|
|
1124
1432
|
}
|
|
1125
1433
|
|
|
1126
1434
|
// src/design-action-schema.ts
|
|
1127
|
-
import { z as
|
|
1128
|
-
var id =
|
|
1129
|
-
var point =
|
|
1130
|
-
var polygonShape =
|
|
1131
|
-
kind:
|
|
1132
|
-
points:
|
|
1435
|
+
import { z as z5 } from "zod";
|
|
1436
|
+
var id = z5.string().min(1).max(120);
|
|
1437
|
+
var point = z5.object({ x: z5.number().min(0).max(1), y: z5.number().min(0).max(1) }).strict();
|
|
1438
|
+
var polygonShape = z5.object({
|
|
1439
|
+
kind: z5.literal("polygon"),
|
|
1440
|
+
points: z5.array(point).min(3).max(512)
|
|
1133
1441
|
}).strict();
|
|
1134
|
-
var rasterShape =
|
|
1135
|
-
kind:
|
|
1136
|
-
source:
|
|
1442
|
+
var rasterShape = z5.object({
|
|
1443
|
+
kind: z5.literal("raster"),
|
|
1444
|
+
source: z5.object({
|
|
1137
1445
|
assetId: id,
|
|
1138
|
-
expectedSha256:
|
|
1139
|
-
sourceRange:
|
|
1140
|
-
startFrame:
|
|
1141
|
-
endFrame:
|
|
1446
|
+
expectedSha256: z5.string().regex(/^[a-f0-9]{64}$/),
|
|
1447
|
+
sourceRange: z5.object({
|
|
1448
|
+
startFrame: z5.number().int().nonnegative(),
|
|
1449
|
+
endFrame: z5.number().int().positive()
|
|
1142
1450
|
}).strict().optional()
|
|
1143
1451
|
}).strict(),
|
|
1144
|
-
widthPx:
|
|
1145
|
-
heightPx:
|
|
1452
|
+
widthPx: z5.number().int().positive().max(8192),
|
|
1453
|
+
heightPx: z5.number().int().positive().max(8192)
|
|
1146
1454
|
}).strict();
|
|
1147
|
-
var shape =
|
|
1148
|
-
var mask =
|
|
1455
|
+
var shape = z5.discriminatedUnion("kind", [polygonShape, rasterShape]);
|
|
1456
|
+
var mask = z5.object({
|
|
1149
1457
|
id,
|
|
1150
1458
|
shape,
|
|
1151
|
-
operation:
|
|
1152
|
-
inverted:
|
|
1153
|
-
feather:
|
|
1154
|
-
allowedSupportMargin:
|
|
1155
|
-
reviewState:
|
|
1156
|
-
provenance:
|
|
1157
|
-
kind:
|
|
1459
|
+
operation: z5.enum(["add", "subtract"]),
|
|
1460
|
+
inverted: z5.boolean().default(false),
|
|
1461
|
+
feather: z5.number().finite().nonnegative().max(0.25).default(0),
|
|
1462
|
+
allowedSupportMargin: z5.number().finite().nonnegative().max(0.25).default(0),
|
|
1463
|
+
reviewState: z5.enum(["draft", "approved", "rejected"]),
|
|
1464
|
+
provenance: z5.object({
|
|
1465
|
+
kind: z5.enum(["manual", "imported", "model-proposed"]),
|
|
1158
1466
|
sourceAssetId: id.optional(),
|
|
1159
|
-
modelId:
|
|
1467
|
+
modelId: z5.string().min(1).max(160).optional()
|
|
1160
1468
|
}).strict()
|
|
1161
1469
|
}).strict();
|
|
1162
|
-
var transform =
|
|
1163
|
-
x:
|
|
1164
|
-
y:
|
|
1165
|
-
width:
|
|
1166
|
-
height:
|
|
1167
|
-
rotationDegrees:
|
|
1168
|
-
opacity:
|
|
1470
|
+
var transform = z5.object({
|
|
1471
|
+
x: z5.number().min(0).max(1),
|
|
1472
|
+
y: z5.number().min(0).max(1),
|
|
1473
|
+
width: z5.number().positive().max(1),
|
|
1474
|
+
height: z5.number().positive().max(1),
|
|
1475
|
+
rotationDegrees: z5.number().min(-360).max(360),
|
|
1476
|
+
opacity: z5.number().min(0).max(1)
|
|
1169
1477
|
}).strict();
|
|
1170
1478
|
var colourParameters = {
|
|
1171
|
-
exposure:
|
|
1172
|
-
contrast:
|
|
1173
|
-
saturation:
|
|
1174
|
-
temperature:
|
|
1175
|
-
tint:
|
|
1479
|
+
exposure: z5.number().finite().min(-2).max(2).default(0),
|
|
1480
|
+
contrast: z5.number().finite().min(0.5).max(2).default(1),
|
|
1481
|
+
saturation: z5.number().finite().min(0).max(2).default(1),
|
|
1482
|
+
temperature: z5.number().finite().min(-1).max(1).default(0),
|
|
1483
|
+
tint: z5.number().finite().min(-1).max(1).default(0)
|
|
1176
1484
|
};
|
|
1177
|
-
var effect =
|
|
1485
|
+
var effect = z5.object({
|
|
1178
1486
|
id,
|
|
1179
|
-
kind:
|
|
1180
|
-
version:
|
|
1487
|
+
kind: z5.literal("local-colour"),
|
|
1488
|
+
version: z5.literal(1),
|
|
1181
1489
|
maskId: id,
|
|
1182
1490
|
...colourParameters
|
|
1183
1491
|
}).strict();
|
|
1184
|
-
var motionPath =
|
|
1492
|
+
var motionPath = z5.object({
|
|
1185
1493
|
id,
|
|
1186
|
-
kind:
|
|
1187
|
-
startFrame:
|
|
1188
|
-
endFrame:
|
|
1494
|
+
kind: z5.literal("cubic-bezier"),
|
|
1495
|
+
startFrame: z5.number().int().nonnegative(),
|
|
1496
|
+
endFrame: z5.number().int().positive(),
|
|
1189
1497
|
start: point,
|
|
1190
1498
|
control1: point,
|
|
1191
1499
|
control2: point,
|
|
1192
1500
|
end: point,
|
|
1193
|
-
easing:
|
|
1194
|
-
orientToPath:
|
|
1501
|
+
easing: z5.enum(["linear", "ease-in", "ease-out", "ease-in-out"]),
|
|
1502
|
+
orientToPath: z5.boolean().default(false)
|
|
1195
1503
|
}).strict();
|
|
1196
|
-
var region =
|
|
1504
|
+
var region = z5.object({ id, shape, label: z5.string().min(1).max(160) }).strict();
|
|
1197
1505
|
var base = { actionId: id, sceneId: id };
|
|
1198
|
-
var vendoredDesignActionSchema =
|
|
1199
|
-
|
|
1506
|
+
var vendoredDesignActionSchema = z5.discriminatedUnion("kind", [
|
|
1507
|
+
z5.object({
|
|
1200
1508
|
...base,
|
|
1201
|
-
kind:
|
|
1509
|
+
kind: z5.literal("set-layer-transform"),
|
|
1202
1510
|
layerId: id,
|
|
1203
1511
|
transform
|
|
1204
1512
|
}).strict(),
|
|
1205
|
-
|
|
1513
|
+
z5.object({
|
|
1206
1514
|
...base,
|
|
1207
|
-
kind:
|
|
1515
|
+
kind: z5.literal("set-local-colour-effect"),
|
|
1208
1516
|
effectId: id,
|
|
1209
|
-
parameters:
|
|
1517
|
+
parameters: z5.object(colourParameters).strict()
|
|
1210
1518
|
}).strict(),
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1519
|
+
z5.object({ ...base, kind: z5.literal("create-mask"), mask }).strict(),
|
|
1520
|
+
z5.object({ ...base, kind: z5.literal("set-mask"), maskId: id, mask }).strict(),
|
|
1521
|
+
z5.object({
|
|
1214
1522
|
...base,
|
|
1215
|
-
kind:
|
|
1523
|
+
kind: z5.literal("set-mask-review-state"),
|
|
1216
1524
|
maskId: id,
|
|
1217
|
-
reviewState:
|
|
1525
|
+
reviewState: z5.enum(["draft", "approved", "rejected"])
|
|
1218
1526
|
}).strict(),
|
|
1219
|
-
|
|
1220
|
-
|
|
1527
|
+
z5.object({ ...base, kind: z5.literal("delete-mask"), maskId: id }).strict(),
|
|
1528
|
+
z5.object({
|
|
1221
1529
|
...base,
|
|
1222
|
-
kind:
|
|
1530
|
+
kind: z5.literal("attach-effect"),
|
|
1223
1531
|
effect,
|
|
1224
|
-
layerIds:
|
|
1532
|
+
layerIds: z5.array(id).min(1).max(50)
|
|
1225
1533
|
}).strict(),
|
|
1226
|
-
|
|
1227
|
-
|
|
1534
|
+
z5.object({ ...base, kind: z5.literal("remove-effect"), effectId: id }).strict(),
|
|
1535
|
+
z5.object({
|
|
1228
1536
|
...base,
|
|
1229
|
-
kind:
|
|
1537
|
+
kind: z5.literal("set-layer-text"),
|
|
1230
1538
|
layerId: id,
|
|
1231
|
-
text:
|
|
1539
|
+
text: z5.string().max(1e4)
|
|
1232
1540
|
}).strict(),
|
|
1233
|
-
|
|
1541
|
+
z5.object({
|
|
1234
1542
|
...base,
|
|
1235
|
-
kind:
|
|
1543
|
+
kind: z5.literal("set-layer-visibility"),
|
|
1236
1544
|
layerId: id,
|
|
1237
|
-
visible:
|
|
1545
|
+
visible: z5.boolean()
|
|
1238
1546
|
}).strict(),
|
|
1239
|
-
|
|
1547
|
+
z5.object({
|
|
1240
1548
|
...base,
|
|
1241
|
-
kind:
|
|
1549
|
+
kind: z5.literal("set-layer-locked"),
|
|
1242
1550
|
layerId: id,
|
|
1243
|
-
locked:
|
|
1551
|
+
locked: z5.boolean()
|
|
1244
1552
|
}).strict(),
|
|
1245
|
-
|
|
1553
|
+
z5.object({
|
|
1246
1554
|
...base,
|
|
1247
|
-
kind:
|
|
1555
|
+
kind: z5.literal("set-layer-mask-ids"),
|
|
1248
1556
|
layerId: id,
|
|
1249
|
-
maskIds:
|
|
1557
|
+
maskIds: z5.array(id).max(16)
|
|
1250
1558
|
}).strict(),
|
|
1251
|
-
|
|
1559
|
+
z5.object({
|
|
1252
1560
|
...base,
|
|
1253
|
-
kind:
|
|
1254
|
-
orderedLayerIds:
|
|
1561
|
+
kind: z5.literal("set-layer-order"),
|
|
1562
|
+
orderedLayerIds: z5.array(id).min(1).max(500)
|
|
1255
1563
|
}).strict(),
|
|
1256
|
-
|
|
1564
|
+
z5.object({
|
|
1257
1565
|
...base,
|
|
1258
|
-
kind:
|
|
1566
|
+
kind: z5.literal("set-motion-path"),
|
|
1259
1567
|
path: motionPath,
|
|
1260
|
-
layerIds:
|
|
1568
|
+
layerIds: z5.array(id).max(50).default([])
|
|
1261
1569
|
}).strict(),
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1570
|
+
z5.object({ ...base, kind: z5.literal("remove-motion-path"), pathId: id }).strict(),
|
|
1571
|
+
z5.object({ ...base, kind: z5.literal("set-protected-region"), region }).strict(),
|
|
1572
|
+
z5.object({
|
|
1265
1573
|
...base,
|
|
1266
|
-
kind:
|
|
1574
|
+
kind: z5.literal("remove-protected-region"),
|
|
1267
1575
|
regionId: id
|
|
1268
1576
|
}).strict()
|
|
1269
1577
|
]);
|
|
1270
|
-
var inspectDesignDocumentToolSchema =
|
|
1271
|
-
projectId:
|
|
1272
|
-
documentId:
|
|
1273
|
-
includeVersions:
|
|
1578
|
+
var inspectDesignDocumentToolSchema = z5.object({
|
|
1579
|
+
projectId: z5.string().uuid(),
|
|
1580
|
+
documentId: z5.string().uuid(),
|
|
1581
|
+
includeVersions: z5.boolean().default(true)
|
|
1274
1582
|
}).strict();
|
|
1275
|
-
var applyDesignActionsToolSchema =
|
|
1276
|
-
projectId:
|
|
1277
|
-
documentId:
|
|
1278
|
-
actionGroupId:
|
|
1279
|
-
expectedVersion:
|
|
1280
|
-
actions:
|
|
1281
|
-
clientCreatedAt:
|
|
1583
|
+
var applyDesignActionsToolSchema = z5.object({
|
|
1584
|
+
projectId: z5.string().uuid(),
|
|
1585
|
+
documentId: z5.string().uuid(),
|
|
1586
|
+
actionGroupId: z5.string().min(1).max(120),
|
|
1587
|
+
expectedVersion: z5.number().int().positive(),
|
|
1588
|
+
actions: z5.array(vendoredDesignActionSchema).min(1).max(50),
|
|
1589
|
+
clientCreatedAt: z5.string().datetime()
|
|
1282
1590
|
}).strict();
|
|
1283
1591
|
|
|
1284
1592
|
// src/tools.ts
|
|
1285
|
-
var uuid = (desc) =>
|
|
1593
|
+
var uuid = (desc) => z6.string().uuid().describe(desc);
|
|
1286
1594
|
var READ_ONLY = {
|
|
1287
1595
|
readOnlyHint: true,
|
|
1288
1596
|
destructiveHint: false,
|
|
@@ -1302,8 +1610,8 @@ function registerTools(server) {
|
|
|
1302
1610
|
title: "List jobs",
|
|
1303
1611
|
description: "List recent video jobs for the current organization (newest first). Start here to discover job IDs; for one job's full detail use get_job_status.",
|
|
1304
1612
|
inputSchema: {
|
|
1305
|
-
limit:
|
|
1306
|
-
status:
|
|
1613
|
+
limit: z6.number().int().min(1).max(50).default(10).describe("Number of jobs to return (default 10, max 50)"),
|
|
1614
|
+
status: z6.enum([
|
|
1307
1615
|
"queued",
|
|
1308
1616
|
"rendering",
|
|
1309
1617
|
"draft_ready",
|
|
@@ -1368,7 +1676,7 @@ function registerTools(server) {
|
|
|
1368
1676
|
description: "Get the latest platform engagement metric snapshots for a published job (newest first). Read-only. Only meaningful after publish_job; for render progress use get_job_status.",
|
|
1369
1677
|
inputSchema: {
|
|
1370
1678
|
jobId: uuid("The job ID"),
|
|
1371
|
-
limit:
|
|
1679
|
+
limit: z6.number().int().min(1).max(20).default(10).describe("Max metric snapshots (default 10, max 20)")
|
|
1372
1680
|
},
|
|
1373
1681
|
annotations: READ_ONLY
|
|
1374
1682
|
},
|
|
@@ -1384,10 +1692,10 @@ function registerTools(server) {
|
|
|
1384
1692
|
title: "Get cost summary",
|
|
1385
1693
|
description: "Query production infrastructure costs (Lambda, RunPod, R2, WaveSpeed, PoYo), optionally with reconciliation drift. This is spend data, not engagement \u2014 for a published job's audience numbers use get_job_metrics.",
|
|
1386
1694
|
inputSchema: {
|
|
1387
|
-
since:
|
|
1388
|
-
provider:
|
|
1389
|
-
groupBy:
|
|
1390
|
-
includeReconciliation:
|
|
1695
|
+
since: z6.string().optional().describe("ISO date \u2014 only include events after this date"),
|
|
1696
|
+
provider: z6.enum(["runpod", "lambda", "r2", "wavespeed", "poyo", "all"]).default("all").describe("Filter by provider"),
|
|
1697
|
+
groupBy: z6.enum(["provider", "job", "day"]).default("provider").describe("How to aggregate the summary"),
|
|
1698
|
+
includeReconciliation: z6.boolean().default(false).describe("Include account-wide reconciliation events")
|
|
1391
1699
|
},
|
|
1392
1700
|
annotations: READ_ONLY
|
|
1393
1701
|
},
|
|
@@ -1403,7 +1711,7 @@ function registerTools(server) {
|
|
|
1403
1711
|
{
|
|
1404
1712
|
title: "Inspect workflow",
|
|
1405
1713
|
description: "Inspect the current Studio workflow and its version history for a job.",
|
|
1406
|
-
inputSchema:
|
|
1714
|
+
inputSchema: z6.object({ jobId: uuid("The job ID") }).strict(),
|
|
1407
1715
|
annotations: READ_ONLY
|
|
1408
1716
|
},
|
|
1409
1717
|
async ({ jobId }) => runTool(
|
|
@@ -1483,10 +1791,10 @@ function registerTools(server) {
|
|
|
1483
1791
|
{
|
|
1484
1792
|
title: "Draft workflow",
|
|
1485
1793
|
description: "Create a durable Documentary Journey or Transformation Montage workflow version. Inspect first and pass its current version, or 0 only when no workflow exists.",
|
|
1486
|
-
inputSchema:
|
|
1794
|
+
inputSchema: z6.object({
|
|
1487
1795
|
jobId: uuid("The job ID"),
|
|
1488
|
-
expectedVersion:
|
|
1489
|
-
document:
|
|
1796
|
+
expectedVersion: z6.number().int().nonnegative().describe("Use 0 when no workflow exists yet"),
|
|
1797
|
+
document: z6.unknown().describe("The complete Studio workflow document")
|
|
1490
1798
|
}).strict(),
|
|
1491
1799
|
annotations: SAFE_WRITE
|
|
1492
1800
|
},
|
|
@@ -1497,10 +1805,10 @@ function registerTools(server) {
|
|
|
1497
1805
|
{
|
|
1498
1806
|
title: "Patch workflow",
|
|
1499
1807
|
description: "Create a new workflow version by recursively merging a patch into the current version.",
|
|
1500
|
-
inputSchema:
|
|
1808
|
+
inputSchema: z6.object({
|
|
1501
1809
|
jobId: uuid("The job ID"),
|
|
1502
|
-
expectedVersion:
|
|
1503
|
-
patch:
|
|
1810
|
+
expectedVersion: z6.number().int().positive(),
|
|
1811
|
+
patch: z6.record(z6.string(), z6.unknown())
|
|
1504
1812
|
}).strict(),
|
|
1505
1813
|
annotations: SAFE_WRITE
|
|
1506
1814
|
},
|
|
@@ -1511,9 +1819,9 @@ function registerTools(server) {
|
|
|
1511
1819
|
{
|
|
1512
1820
|
title: "Validate workflow",
|
|
1513
1821
|
description: "Validate and compile the inspected workflow version without rendering or publishing it.",
|
|
1514
|
-
inputSchema:
|
|
1822
|
+
inputSchema: z6.object({
|
|
1515
1823
|
jobId: uuid("The job ID"),
|
|
1516
|
-
expectedVersion:
|
|
1824
|
+
expectedVersion: z6.number().int().positive()
|
|
1517
1825
|
}).strict(),
|
|
1518
1826
|
annotations: SAFE_WRITE
|
|
1519
1827
|
},
|
|
@@ -1524,7 +1832,7 @@ function registerTools(server) {
|
|
|
1524
1832
|
{
|
|
1525
1833
|
title: "Preview workflow",
|
|
1526
1834
|
description: "Return the current workflow's compiled UniversalVideo brief for a safe pre-render preview.",
|
|
1527
|
-
inputSchema:
|
|
1835
|
+
inputSchema: z6.object({ jobId: uuid("The job ID") }).strict(),
|
|
1528
1836
|
annotations: READ_ONLY
|
|
1529
1837
|
},
|
|
1530
1838
|
async ({ jobId }) => runTool(
|
|
@@ -1538,10 +1846,10 @@ function registerTools(server) {
|
|
|
1538
1846
|
{
|
|
1539
1847
|
title: "Compare workflow versions",
|
|
1540
1848
|
description: "Compare two durable workflow versions field by field.",
|
|
1541
|
-
inputSchema:
|
|
1849
|
+
inputSchema: z6.object({
|
|
1542
1850
|
jobId: uuid("The job ID"),
|
|
1543
|
-
fromVersion:
|
|
1544
|
-
toVersion:
|
|
1851
|
+
fromVersion: z6.number().int().positive(),
|
|
1852
|
+
toVersion: z6.number().int().positive()
|
|
1545
1853
|
}).strict(),
|
|
1546
1854
|
annotations: READ_ONLY
|
|
1547
1855
|
},
|
|
@@ -1565,9 +1873,9 @@ function registerTools(server) {
|
|
|
1565
1873
|
{
|
|
1566
1874
|
title: "Approve workflow",
|
|
1567
1875
|
description: "Validate and approve the inspected workflow version. This does not approve or publish the video job.",
|
|
1568
|
-
inputSchema:
|
|
1876
|
+
inputSchema: z6.object({
|
|
1569
1877
|
jobId: uuid("The job ID"),
|
|
1570
|
-
expectedVersion:
|
|
1878
|
+
expectedVersion: z6.number().int().positive()
|
|
1571
1879
|
}).strict(),
|
|
1572
1880
|
annotations: {
|
|
1573
1881
|
readOnlyHint: false,
|
|
@@ -1624,7 +1932,7 @@ function registerTools(server) {
|
|
|
1624
1932
|
inputSchema: {
|
|
1625
1933
|
jobId: uuid("The current job ID"),
|
|
1626
1934
|
sourceRunId: uuid("The current run ID"),
|
|
1627
|
-
trigger:
|
|
1935
|
+
trigger: z6.enum(["user_requested", "verifier_recommendation"])
|
|
1628
1936
|
},
|
|
1629
1937
|
annotations: SAFE_WRITE
|
|
1630
1938
|
},
|
|
@@ -1643,7 +1951,8 @@ function registerTools(server) {
|
|
|
1643
1951
|
inputSchema: { jobId: uuid("The job ID to approve") },
|
|
1644
1952
|
annotations: {
|
|
1645
1953
|
readOnlyHint: false,
|
|
1646
|
-
|
|
1954
|
+
// Approval revokes active review links, so this is not purely additive.
|
|
1955
|
+
destructiveHint: true,
|
|
1647
1956
|
// Approving an already-approved job does not change state further.
|
|
1648
1957
|
idempotentHint: true,
|
|
1649
1958
|
openWorldHint: false
|
|
@@ -1660,15 +1969,25 @@ function registerTools(server) {
|
|
|
1660
1969
|
description: "Publish an APPROVED job to its connected social destination via the gated publish API. All production gates run (paid plan, ownership, abuse, QA, disclosure, destination, rights, moderation). Requires a key with publish scope; the post goes live on the external platform and cannot be recalled by the API. For YouTube pass youtubePrivacyStatus:'private' for safe runs.",
|
|
1661
1970
|
inputSchema: {
|
|
1662
1971
|
jobId: uuid("The approved job to publish"),
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1972
|
+
xPreviewArtifactId: z6.string().min(1).optional().describe("X only. Exact reviewed video artifact ID."),
|
|
1973
|
+
xCaption: z6.string().trim().min(1).max(2e3).optional().describe("X only. Exact post text approved by the user."),
|
|
1974
|
+
xMadeWithAi: z6.boolean().optional().describe("X only. Enable the Made with AI disclosure."),
|
|
1975
|
+
confirmXPublicPost: z6.boolean().optional().describe("True only after the user authorizes this public X post."),
|
|
1976
|
+
confirmXAutomationPolicy: z6.boolean().optional().describe(
|
|
1977
|
+
"True only after the user confirms the X automation policy."
|
|
1978
|
+
),
|
|
1979
|
+
confirmXOriginalContent: z6.boolean().optional().describe(
|
|
1980
|
+
"True only after the user confirms ownership or publishing rights."
|
|
1981
|
+
),
|
|
1982
|
+
youtubePrivacyStatus: z6.enum(["private", "unlisted", "public"]).optional().describe("YouTube only. Use 'private' for safe dogfood runs."),
|
|
1983
|
+
tiktokPostMode: z6.enum(["direct_publish", "draft_upload"]).optional().describe("TikTok only. Use 'draft_upload' for safe dogfood runs."),
|
|
1984
|
+
connectedAccountDestinationId: z6.string().uuid().optional().describe(
|
|
1666
1985
|
"Specific destination when the org has multiple accounts for the provider."
|
|
1667
1986
|
),
|
|
1668
|
-
confirmSyntheticDisclosure:
|
|
1987
|
+
confirmSyntheticDisclosure: z6.boolean().optional().describe(
|
|
1669
1988
|
"Set true when the job requires an AI synthetic-media disclosure."
|
|
1670
1989
|
),
|
|
1671
|
-
confirmQaOverride:
|
|
1990
|
+
confirmQaOverride: z6.boolean().optional().describe("Set true to override a non-passing QA gate.")
|
|
1672
1991
|
},
|
|
1673
1992
|
annotations: {
|
|
1674
1993
|
readOnlyHint: false,
|
|
@@ -1691,7 +2010,7 @@ function registerTools(server) {
|
|
|
1691
2010
|
title: "Connect social account",
|
|
1692
2011
|
description: "Start connecting a social account headlessly (no local browser needed). Returns an approve URL + a short confirm code: open the URL in a browser signed in to Instavar, check the code matches THIS terminal, and approve. Then poll connect_account_status with the returned pairingId until it reports 'connected'.",
|
|
1693
2012
|
inputSchema: {
|
|
1694
|
-
provider:
|
|
2013
|
+
provider: z6.enum([
|
|
1695
2014
|
"youtube",
|
|
1696
2015
|
"x",
|
|
1697
2016
|
"tiktok",
|
|
@@ -1740,7 +2059,7 @@ function registerConfiguredTools(server, apiKey = process.env.INSTAVAR_API_KEY)
|
|
|
1740
2059
|
// package.json
|
|
1741
2060
|
var package_default = {
|
|
1742
2061
|
name: "@instavar/mcp-server",
|
|
1743
|
-
version: "0.
|
|
2062
|
+
version: "0.6.0",
|
|
1744
2063
|
mcpName: "com.instavar/mcp-server",
|
|
1745
2064
|
description: "Make local videos with Instavar Remotion templates for free, then add an API key for hosted Studio workflows.",
|
|
1746
2065
|
type: "module",
|
|
@@ -1817,7 +2136,7 @@ async function main() {
|
|
|
1817
2136
|
const server = new McpServer(
|
|
1818
2137
|
{ name: "instavar", version: VERSION },
|
|
1819
2138
|
{
|
|
1820
|
-
instructions: "Before drafting concise carousel or video copy, call get_creative_guide for visual-copy. Use the carousel-stories, visual-design and human-review guides as relevant. Apply them to the user brief; the guides do not generate, render, save or publish media."
|
|
2139
|
+
instructions: PAIRED_ANNOUNCEMENT_INSTRUCTIONS + " Before drafting concise carousel or video copy, call get_creative_guide for visual-copy. Use the carousel-stories, visual-design and human-review guides as relevant. Apply them to the user brief; the guides do not generate, render, save or publish media."
|
|
1821
2140
|
}
|
|
1822
2141
|
);
|
|
1823
2142
|
registerConfiguredTools(server);
|