@kolbo/mcp 1.91.4 → 1.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +1 -1
- package/src/toolAnnotations.js +3 -0
- package/src/tools/adobe.js +154 -0
package/README.md
CHANGED
|
@@ -264,7 +264,7 @@ Every generation tool also accepts an optional `project_id` arg that routes the
|
|
|
264
264
|
| `blender_get_command_status` | Read bounded command status/result/error plus absolute `expires_at`; records and idempotency claims expire after 24 hours, and `awaiting_approval` means stop and wait for the user |
|
|
265
265
|
|
|
266
266
|
**Premiere Pro & After Effects Bridge**
|
|
267
|
-
Requires the Kolbo Studio panel open in Premiere Pro or After Effects with **AI agents** switched on in its header. Every edit is approved by the editor inside the panel;
|
|
267
|
+
Requires the Kolbo Studio panel open in Premiere Pro or After Effects with **AI agents** switched on in its header. Every edit is approved by the editor inside the panel; scripts are shown in full before they run.
|
|
268
268
|
|
|
269
269
|
| Tool | Description |
|
|
270
270
|
|------|-------------|
|
|
@@ -275,6 +275,9 @@ Requires the Kolbo Studio panel open in Premiere Pro or After Effects with **AI
|
|
|
275
275
|
| `adobe_place_on_timeline` | Import and place one Kolbo media item at the playhead of the work sequence / active comp (no time or track control in v1) |
|
|
276
276
|
| `adobe_create_sequence` | Create and open a Premiere Pro sequence matching the open sequence's settings, no dialog (Premiere only) |
|
|
277
277
|
| `adobe_import_captions` | Import a Kolbo-hosted SRT onto the active Premiere sequence (Premiere only) |
|
|
278
|
+
| `adobe_edit_composition` | After Effects: one undo step of structured edits - comps, timed and trimmed Kolbo media, titles with stroke, solids, transforms, keyframes, audio fades, layer deletion |
|
|
279
|
+
| `adobe_run_script` | Approval-gated ExtendScript for real motion graphics (shape layers, trim paths, text animators, effects, expressions, cameras); the editor reviews the exact code |
|
|
280
|
+
| `adobe_capture_frame` | Render a comp/sequence frame to the Kolbo library so the agent can check its work |
|
|
278
281
|
| `adobe_get_command_status` | Read command status/result/error; `awaiting_approval` means stop and wait for the editor |
|
|
279
282
|
|
|
280
283
|
**Discovery & Account**
|
package/package.json
CHANGED
package/src/toolAnnotations.js
CHANGED
|
@@ -105,12 +105,15 @@ const OPEN_WORLD_WRITE = [
|
|
|
105
105
|
'publish_html_artifact', 'create_review_share_link', 'blender_capture_viewport',
|
|
106
106
|
// Adobe edits add bins, clips, sequences or caption tracks; none delete or overwrite.
|
|
107
107
|
'adobe_import_media', 'adobe_place_on_timeline', 'adobe_create_sequence', 'adobe_import_captions',
|
|
108
|
+
'adobe_capture_frame',
|
|
108
109
|
];
|
|
109
110
|
|
|
110
111
|
const OPEN_WORLD_DESTRUCTIVE = [
|
|
111
112
|
'share_doc', 'revoke_review_share_link',
|
|
112
113
|
'blender_apply_operations', 'blender_import_media', 'blender_render',
|
|
113
114
|
'blender_undo', 'blender_file_operation', 'blender_execute_python',
|
|
115
|
+
// Can delete layers; approved once per batch in the Kolbo panel.
|
|
116
|
+
'adobe_edit_composition', 'adobe_run_script',
|
|
114
117
|
];
|
|
115
118
|
|
|
116
119
|
const CONTRACT_GROUPS = [
|
package/src/tools/adobe.js
CHANGED
|
@@ -31,6 +31,114 @@ const mediaUrl = z.string().url().max(4096).optional()
|
|
|
31
31
|
const mediaKind = z.enum(['video', 'image', 'audio']).optional()
|
|
32
32
|
.describe('Media kind. Inferred from the Kolbo record or file extension when omitted.');
|
|
33
33
|
|
|
34
|
+
// ─── adobe_edit_composition operation schema (mirrors kolbo-api adobe/schemas.js) ───
|
|
35
|
+
const seconds = (min = 0) => z.number().finite().min(min).max(3600);
|
|
36
|
+
const color = () => z.array(z.number().finite().min(0).max(1)).length(3).describe('[r, g, b], each 0-1');
|
|
37
|
+
const point = () => z.array(z.number().finite().min(-100000).max(100000)).length(2).describe('[x, y] in composition pixels; [0, 0] is top-left');
|
|
38
|
+
const layerRef = z.union([z.number().int().min(1).max(10000), noControls(128)])
|
|
39
|
+
.describe('Layer name (exact) or 1-based index, 1 = top layer');
|
|
40
|
+
// Titles may span lines; every other control character is refused.
|
|
41
|
+
const TITLE_CONTROL = new RegExp('[\\x00-\\x09\\x0b\\x0c\\x0e-\\x1f]');
|
|
42
|
+
const titleText = z.string().min(1).max(500).refine(
|
|
43
|
+
(value) => value.trim().length > 0 && !TITLE_CONTROL.test(value),
|
|
44
|
+
'Text must be 1-500 characters; newlines allowed, no other control characters.',
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const compOperation = z.discriminatedUnion('op', [
|
|
48
|
+
z.object({
|
|
49
|
+
op: z.literal('comp.create'),
|
|
50
|
+
name: noControls(128),
|
|
51
|
+
width: z.number().int().min(16).max(8192).optional().describe('Default 1920'),
|
|
52
|
+
height: z.number().int().min(16).max(8192).optional().describe('Default 1080'),
|
|
53
|
+
frame_rate: z.number().finite().min(1).max(120).optional().describe('Default 30'),
|
|
54
|
+
duration_seconds: seconds(0.1),
|
|
55
|
+
background_color: color().optional(),
|
|
56
|
+
}).strict(),
|
|
57
|
+
z.object({
|
|
58
|
+
op: z.literal('comp.update'),
|
|
59
|
+
name: noControls(128).optional(),
|
|
60
|
+
duration_seconds: seconds(0.1).optional(),
|
|
61
|
+
background_color: color().optional(),
|
|
62
|
+
}).strict(),
|
|
63
|
+
z.object({
|
|
64
|
+
op: z.literal('layer.add_media'),
|
|
65
|
+
media_id: mediaId,
|
|
66
|
+
url: mediaUrl,
|
|
67
|
+
kind: mediaKind,
|
|
68
|
+
name: noControls(128).optional().describe('Layer name; defaults to the file name. Name layers you will animate later.'),
|
|
69
|
+
start_seconds: seconds().optional().describe('Where the layer starts on the comp timeline. Default 0.'),
|
|
70
|
+
trim_start_seconds: seconds().optional().describe('Seconds skipped at the head of the source clip. Default 0.'),
|
|
71
|
+
duration_seconds: seconds(0.04).optional().describe('Visible length. Default: rest of the source (stills: rest of the comp).'),
|
|
72
|
+
fit: z.enum(['cover', 'contain', 'none']).optional().describe('Scale to fill the frame (cover, default), fit inside it, or keep size.'),
|
|
73
|
+
}).strict(),
|
|
74
|
+
z.object({
|
|
75
|
+
op: z.literal('layer.add_text'),
|
|
76
|
+
text: titleText,
|
|
77
|
+
name: noControls(128).optional(),
|
|
78
|
+
start_seconds: seconds().optional(),
|
|
79
|
+
duration_seconds: seconds(0.04),
|
|
80
|
+
font: noControls(128).optional().describe('PostScript font name. Default "Arial-BoldMT".'),
|
|
81
|
+
font_size: z.number().finite().min(4).max(1000).optional().describe('Default 100'),
|
|
82
|
+
color: color().optional().describe('Default white'),
|
|
83
|
+
stroke_color: color().optional().describe('Outline colour. Default black when stroke_width is set.'),
|
|
84
|
+
stroke_width: z.number().finite().min(0).max(100).optional().describe('Outline in pixels. Use 2-6 whenever text sits over bright or busy footage.'),
|
|
85
|
+
position: point().optional().describe('Centre of the text block. Default: centre of the comp.'),
|
|
86
|
+
}).strict(),
|
|
87
|
+
z.object({
|
|
88
|
+
op: z.literal('layer.add_solid'),
|
|
89
|
+
color: color(),
|
|
90
|
+
name: noControls(128).optional(),
|
|
91
|
+
start_seconds: seconds().optional(),
|
|
92
|
+
duration_seconds: seconds(0.04).optional(),
|
|
93
|
+
}).strict(),
|
|
94
|
+
z.object({
|
|
95
|
+
op: z.literal('layer.update'),
|
|
96
|
+
layer: layerRef,
|
|
97
|
+
new_name: noControls(128).optional(),
|
|
98
|
+
start_seconds: seconds().optional(),
|
|
99
|
+
trim_start_seconds: seconds().optional(),
|
|
100
|
+
duration_seconds: seconds(0.04).optional(),
|
|
101
|
+
position: point().optional(),
|
|
102
|
+
scale: z.number().finite().min(0).max(10000).optional().describe('Percent, uniform'),
|
|
103
|
+
opacity: z.number().finite().min(0).max(100).optional(),
|
|
104
|
+
rotation: z.number().finite().min(-36000).max(36000).optional().describe('Degrees'),
|
|
105
|
+
audio_levels: z.number().finite().min(-96).max(24).optional().describe('dB'),
|
|
106
|
+
enabled: z.boolean().optional(),
|
|
107
|
+
}).strict(),
|
|
108
|
+
z.object({
|
|
109
|
+
op: z.literal('layer.animate'),
|
|
110
|
+
layer: layerRef,
|
|
111
|
+
property: z.enum(['opacity', 'scale', 'position', 'rotation', 'audio_levels'])
|
|
112
|
+
.describe('opacity 0-100, scale percent, rotation degrees, audio_levels dB, position [x, y]'),
|
|
113
|
+
keyframes: z.array(z.object({
|
|
114
|
+
time_seconds: seconds().describe('Comp time of this key'),
|
|
115
|
+
value: z.union([z.number().finite().min(-36000).max(36000), point()]),
|
|
116
|
+
}).strict()).min(1).max(50),
|
|
117
|
+
easing: z.enum(['ease', 'linear']).optional().describe('Default ease'),
|
|
118
|
+
}).strict(),
|
|
119
|
+
z.object({
|
|
120
|
+
op: z.literal('layer.delete'),
|
|
121
|
+
layer: layerRef,
|
|
122
|
+
}).strict(),
|
|
123
|
+
]);
|
|
124
|
+
const compOperations = z.array(compOperation).min(1).max(100)
|
|
125
|
+
.superRefine((ops, ctx) => {
|
|
126
|
+
ops.forEach((operation, opIndex) => {
|
|
127
|
+
if (operation.op !== 'layer.animate') return;
|
|
128
|
+
const wantsPoint = operation.property === 'position';
|
|
129
|
+
operation.keyframes.forEach((key, index) => {
|
|
130
|
+
if (Array.isArray(key.value) !== wantsPoint) {
|
|
131
|
+
ctx.addIssue({
|
|
132
|
+
code: z.ZodIssueCode.custom,
|
|
133
|
+
path: [opIndex, 'keyframes', index, 'value'],
|
|
134
|
+
message: wantsPoint ? 'position keyframes need [x, y]' : `${operation.property} keyframes need a number`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
})
|
|
140
|
+
.refine((ops) => Buffer.byteLength(JSON.stringify(ops), 'utf8') <= 256 * 1024, 'Operations must be at most 256 KiB as JSON.');
|
|
141
|
+
|
|
34
142
|
function text(value) {
|
|
35
143
|
return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };
|
|
36
144
|
}
|
|
@@ -162,6 +270,52 @@ function registerAdobeTools(server, client) {
|
|
|
162
270
|
}
|
|
163
271
|
);
|
|
164
272
|
|
|
273
|
+
server.tool(
|
|
274
|
+
'adobe_edit_composition',
|
|
275
|
+
'After Effects only. Apply a batch of structured edits to a composition as ONE undo step: create or update a comp, add Kolbo media (trimmed and timed), text titles and solids, change layer timing/transform/opacity/audio, animate with keyframes, or delete layers. Times are in seconds on the composition timeline. Layers are addressed by the exact name you gave them or by 1-based index (1 = top). New layers stack on top; solids go to the bottom. Operations run in order and stop at the first failure (earlier ones stay applied). The editor approves the whole batch once in the Kolbo panel. Read workflows/adobe.md before the first call; verify with adobe_get_timeline afterwards.',
|
|
276
|
+
{
|
|
277
|
+
session_id: sessionId,
|
|
278
|
+
operations: compOperations,
|
|
279
|
+
idempotency_key: idempotencyKey,
|
|
280
|
+
},
|
|
281
|
+
async (args) => {
|
|
282
|
+
for (const operation of args.operations) {
|
|
283
|
+
if (operation.op === 'layer.add_media') mediaSource(operation, 'adobe_edit_composition layer.add_media');
|
|
284
|
+
}
|
|
285
|
+
return command(client, 'comp.edit', args, { operations: args.operations });
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
server.tool(
|
|
290
|
+
'adobe_run_script',
|
|
291
|
+
'Run ExtendScript inside the connected After Effects (or Premiere Pro) for real motion graphics: shape layers, trim paths, text animators, effects, masks, expressions, cameras, precomps. `code` is a FUNCTION BODY: use log(...) for progress and `return` a JSON-serialisable result. In After Effects the whole script is one undo step. The editor sees the exact code in the Kolbo panel and must approve it. Scripts have full access to the project and the computer, so never read or write files, call system.callSystem, or touch the network unless the user explicitly asked. Read workflows/after-effects-motion.md before writing motion graphics, then verify with adobe_capture_frame.',
|
|
292
|
+
{
|
|
293
|
+
session_id: sessionId,
|
|
294
|
+
code: z.string().min(1).max(64 * 1024).refine(
|
|
295
|
+
(value) => value.trim().length > 0 && Buffer.byteLength(value, 'utf8') <= 64 * 1024,
|
|
296
|
+
'Script must be non-empty and at most 64 KiB as UTF-8.',
|
|
297
|
+
),
|
|
298
|
+
purpose: noControls(500).describe('Plain-language reason shown to the editor next to the code.'),
|
|
299
|
+
idempotency_key: idempotencyKey,
|
|
300
|
+
},
|
|
301
|
+
async (args) => command(client, 'script.run', args, { code: args.code, purpose: args.purpose })
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
server.tool(
|
|
305
|
+
'adobe_capture_frame',
|
|
306
|
+
'Render one frame of the active After Effects composition (PNG) or Premiere Pro sequence (JPEG) and save it to the Kolbo media library. The result carries the image `url` - look at it to check your motion graphics or edit before telling the user it is done. The editor approves it in the Kolbo panel.',
|
|
307
|
+
{
|
|
308
|
+
session_id: sessionId,
|
|
309
|
+
time_seconds: z.number().finite().min(0).max(3600).optional().describe('Frame time. Default: current playhead / comp time.'),
|
|
310
|
+
project_id: z.string().min(1).max(128).regex(SAFE_ID).optional().describe('Kolbo project to file the capture in.'),
|
|
311
|
+
idempotency_key: idempotencyKey,
|
|
312
|
+
},
|
|
313
|
+
async (args) => command(client, 'frame.capture', args, {
|
|
314
|
+
...(args.time_seconds !== undefined ? { time_seconds: args.time_seconds } : {}),
|
|
315
|
+
...(args.project_id ? { project_id: args.project_id } : {}),
|
|
316
|
+
})
|
|
317
|
+
);
|
|
318
|
+
|
|
165
319
|
server.tool(
|
|
166
320
|
'adobe_get_command_status',
|
|
167
321
|
'Read the state, expires_at and bounded result/error of one Adobe command owned by the caller. awaiting_approval is not a polling state: stop and ask the user to approve or deny it in the Kolbo panel.',
|