@cmo-so/mcp 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cmo-mcp.mjs +103 -15
  2. package/package.json +1 -1
package/dist/cmo-mcp.mjs CHANGED
@@ -7199,7 +7199,7 @@ var require_dist = __commonJS({
7199
7199
  // package.json
7200
7200
  var package_default = {
7201
7201
  name: "@cmo-so/cli",
7202
- version: "0.10.0",
7202
+ version: "0.11.0",
7203
7203
  description: "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
7204
7204
  type: "module",
7205
7205
  license: "MIT",
@@ -8149,8 +8149,17 @@ function domainApi(api2, domain) {
8149
8149
  headers: { "Content-Type": "application/json" },
8150
8150
  body: JSON.stringify(changes)
8151
8151
  }),
8152
- /** Make it (billed): send back the estimate the person agreed to. */
8153
- makeAdVideo: (adId, estimateUsd) => post(`${base}/social/ad-videos/${adId}/make`, { confirm: true, estimate_usd: estimateUsd }),
8152
+ /** Make it (billed): send back the estimate the person agreed to (estimate.pictures_usd for stage
8153
+ * "pictures": only the first frames, to look at before the paid clips). A script that doesn't fit its
8154
+ * timing is refused (409 script_check) unless goAhead. */
8155
+ makeAdVideo: (adId, estimateUsd, opts = {}) => post(`${base}/social/ad-videos/${adId}/make`, {
8156
+ confirm: true,
8157
+ estimate_usd: estimateUsd,
8158
+ ...opts.stage ? { stage: opts.stage } : {},
8159
+ ...opts.goAhead ? { go_ahead: true } : {}
8160
+ }),
8161
+ /** The person's own photo as a scene's first frame (base64, or photo_url: a picture already on CMO.so). */
8162
+ adVideoScenePicture: (adId, sceneId, photo) => post(`${base}/social/ad-videos/${adId}/scenes/${encodeURIComponent(sceneId)}/picture`, photo),
8154
8163
  /** Stop one being made (what's made is kept), or remove one. */
8155
8164
  stopAdVideo: (adId) => api2.request(`${base}/social/ad-videos/${adId}`, { method: "DELETE" }),
8156
8165
  /** Make a LinkedIn, Facebook or Instagram post a video post: it goes out as this video (its picture is dropped). */
@@ -8908,8 +8917,12 @@ var confirmCopy = {
8908
8917
  "zh-Hant": (a) => `\u4FEE\u6539\u5EE3\u544A\u5F71\u7247 #${a.id} \u7684\u5206\u93E1\uFF1A${keys(a).filter((k) => k !== "id").join("\u3001") || "\u7121"}\u3002`
8909
8918
  },
8910
8919
  ad_video_make: {
8911
- en: (a) => `Make ad video #${a.id} now. This costs about $${Number(a.estimate_usd ?? 0).toFixed(2)} (pictures, moving clips, voice and music), in the background.`,
8912
- "zh-Hant": (a) => `\u73FE\u5728\u88FD\u4F5C\u5EE3\u544A\u5F71\u7247 #${a.id}\uFF0C\u8CBB\u7528\u7D04 $${Number(a.estimate_usd ?? 0).toFixed(2)}\uFF08\u5716\u7247\u3001\u52D5\u614B\u7247\u6BB5\u3001\u914D\u97F3\u548C\u914D\u6A02\uFF09\uFF0C\u5728\u80CC\u666F\u9032\u884C\u3002`
8920
+ en: (a) => a.stage === "pictures" ? `Make the pictures of ad video #${a.id} now, to look at before the moving clips. This costs about $${Number(a.estimate_usd ?? 0).toFixed(2)}.` : `Make ad video #${a.id} now. This costs about $${Number(a.estimate_usd ?? 0).toFixed(2)} (pictures, moving clips, voice and music), in the background.${a.go_ahead ? " Its script doesn't fit its timing everywhere; it's made anyway." : ""}`,
8921
+ "zh-Hant": (a) => a.stage === "pictures" ? `\u73FE\u5728\u5148\u88FD\u4F5C\u5EE3\u544A\u5F71\u7247 #${a.id} \u7684\u5716\u7247\uFF0C\u770B\u904E\u518D\u505A\u52D5\u614B\u7247\u6BB5\u3002\u8CBB\u7528\u7D04 $${Number(a.estimate_usd ?? 0).toFixed(2)}\u3002` : `\u73FE\u5728\u88FD\u4F5C\u5EE3\u544A\u5F71\u7247 #${a.id}\uFF0C\u8CBB\u7528\u7D04 $${Number(a.estimate_usd ?? 0).toFixed(2)}\uFF08\u5716\u7247\u3001\u52D5\u614B\u7247\u6BB5\u3001\u914D\u97F3\u548C\u914D\u6A02\uFF09\uFF0C\u5728\u80CC\u666F\u9032\u884C\u3002${a.go_ahead ? "\u8173\u672C\u6709\u90E8\u5206\u6642\u9593\u4E0D\u5408\uFF0C\u4ECD\u7167\u6A23\u88FD\u4F5C\u3002" : ""}`
8922
+ },
8923
+ ad_video_picture: {
8924
+ en: (a) => `Use this picture as the first frame of scene ${a.scene} in ad video #${a.id} (its clip is made again).`,
8925
+ "zh-Hant": (a) => `\u628A\u9019\u5F35\u5716\u7247\u7528\u4F5C\u5EE3\u544A\u5F71\u7247 #${a.id} \u7B2C ${a.scene} \u683C\u7684\u7B2C\u4E00\u500B\u756B\u9762\uFF08\u9019\u683C\u7684\u52D5\u614B\u7247\u6BB5\u6703\u91CD\u505A\uFF09\u3002`
8913
8926
  },
8914
8927
  ad_video_stop: {
8915
8928
  en: (a) => `Stop ad video #${a.id} if it's being made (what's made is kept), or remove it from the list.`,
@@ -11685,7 +11698,7 @@ var adVideo = (v) => ({
11685
11698
  voice: v.plan.voice,
11686
11699
  music: v.plan.music,
11687
11700
  seconds: v.plan.seconds,
11688
- end_card: v.plan.cta,
11701
+ end_card: { ...v.plan.cta, seconds: v.plan.card_seconds ?? 2.5 },
11689
11702
  scenes: v.plan.scenes.map((s) => ({
11690
11703
  id: s.id,
11691
11704
  seconds: s.seconds,
@@ -11693,15 +11706,23 @@ var adVideo = (v) => ({
11693
11706
  motion: s.motion,
11694
11707
  line: s.line,
11695
11708
  caption: s.caption,
11696
- picture_url: s.still_url ?? null
11709
+ picture_url: s.still_url ?? null,
11710
+ own_picture: s.own_picture ?? false,
11711
+ ...s.caption_in != null ? { caption_in: s.caption_in } : {},
11712
+ ...s.caption_out != null ? { caption_out: s.caption_out } : {},
11713
+ ...s.voice_at != null ? { voice_at: s.voice_at } : {}
11697
11714
  }))
11698
11715
  } : null,
11716
+ timeline: v.timeline ?? null,
11717
+ script_check: v.script_check ? { ok: v.script_check.ok, total: v.script_check.total, issues: v.script_check.issues } : null,
11718
+ pictures_ready: v.pictures_ready ?? false,
11699
11719
  estimate: v.estimate,
11700
11720
  cost_usd: v.cost_usd,
11701
11721
  video_id: v.video_id,
11722
+ post: v.item ?? null,
11702
11723
  video: v.video ? { status: v.video.status, preview_url: v.video.preview_url, poster_url: v.video.poster_url } : null,
11703
11724
  error: v.error,
11704
- next: v.status === "planned" ? `Show the storyboard and the cost (about $${(v.estimate?.usd ?? 0).toFixed(2)}); change it with ad_video_change, make it with ad_video_make when the user agrees to that cost.` : v.status === "running" ? "Being made; check again with ad_video_get in a minute or so." : v.status === "ready" ? "Ready: post it with social_video_add (video_id and channels) once video.status is ready." : v.status === "failed" ? "Failed: say why; ad_video_make again carries on from what was made." : ""
11725
+ next: v.status === "planned" ? `Show the storyboard, the timeline, any script_check issues and the cost (about $${(v.estimate?.usd ?? 0).toFixed(2)}${v.pictures_ready ? "" : `; the pictures alone about $${(v.estimate?.pictures_usd ?? 0).toFixed(2)}`}); change it with ad_video_change (or ad_video_picture for the user's own photo); ${v.pictures_ready ? "the pictures are made: show them (picture_url), then" : "suggest ad_video_make stage pictures first to see the pictures cheaply, then"} ad_video_make when the user agrees to that cost.` : v.status === "running" ? "Being made; check again with ad_video_get in a minute or so." : v.status === "ready" ? v.item?.attached ? `Ready, and it's on the post it was made from (post ${v.item.id}): that post goes out as this video. To post it elsewhere too: social_video_add (video_id and channels) once video.status is ready.` : `Ready: post it with social_video_add (video_id and channels) once video.status is ready.${v.item?.skipped ? ` It wasn't put on post ${v.item.id}: ${v.item.skipped}` : ""}` : v.status === "failed" ? "Failed: say why; ad_video_make again carries on from what was made." : ""
11705
11726
  });
11706
11727
  var channelsParam = {
11707
11728
  type: "array",
@@ -13122,7 +13143,7 @@ var socialTools = [
13122
13143
  }),
13123
13144
  tool({
13124
13145
  name: "ad_video_plan",
13125
- description: "Plan a short ad video (beta) and say what making it costs. Maggie writes a storyboard: 2-5 scenes, each with the first picture's brief (no words in pictures), what moves and one camera move, the narrator's line and the words on screen; an end card with the next step and the logo; a voice and music. One AI generation; nothing is billed by the second yet. brief: what the ad should do, in the user's words, or item_id: a post whose text is the brief. aspect 9:16 (Reels, TikTok, Shorts; default) or 16:9 (LinkedIn, YouTube); seconds 15, 20 (default) or 30; reference_url: a public YouTube ad whose rhythm to follow (Gemini watches it; never downloaded). Show the storyboard and the cost (estimate.items, estimate.usd) before ad_video_make. Preview first.",
13146
+ description: "Plan a short ad video (beta) and say what making it costs. Maggie writes a storyboard: 2-5 scenes, each with the first picture's brief (no words in pictures), what moves and one camera move, the narrator's line and the words on screen; an end card with the next step and the logo; a voice and music. One AI generation; nothing is billed by the second yet. brief: what the ad should do, in the user's words, or item_id: a post whose text is the brief (once made, the video goes on that post if it's still to go out on LinkedIn, Facebook or Instagram). aspect 9:16 (Reels, TikTok, Shorts; default) or 16:9 (LinkedIn, YouTube); seconds 15, 20 (default) or 30; reference_url: a public YouTube ad whose rhythm to follow (Gemini watches it; never downloaded). Show the storyboard and the cost (estimate.items, estimate.usd) before ad_video_make. Preview first.",
13126
13147
  parameters: {
13127
13148
  type: "object",
13128
13149
  properties: {
@@ -13169,6 +13190,8 @@ var socialTools = [
13169
13190
  cost_usd: v.cost_usd,
13170
13191
  video_id: v.video_id,
13171
13192
  video_status: v.video?.status ?? null,
13193
+ post_id: v.item?.id ?? null,
13194
+ on_post: v.item?.attached ?? false,
13172
13195
  error: v.error
13173
13196
  })),
13174
13197
  voices: out.voices
@@ -13190,7 +13213,7 @@ var socialTools = [
13190
13213
  }),
13191
13214
  tool({
13192
13215
  name: "ad_video_change",
13193
- description: "Change an ad video's storyboard (beta) as the user asks: a scene's picture brief (still), its movement (motion), the narrator's line, the words on screen (caption) or its seconds (3-8); redo_scenes to make scenes' pictures and clips again; the end card (cta_headline, cta_sub); the voice (a name from ad_video_list voices, or none); the music (words, or none). Only what a change touches is made again (redo): a picture brief or movement means a new paid clip; lines, words, end card, voice or music are cheap. Says the new cost; then ad_video_make. Not while it's being made. Preview first.",
13216
+ description: `Change an ad video's storyboard (beta) as the user asks, before making it (or after: only what changed is made again). A scene (scenes[]): its picture brief (still), movement (motion), the narrator's line, the words on screen (caption), its seconds (3-8), and timing inside it: caption_in / caption_out (when the words show and go, seconds into the scene) and voice_at (when the narrator starts); -1 puts a time back to its default; picture "ai" swaps the user's own photo back to an AI picture. add_scenes (still required; after: a scene id, else at the end); order: the scene ids to keep, in order (removes and reorders; 2-5 scenes); redo_scenes: make scenes' pictures and clips again. The whole video: style (the look; every AI picture and clip again), aspect 9:16 or 16:9 (every picture and clip again), seconds 15/20/30 (scenes rescaled), card_seconds (1.5-4); the end card (cta_headline, cta_sub); the voice (a name from ad_video_list voices, or none); the music (words, or none). redo says what's made again: a picture brief, the look or the shape mean new paid pictures and clips, a movement a new paid clip; lines, words, timing, order, end card, voice or music are cheap. Returns the new cost, the timeline and the script check (lines that don't fit their time, captions too fast to read, length); fix errors before ad_video_make. Not while it's being made. Preview first.`,
13194
13217
  parameters: {
13195
13218
  type: "object",
13196
13219
  properties: {
@@ -13205,12 +13228,36 @@ var socialTools = [
13205
13228
  motion: { type: "string" },
13206
13229
  line: { type: "string" },
13207
13230
  caption: { type: "string" },
13208
- seconds: { type: "number" }
13231
+ seconds: { type: "number" },
13232
+ caption_in: { type: "number", description: "Words show at, seconds into the scene (-1: default)" },
13233
+ caption_out: { type: "number", description: "Words go at, seconds into the scene (-1: default)" },
13234
+ voice_at: { type: "number", description: "Narrator starts at, seconds into the scene (-1: default)" },
13235
+ picture: { type: "string", enum: ["ai"], description: "Back to an AI picture" }
13209
13236
  },
13210
13237
  required: ["id"]
13211
13238
  }
13212
13239
  },
13240
+ add_scenes: {
13241
+ type: "array",
13242
+ items: {
13243
+ type: "object",
13244
+ properties: {
13245
+ still: { type: "string", description: "The first frame's brief (no words in it)" },
13246
+ motion: { type: "string" },
13247
+ line: { type: "string" },
13248
+ caption: { type: "string" },
13249
+ seconds: { type: "number" },
13250
+ after: { type: "string", description: "Put it after this scene id" }
13251
+ },
13252
+ required: ["still"]
13253
+ }
13254
+ },
13255
+ order: { type: "array", items: { type: "string" }, description: "Scene ids to keep, in order" },
13213
13256
  redo_scenes: { type: "array", items: { type: "string" } },
13257
+ style: { type: "string", description: "The look of every scene, one sentence" },
13258
+ aspect: { type: "string", enum: ["9:16", "16:9"] },
13259
+ seconds: { type: "integer", description: "15, 20 or 30" },
13260
+ card_seconds: { type: "number", description: "The end card's seconds, 1.5-4" },
13214
13261
  cta_headline: { type: "string" },
13215
13262
  cta_sub: { type: "string" },
13216
13263
  voice: { type: "string", description: "A voice name, or none" },
@@ -13229,8 +13276,22 @@ var socialTools = [
13229
13276
  ...a.cta_sub != null ? { sub: a.cta_sub } : {}
13230
13277
  };
13231
13278
  const out = await ws(ctx).changeAdVideo(Number(a.id), {
13232
- ...a.scenes?.length ? { scenes: a.scenes } : {},
13279
+ ...a.scenes?.length ? {
13280
+ scenes: a.scenes.map((sc) => {
13281
+ const out2 = { ...sc };
13282
+ for (const k of ["caption_in", "caption_out", "voice_at"]) {
13283
+ if (out2[k] != null && Number(out2[k]) < 0) out2[k] = null;
13284
+ }
13285
+ return out2;
13286
+ })
13287
+ } : {},
13288
+ ...a.add_scenes?.length ? { add_scenes: a.add_scenes } : {},
13289
+ ...a.order?.length ? { order: a.order.map(String) } : {},
13233
13290
  ...a.redo_scenes?.length ? { redo_scenes: a.redo_scenes } : {},
13291
+ ...a.style ? { style: a.style } : {},
13292
+ ...a.aspect ? { aspect: a.aspect } : {},
13293
+ ...a.seconds ? { seconds: Number(a.seconds) } : {},
13294
+ ...a.card_seconds != null ? { card_seconds: Number(a.card_seconds) } : {},
13234
13295
  ...Object.keys(cta).length ? { cta } : {},
13235
13296
  ...a.voice != null ? { voice: /^none$/i.test(a.voice) ? false : a.voice } : {},
13236
13297
  ...a.music != null ? { music: /^none$/i.test(a.music) ? "" : a.music } : {},
@@ -13241,12 +13302,14 @@ var socialTools = [
13241
13302
  }),
13242
13303
  tool({
13243
13304
  name: "ad_video_make",
13244
- description: "Make the ad video (beta): its pictures (on the user's own ChatGPT when their Maggie Bridge has images on, else Gemini), the moving clips (Gemini, billed per second), the voice, the music, the words and the end card, cut into one video, in the background (several minutes; follow it with ad_video_get). It COSTS MONEY: estimate_usd must be the estimate.usd from ad_video_plan / ad_video_get / ad_video_change that the user agreed to; if the cost changed it is refused with the new one. Each website has a daily limit. Making it again after a change or a failure only makes what's missing. When ready, post it with social_video_add and its video_id. Preview first.",
13305
+ description: `Make the ad video (beta). stage "pictures" makes only the first frames (cheap: estimate.pictures_usd) so the user sees them (picture_url) and changes any before the paid clips; then make it again (stage all) for the rest. Stage all: its pictures (on the user's own ChatGPT when their Maggie Bridge has images on, else Gemini), the moving clips (Gemini, billed per second), the voice, the music, the words and the end card, cut into one video, in the background (several minutes; follow it with ad_video_get). It COSTS MONEY: estimate_usd must be the estimate.usd (or estimate.pictures_usd for stage pictures) from ad_video_plan / ad_video_get / ad_video_change that the user agreed to; if the cost changed it is refused with the new one. A script that doesn't fit its timing (script_check errors) is refused: fix it with ad_video_change, or go_ahead true only when the user says to make it anyway. Each website has a daily limit. Making it again after a change or a failure only makes what's missing. When ready, post it with social_video_add and its video_id. Preview first.`,
13245
13306
  parameters: {
13246
13307
  type: "object",
13247
13308
  properties: {
13248
13309
  id: { type: "integer", description: "The ad video" },
13249
- estimate_usd: { type: "number", description: "The cost the user agreed to (estimate.usd)" }
13310
+ estimate_usd: { type: "number", description: "The cost the user agreed to (estimate.usd, or pictures_usd)" },
13311
+ stage: { type: "string", enum: ["all", "pictures"], description: "pictures: only the first frames" },
13312
+ go_ahead: { type: "boolean", description: "Make it although the script check found errors" }
13250
13313
  },
13251
13314
  required: ["id", "estimate_usd"]
13252
13315
  },
@@ -13254,7 +13317,32 @@ var socialTools = [
13254
13317
  need: write5,
13255
13318
  risk: "W3",
13256
13319
  gates: ["VER"],
13257
- run: async (ctx, { id, estimate_usd }) => adVideo(await ws(ctx).makeAdVideo(Number(id), Number(estimate_usd)))
13320
+ run: async (ctx, { id, estimate_usd, stage, go_ahead }) => adVideo(
13321
+ await ws(ctx).makeAdVideo(Number(id), Number(estimate_usd), {
13322
+ ...stage ? { stage } : {},
13323
+ ...go_ahead ? { goAhead: true } : {}
13324
+ })
13325
+ )
13326
+ }),
13327
+ tool({
13328
+ name: "ad_video_picture",
13329
+ description: `Use the user's own photo as an ad video scene's first frame (beta): a picture already on CMO.so (picture_url: a post's picture from social_list / social_post_get). It is cropped to the video's shape; no AI picture, no cost for it; the scene's moving clip is made again. For a photo on their device, they use the Ad video screen in the CMO.so app. Back to an AI picture: ad_video_change scenes [{id, picture: "ai"}]. Not while it's being made. Preview first.`,
13330
+ parameters: {
13331
+ type: "object",
13332
+ properties: {
13333
+ id: { type: "integer", description: "The ad video" },
13334
+ scene: { type: "string", description: "Scene id, e.g. s2" },
13335
+ picture_url: { type: "string", description: "A picture on CMO.so (a post's picture)" }
13336
+ },
13337
+ required: ["id", "scene", "picture_url"]
13338
+ },
13339
+ group: "social",
13340
+ need: write5,
13341
+ risk: "W1",
13342
+ gates: ["VER"],
13343
+ run: async (ctx, a) => adVideo(
13344
+ await ws(ctx).adVideoScenePicture(Number(a.id), String(a.scene), { photo_url: String(a.picture_url).trim() })
13345
+ )
13258
13346
  }),
13259
13347
  tool({
13260
13348
  name: "ad_video_stop",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmo-so/mcp",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "CMO.so's MCP server for Claude Desktop, Cursor, VS Code and other MCP clients: manage your websites, blog posts, LinkedIn queue and results with Maggie.",
5
5
  "type": "module",
6
6
  "license": "MIT",