@writepanda/mcp 1.21.0 → 1.22.1
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/server.mjs +325 -0
- package/package.json +1 -1
package/bin/server.mjs
CHANGED
|
@@ -163,6 +163,208 @@ const TOOLS = [
|
|
|
163
163
|
command: "system.list",
|
|
164
164
|
},
|
|
165
165
|
|
|
166
|
+
// ── workspaces (v1.19) ──────────────────────────────────────────
|
|
167
|
+
// Multi-workspace isolation: each workspace has its own projects,
|
|
168
|
+
// exports, Replicate key, and (v1.19.0+) YouTube connections.
|
|
169
|
+
// Agents managing an agency account can switch workspaces mid-
|
|
170
|
+
// flow to operate inside a specific client's bucket.
|
|
171
|
+
{
|
|
172
|
+
name: "workspace_list",
|
|
173
|
+
description:
|
|
174
|
+
"List every workspace on this machine. Returns `currentWorkspaceId` + a `limit` object describing the plan's cap (`max: null` for unlimited on Team, 1 on Starter/Trial, 3 on Creator). Call this first to see what workspaces exist and which one every other query defaults to.",
|
|
175
|
+
inputSchema: { type: "object", properties: {} },
|
|
176
|
+
command: "workspace.list",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "workspace_current",
|
|
180
|
+
description: "Return the active workspace (what every workspace-scoped query defaults to).",
|
|
181
|
+
inputSchema: { type: "object", properties: {} },
|
|
182
|
+
command: "workspace.current",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "workspace_create",
|
|
186
|
+
description:
|
|
187
|
+
"Create a new workspace. On plan cap: returns `{ ok: false, details: { code: 'workspace_limit_reached', upgradeTo: 'Creator' | 'Team' | null } }` — surface that to the user instead of retrying. Pass `switchTo: true` to make the new workspace active immediately.",
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: "object",
|
|
190
|
+
properties: {
|
|
191
|
+
name: { type: "string", description: "Display name, max 80 chars." },
|
|
192
|
+
switchTo: {
|
|
193
|
+
type: "boolean",
|
|
194
|
+
description: "Switch to the new workspace immediately. Default false.",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
required: ["name"],
|
|
198
|
+
},
|
|
199
|
+
command: "workspace.create",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "workspace_rename",
|
|
203
|
+
description: "Rename a workspace. No cross-workspace side effects.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
id: { type: "string" },
|
|
208
|
+
name: { type: "string", description: "New name, max 80 chars." },
|
|
209
|
+
},
|
|
210
|
+
required: ["id", "name"],
|
|
211
|
+
},
|
|
212
|
+
command: "workspace.rename",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: "workspace_delete",
|
|
216
|
+
description:
|
|
217
|
+
"Hard-delete a workspace and everything inside it: project rows (on-disk .pandastudio files remain), exports, shots, Replicate key + YouTube connections. Refuses to delete the last remaining workspace. Call `workspace.contents` first and show the user what they'll lose.",
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: { id: { type: "string" } },
|
|
221
|
+
required: ["id"],
|
|
222
|
+
},
|
|
223
|
+
command: "workspace.delete",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "workspace_switch",
|
|
227
|
+
description:
|
|
228
|
+
"Change the active workspace. All subsequent project.*/export.*/motion.* queries operate inside the target until the next switch.",
|
|
229
|
+
inputSchema: {
|
|
230
|
+
type: "object",
|
|
231
|
+
properties: { id: { type: "string" } },
|
|
232
|
+
required: ["id"],
|
|
233
|
+
},
|
|
234
|
+
command: "workspace.switch",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "workspace_contents",
|
|
238
|
+
description:
|
|
239
|
+
"Count projects / exports / published YouTube videos in a workspace. Use before `workspace.delete` for the 'you will lose N items' warning.",
|
|
240
|
+
inputSchema: {
|
|
241
|
+
type: "object",
|
|
242
|
+
properties: { id: { type: "string" } },
|
|
243
|
+
required: ["id"],
|
|
244
|
+
},
|
|
245
|
+
command: "workspace.contents",
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// ── YouTube publishing (v1.19) ──────────────────────────────────
|
|
249
|
+
// Connect Google accounts per workspace, publish exports, edit
|
|
250
|
+
// already-published videos. All verbs operate on the active
|
|
251
|
+
// workspace unless the agent switches first.
|
|
252
|
+
{
|
|
253
|
+
name: "youtube_is_configured",
|
|
254
|
+
description:
|
|
255
|
+
"Return whether this PandaStudio build has Google OAuth credentials baked in. When false, every other `youtube.*` verb will fail — tell the user this build can't publish to YouTube.",
|
|
256
|
+
inputSchema: { type: "object", properties: {} },
|
|
257
|
+
command: "youtube.is-configured",
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: "youtube_connect",
|
|
261
|
+
description:
|
|
262
|
+
"Start Google OAuth in the user's system browser. Long-running: waits up to 5 minutes for consent. On success, stores refresh token encrypted under the active workspace and returns the account + its channels. INTERACTIVE — only call when the user explicitly asks to connect a YouTube account.",
|
|
263
|
+
inputSchema: { type: "object", properties: {} },
|
|
264
|
+
command: "youtube.connect",
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: "youtube_disconnect",
|
|
268
|
+
description:
|
|
269
|
+
"Revoke and delete a YouTube account from the active workspace. Published videos stay on YouTube; only the local connection + cached channels + encrypted refresh token are removed.",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: { accountId: { type: "string" } },
|
|
273
|
+
required: ["accountId"],
|
|
274
|
+
},
|
|
275
|
+
command: "youtube.disconnect",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: "youtube_list_accounts",
|
|
279
|
+
description:
|
|
280
|
+
"List connected Google accounts in the active workspace + cached YouTube channels. Call this before `export.publish-youtube` so the user can pick account + channel.",
|
|
281
|
+
inputSchema: { type: "object", properties: {} },
|
|
282
|
+
command: "youtube.list-accounts",
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "youtube_list_channels",
|
|
286
|
+
description:
|
|
287
|
+
"Re-pull the channel list for a connected account from YouTube (bypasses local cache). Useful if the user just created a new channel under the same Google account.",
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: "object",
|
|
290
|
+
properties: { accountId: { type: "string" } },
|
|
291
|
+
required: ["accountId"],
|
|
292
|
+
},
|
|
293
|
+
command: "youtube.list-channels",
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: "export_publish_youtube",
|
|
297
|
+
description:
|
|
298
|
+
"Upload an export-library entry to YouTube via resumable upload. Stamps title/description/tags/privacy on the video and (optionally) pushes the entry's thumbnail. Returns `{ videoId, videoUrl }`. LONG-RUNNING (expect ~30 s per 100 MB). The export row is marked `youtubeVideoId` on success, so repeated calls on an already-published entry should check that field first.",
|
|
299
|
+
inputSchema: {
|
|
300
|
+
type: "object",
|
|
301
|
+
properties: {
|
|
302
|
+
id: { type: "string", description: "Export entry id." },
|
|
303
|
+
accountId: { type: "string" },
|
|
304
|
+
channelId: { type: "string" },
|
|
305
|
+
title: { type: "string" },
|
|
306
|
+
description: { type: "string" },
|
|
307
|
+
tags: { type: "array", items: { type: "string" } },
|
|
308
|
+
categoryId: { type: "string" },
|
|
309
|
+
privacyStatus: {
|
|
310
|
+
type: "string",
|
|
311
|
+
description: "public | unlisted | private. Default unlisted.",
|
|
312
|
+
},
|
|
313
|
+
setThumbnail: { type: "boolean" },
|
|
314
|
+
},
|
|
315
|
+
required: ["id", "accountId", "channelId", "title"],
|
|
316
|
+
},
|
|
317
|
+
command: "export.publish-youtube",
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "export_list_youtube",
|
|
321
|
+
description:
|
|
322
|
+
"List recently-published videos on a connected account (includes videos not published via PandaStudio). Dashboard data.",
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
accountId: { type: "string" },
|
|
327
|
+
max: { type: "number", description: "Default 25, max 50." },
|
|
328
|
+
},
|
|
329
|
+
required: ["accountId"],
|
|
330
|
+
},
|
|
331
|
+
command: "export.list-youtube",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "export_update_youtube",
|
|
335
|
+
description:
|
|
336
|
+
"Update an already-published video's metadata (title / description / tags / category / privacyStatus). Pass only the fields you want to change. Cannot replace the video file — YouTube requires deleting and re-uploading for that.",
|
|
337
|
+
inputSchema: {
|
|
338
|
+
type: "object",
|
|
339
|
+
properties: {
|
|
340
|
+
accountId: { type: "string" },
|
|
341
|
+
videoId: { type: "string" },
|
|
342
|
+
title: { type: "string" },
|
|
343
|
+
description: { type: "string" },
|
|
344
|
+
tags: { type: "array", items: { type: "string" } },
|
|
345
|
+
categoryId: { type: "string" },
|
|
346
|
+
privacyStatus: { type: "string" },
|
|
347
|
+
},
|
|
348
|
+
required: ["accountId", "videoId"],
|
|
349
|
+
},
|
|
350
|
+
command: "export.update-youtube",
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: "export_update_youtube_thumbnail",
|
|
354
|
+
description:
|
|
355
|
+
"Replace an already-published video's thumbnail on YouTube via thumbnails.set. Expects a local 1280×720 image (no auto-crop — pre-process or use `export.generate-thumbnail` output).",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {
|
|
359
|
+
accountId: { type: "string" },
|
|
360
|
+
videoId: { type: "string" },
|
|
361
|
+
imagePath: { type: "string" },
|
|
362
|
+
},
|
|
363
|
+
required: ["accountId", "videoId", "imagePath"],
|
|
364
|
+
},
|
|
365
|
+
command: "export.update-youtube-thumbnail",
|
|
366
|
+
},
|
|
367
|
+
|
|
166
368
|
// ── project lifecycle ───────────────────────────────────────────
|
|
167
369
|
{
|
|
168
370
|
name: "project_list",
|
|
@@ -1176,6 +1378,39 @@ const TOOLS = [
|
|
|
1176
1378
|
},
|
|
1177
1379
|
command: "motion.concat",
|
|
1178
1380
|
},
|
|
1381
|
+
{
|
|
1382
|
+
name: "motion_verify_frames",
|
|
1383
|
+
description:
|
|
1384
|
+
"Extract PNG frames at given timestamps from a rendered video so the agent can VISUALLY verify the motion graphics landed. This operationalises the 'lint passing ≠ design working — VIEW THE FRAMES' rule in reference/motion-philosophy.md §4. Call this after motion_render_html or export.start and BEFORE declaring a motion-graphics deliverable done. Pass either entryId (export-library entry) or videoPath (arbitrary MP4). Timestamps are in seconds, typically 8-15 spread across hero moments. Returns { frames: [{timestampSeconds, path}...] } — the agent must then Read each path as an image (multimodal) and confirm no cropped faces / text overflow / blank frames / forbidden-zone occlusion / flat-white headlines.",
|
|
1385
|
+
inputSchema: {
|
|
1386
|
+
type: "object",
|
|
1387
|
+
required: ["timestamps"],
|
|
1388
|
+
properties: {
|
|
1389
|
+
entryId: {
|
|
1390
|
+
type: "string",
|
|
1391
|
+
description:
|
|
1392
|
+
"Export library entry id. Mutually exclusive with videoPath — one must be provided.",
|
|
1393
|
+
},
|
|
1394
|
+
videoPath: {
|
|
1395
|
+
type: "string",
|
|
1396
|
+
description:
|
|
1397
|
+
"Absolute path to an MP4 (e.g. a motion.render-html intermediate output). Mutually exclusive with entryId.",
|
|
1398
|
+
},
|
|
1399
|
+
timestamps: {
|
|
1400
|
+
type: "array",
|
|
1401
|
+
items: { type: "number" },
|
|
1402
|
+
description:
|
|
1403
|
+
"Timestamps in seconds to extract frames at (e.g. [0.5, 1.5, 3.0, 5.0, 7.5, 10.0, 12.5, 15.0]). Max 30 per call.",
|
|
1404
|
+
},
|
|
1405
|
+
outputName: {
|
|
1406
|
+
type: "string",
|
|
1407
|
+
description:
|
|
1408
|
+
"Optional subdir stem for the output PNGs. Defaults to verify-<rand>. Frames land in RECORDINGS_DIR/<stem>/frame-<ms>.png.",
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
},
|
|
1412
|
+
command: "motion.verify-frames",
|
|
1413
|
+
},
|
|
1179
1414
|
|
|
1180
1415
|
// ── assets ──────────────────────────────────────────────────────
|
|
1181
1416
|
{
|
|
@@ -1273,6 +1508,96 @@ const TOOLS = [
|
|
|
1273
1508
|
inputSchema: { type: "object", properties: {} },
|
|
1274
1509
|
command: "export.list",
|
|
1275
1510
|
},
|
|
1511
|
+
{
|
|
1512
|
+
name: "export_generate_thumbnail",
|
|
1513
|
+
description:
|
|
1514
|
+
"Generate a YouTube thumbnail for an export-library entry via Replicate's gpt-image-2 model. If `prompt` isn't supplied, the local LLM writes one from the transcript (an opinionated YouTube-thumbnail art-director prompt targeting 3:2 aspect, photorealistic or bold-graphic, high contrast, optional short overlay text). Requires the user's Replicate API key to be set in Settings → Integrations (the key is stored encrypted via OS keychain; PandaStudio never sees the user's OpenAI bill). Returns { imagePath, prompt, iterations } on success.",
|
|
1515
|
+
inputSchema: {
|
|
1516
|
+
type: "object",
|
|
1517
|
+
properties: {
|
|
1518
|
+
id: { type: "string", description: "Export entry id." },
|
|
1519
|
+
prompt: {
|
|
1520
|
+
type: "string",
|
|
1521
|
+
description:
|
|
1522
|
+
"Optional explicit prompt. If omitted, the local LLM auto-writes one from the transcript.",
|
|
1523
|
+
},
|
|
1524
|
+
referenceImagePath: {
|
|
1525
|
+
type: "string",
|
|
1526
|
+
description:
|
|
1527
|
+
"Optional reference image (absolute path or https URL) passed as gpt-image-2 `input_images`.",
|
|
1528
|
+
},
|
|
1529
|
+
quality: {
|
|
1530
|
+
type: "string",
|
|
1531
|
+
description: "gpt-image-2 quality. 'low' | 'medium' (default) | 'high'.",
|
|
1532
|
+
},
|
|
1533
|
+
},
|
|
1534
|
+
required: ["id"],
|
|
1535
|
+
},
|
|
1536
|
+
command: "export.generate-thumbnail",
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
name: "export_edit_thumbnail",
|
|
1540
|
+
description:
|
|
1541
|
+
"Iterate on the existing thumbnail via natural-language edit prompt ('make it more red', 'add bold text at top', 'remove the logo'). The current thumbnail is passed to gpt-image-2 as input_images, and the result replaces it. The previous thumbnail is pushed onto `entry.thumbnailIterations` so the user can revert. Quality is fixed at 'low' on edits — fast and cheap, suitable for rapid iteration.",
|
|
1542
|
+
inputSchema: {
|
|
1543
|
+
type: "object",
|
|
1544
|
+
properties: {
|
|
1545
|
+
id: { type: "string", description: "Export entry id." },
|
|
1546
|
+
editPrompt: {
|
|
1547
|
+
type: "string",
|
|
1548
|
+
description: "What to change. One-sentence natural-language instruction.",
|
|
1549
|
+
},
|
|
1550
|
+
},
|
|
1551
|
+
required: ["id", "editPrompt"],
|
|
1552
|
+
},
|
|
1553
|
+
command: "export.edit-thumbnail",
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
name: "export_set_thumbnail",
|
|
1557
|
+
description:
|
|
1558
|
+
"Attach a manually-chosen image file (PNG / JPEG / WebP) as the thumbnail. Use this when the user provides a ready-made image, or when an agent has already generated one elsewhere. Copies the file into the managed thumbnails directory; iteration history is cleared. Prefer `export.generate-thumbnail` for AI-generated thumbnails so iteration history is preserved.",
|
|
1559
|
+
inputSchema: {
|
|
1560
|
+
type: "object",
|
|
1561
|
+
properties: {
|
|
1562
|
+
id: { type: "string", description: "Export entry id." },
|
|
1563
|
+
sourcePath: {
|
|
1564
|
+
type: "string",
|
|
1565
|
+
description: "Absolute path to the local image file (PNG / JPEG / WebP).",
|
|
1566
|
+
},
|
|
1567
|
+
},
|
|
1568
|
+
required: ["id", "sourcePath"],
|
|
1569
|
+
},
|
|
1570
|
+
command: "export.set-thumbnail",
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
name: "export_clear_thumbnail",
|
|
1574
|
+
description: "Remove the current thumbnail and wipe its iteration history.",
|
|
1575
|
+
inputSchema: {
|
|
1576
|
+
type: "object",
|
|
1577
|
+
properties: {
|
|
1578
|
+
id: { type: "string", description: "Export entry id." },
|
|
1579
|
+
},
|
|
1580
|
+
required: ["id"],
|
|
1581
|
+
},
|
|
1582
|
+
command: "export.clear-thumbnail",
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
name: "export_revert_thumbnail",
|
|
1586
|
+
description:
|
|
1587
|
+
"Revert to a previous thumbnail from the iteration history (`entry.thumbnailIterations[].id`). The current image gets pushed onto history, so the revert itself is undoable.",
|
|
1588
|
+
inputSchema: {
|
|
1589
|
+
type: "object",
|
|
1590
|
+
properties: {
|
|
1591
|
+
id: { type: "string", description: "Export entry id." },
|
|
1592
|
+
iterationId: {
|
|
1593
|
+
type: "string",
|
|
1594
|
+
description: "id of the iteration entry from `entry.thumbnailIterations`.",
|
|
1595
|
+
},
|
|
1596
|
+
},
|
|
1597
|
+
required: ["id", "iterationId"],
|
|
1598
|
+
},
|
|
1599
|
+
command: "export.revert-thumbnail",
|
|
1600
|
+
},
|
|
1276
1601
|
|
|
1277
1602
|
// ── preview overlay (v1.9.2) ────────────────────────────────────
|
|
1278
1603
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@writepanda/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.1",
|
|
4
4
|
"description": "Model Context Protocol server for PandaStudio. Exposes the desktop video editor's automation surface to Cursor, Continue, Cline, Claude Desktop, and any MCP-compliant client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pandastudio",
|