@hubfluencer/mcp 0.6.0 → 0.7.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/README.md +4 -3
- package/dist/index.js +138 -17
- package/package.json +1 -1
- package/src/index.ts +333 -24
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Hubfluencer app, and a scoped access token is saved locally to `~/.hubfluencer/c
|
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
npx -y @hubfluencer/mcp login "Claude Code"
|
|
32
|
-
# 1. Open: https://hubfluencer.com/connect?code=ABCD-EFGH
|
|
32
|
+
# 1. Open: https://app.hubfluencer.com/connect?code=ABCD-EFGH
|
|
33
33
|
# 2. Confirm code: ABCDEFGH
|
|
34
34
|
# 3. Click Approve. → ✓ Connected.
|
|
35
35
|
```
|
|
@@ -68,7 +68,7 @@ claude mcp add hubfluencer --env HUBFLUENCER_API_TOKEN=YOUR_TOKEN -- npx -y @hub
|
|
|
68
68
|
|
|
69
69
|
## Tools
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
89 tools, grouped by workflow. Every tool prices its action in its own `description` — costs
|
|
72
72
|
below are the ground truth. **"$0" means it spends no video credits**; planning/AI-helper tools
|
|
73
73
|
instead draw a **free daily quota of 20 AI assists** (top up with `unlock_ai_assists`: 1 credit
|
|
74
74
|
→ +10). Reads need the `video:read` scope; writes need `video:generate`. Nothing here needs
|
|
@@ -79,7 +79,8 @@ instead draw a **free daily quota of 20 AI assists** (top up with `unlock_ai_ass
|
|
|
79
79
|
| Tool | What it does | Cost |
|
|
80
80
|
|---|---|---|
|
|
81
81
|
| **`make_video`** | **One shot: prompt → finished MP4** (create → **price** → start → poll → download). `kind:"auto"` picks a multi-scene editor ad for ad/promo/story briefs, a short for simple ones (reported as `kind_inferred`). Pass `dry_run:true` to preview the cost without spending, or `max_credits` to cap it. | short **15**, editor ~**28** (`dry_run`/`max_credits` gate it) |
|
|
82
|
-
| `create_short` | Create a short draft (12s vertical: two 6s AI segments + title overlay + music; 14s with an end-card poster). Set `headline`/`subheadline`/`music_vibe`/`visual_language` so it isn't bare. | **0** |
|
|
82
|
+
| `create_short` | Create a short draft (12s vertical: two 6s AI segments + title overlay + music; 14s with an end-card poster). Set `headline`/`subheadline`/`music_vibe`/`visual_language` so it isn't bare. Optional opt-in conversion graphics: `offer_text`/`cta_text`/`badge_text`/`star_rating` (off unless you set them). | **0** |
|
|
83
|
+
| `update_short` | Patch an existing short draft — brief (`product_prompt`), copy, styling, or the opt-in conversion graphics; only the fields you pass change. Clear one with `""` (`[]` for `text_beats`, `0` for `star_rating`). Re-run `generate_short` to apply. | **0** |
|
|
83
84
|
| `generate_short` | Render an existing short. Safe to re-call (in-flight = reported, no double charge; a failed short can re-generate). | **15** |
|
|
84
85
|
| `generate_short_text` | AI-draft editable headline / subheadline / caption beats for a short. | 1 assist |
|
|
85
86
|
| `create_editor_ad` | Create an editor project **and** run autopilot end-to-end. Style it with `creative_format` + `visual_language` + `theme`. | credits (est. first) |
|
package/dist/index.js
CHANGED
|
@@ -30120,7 +30120,7 @@ function readStoredCredentials() {
|
|
|
30120
30120
|
// package.json
|
|
30121
30121
|
var package_default = {
|
|
30122
30122
|
name: "@hubfluencer/mcp",
|
|
30123
|
-
version: "0.
|
|
30123
|
+
version: "0.7.1",
|
|
30124
30124
|
description: "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
|
|
30125
30125
|
license: "MIT",
|
|
30126
30126
|
author: "Monocursive <contact@monocursive.com>",
|
|
@@ -31429,6 +31429,21 @@ var VISUAL_LANGUAGES = [
|
|
|
31429
31429
|
"startup_explainer",
|
|
31430
31430
|
"luxury_minimal"
|
|
31431
31431
|
];
|
|
31432
|
+
var LANGUAGES = [
|
|
31433
|
+
"en",
|
|
31434
|
+
"fr",
|
|
31435
|
+
"es",
|
|
31436
|
+
"de",
|
|
31437
|
+
"it",
|
|
31438
|
+
"nl",
|
|
31439
|
+
"pl",
|
|
31440
|
+
"pt",
|
|
31441
|
+
"ru",
|
|
31442
|
+
"zh",
|
|
31443
|
+
"ja",
|
|
31444
|
+
"ko",
|
|
31445
|
+
"ar"
|
|
31446
|
+
];
|
|
31432
31447
|
var RO = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
|
|
31433
31448
|
var WRITE = {
|
|
31434
31449
|
readOnlyHint: false,
|
|
@@ -31511,7 +31526,7 @@ registerTool("make_video", {
|
|
|
31511
31526
|
kind: exports_external.string().optional().describe("'short' (fast, 1 clip), 'editor' (multi-scene), or 'auto' (default — inferred)"),
|
|
31512
31527
|
product_subject: exports_external.string().max(300).optional().describe('EDITOR only: the concrete product / main subject the video must be about (e.g. "Bordeaux red ' + 'wine, dark green bottle"). Hard-grounds the AI so it cannot invent an unrelated product. ' + "Recommended for ads."),
|
|
31513
31528
|
project_intent: exports_external.enum(["social_ad", "creative_story"]).optional().describe("EDITOR only: social_ad for promotional content (product focus + CTA) or creative_story (default)."),
|
|
31514
|
-
language: exports_external.
|
|
31529
|
+
language: exports_external.enum(LANGUAGES).optional().describe('Language (default "en"). Bare ISO-639-1 code only — no region/script subtag ' + '(use "pt", never "pt-BR"/"en-US"). Drives scenario, narration, voice, and captions.'),
|
|
31515
31530
|
aspect: exports_external.string().optional().describe("Aspect ratio for editor: 9:16 (default), 16:9, or 1:1"),
|
|
31516
31531
|
voice_id: exports_external.string().optional().describe("Preferred narration voice id for editor ads (see list_voices); shorts have no voiceover"),
|
|
31517
31532
|
headline: exports_external.string().optional().describe("SHORTS only: the on-screen TITLE overlay (≤160). Set this so the short isn't bare."),
|
|
@@ -31523,9 +31538,10 @@ registerTool("make_video", {
|
|
|
31523
31538
|
offer_text: exports_external.string().optional().describe("SHORTS only: optional offer chip, e.g. -40% (≤16 chars)"),
|
|
31524
31539
|
cta_text: exports_external.string().optional().describe("SHORTS only: optional CTA pill, e.g. Shop now (≤24 chars)"),
|
|
31525
31540
|
badge_text: exports_external.string().optional().describe("SHORTS only: optional badge stamp, e.g. BEST SELLER (≤24 chars)"),
|
|
31526
|
-
star_rating: exports_external.number().optional().describe("SHORTS only: optional 0..5 star rating under the subheadline"),
|
|
31541
|
+
star_rating: exports_external.number().int().min(0).max(5).optional().describe("SHORTS only: optional 0..5 star rating under the subheadline"),
|
|
31527
31542
|
text_position: exports_external.enum(["top", "center", "bottom"]).optional().describe("SHORTS only: title overlay position (default bottom)"),
|
|
31528
31543
|
text_animation: exports_external.enum([
|
|
31544
|
+
"auto",
|
|
31529
31545
|
"reveal",
|
|
31530
31546
|
"typewriter",
|
|
31531
31547
|
"fade_in",
|
|
@@ -31533,9 +31549,10 @@ registerTool("make_video", {
|
|
|
31533
31549
|
"bounce",
|
|
31534
31550
|
"word_stagger",
|
|
31535
31551
|
"word_spotlight"
|
|
31536
|
-
]).optional().describe("SHORTS only: title overlay animation (
|
|
31537
|
-
font_family: exports_external.string().optional().describe("SHORTS only: overlay font
|
|
31552
|
+
]).optional().describe("SHORTS only: title overlay animation. Defaults to auto (follow the visual_language's own " + "animation). Every concrete value (including fade_in) is an explicit pick that always wins."),
|
|
31553
|
+
font_family: exports_external.string().optional().describe("SHORTS only: overlay font. Defaults to auto (follow the visual_language's typography), or pass " + "one of the 15 ShortFont* faces (ShortFontSpaceGrotesk, ShortFontAnton, …). Every concrete face " + "is an explicit pick that always wins."),
|
|
31538
31554
|
music_vibe: exports_external.string().optional().describe("SHORTS only: music mood — Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz"),
|
|
31555
|
+
music_instruments: exports_external.array(exports_external.string()).optional().describe('SHORTS only: instrument hints woven into the music (≤10, each ≤40 chars), e.g. ["piano", "strings"]'),
|
|
31539
31556
|
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("SHORTS + EDITOR: narrative arc / segment structure. Omit for Auto/generic."),
|
|
31540
31557
|
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("SHORTS + EDITOR: visual style direction and render look. Default app choice is kinetic_creator."),
|
|
31541
31558
|
theme: exports_external.string().optional().describe("Visual theme: editor genre overlay, and legacy shorts fallback only when visual_language is unset. " + "For editor, when visual_language is set it drives the look and 'none' is ignored. " + "none (literal — no imposed style), realistic (default), cinematic, " + "anime, sci_fi, fantasy, noir, superhero, horror, mockumentary, sports, gaming, retro_80s, " + "minimalist, cyberpunk"),
|
|
@@ -31572,6 +31589,14 @@ registerTool("make_video", {
|
|
|
31572
31589
|
return fail("each text_beats entry must be at most 120 characters.");
|
|
31573
31590
|
}
|
|
31574
31591
|
}
|
|
31592
|
+
if (args.music_instruments !== undefined) {
|
|
31593
|
+
if (args.music_instruments.length > 10) {
|
|
31594
|
+
return fail("music_instruments must have at most 10 entries.");
|
|
31595
|
+
}
|
|
31596
|
+
if (args.music_instruments.some((i) => i.length > 40)) {
|
|
31597
|
+
return fail("each music_instruments entry must be at most 40 characters.");
|
|
31598
|
+
}
|
|
31599
|
+
}
|
|
31575
31600
|
if (args.save_path)
|
|
31576
31601
|
resolveSavePath(args.save_path);
|
|
31577
31602
|
let slug;
|
|
@@ -31593,10 +31618,11 @@ registerTool("make_video", {
|
|
|
31593
31618
|
short_text_animation: args.text_animation,
|
|
31594
31619
|
short_font_family: args.font_family,
|
|
31595
31620
|
music_vibe: args.music_vibe,
|
|
31621
|
+
music_instruments: args.music_instruments,
|
|
31596
31622
|
creative_format: args.creative_format,
|
|
31597
31623
|
visual_language: args.visual_language,
|
|
31598
31624
|
theme: args.theme
|
|
31599
|
-
}, idemKey("make-short", args.prompt, args.language ?? "", args.headline ?? "", args.subheadline ?? "", JSON.stringify(args.text_beats ?? []), args.headline_color ?? "", args.subheadline_color ?? "", args.accent_color ?? "", args.offer_text ?? "", args.cta_text ?? "", args.badge_text ?? "", String(args.star_rating ?? ""), args.text_position ?? "", args.text_animation ?? "", args.font_family ?? "", args.music_vibe ?? "", args.creative_format ?? "", args.visual_language ?? "", args.theme ?? ""));
|
|
31625
|
+
}, idemKey("make-short", args.prompt, args.language ?? "", args.headline ?? "", args.subheadline ?? "", JSON.stringify(args.text_beats ?? []), args.headline_color ?? "", args.subheadline_color ?? "", args.accent_color ?? "", args.offer_text ?? "", args.cta_text ?? "", args.badge_text ?? "", String(args.star_rating ?? ""), args.text_position ?? "", args.text_animation ?? "", args.font_family ?? "", args.music_vibe ?? "", JSON.stringify(args.music_instruments ?? []), args.creative_format ?? "", args.visual_language ?? "", args.theme ?? ""));
|
|
31600
31626
|
slug = created.data.slug;
|
|
31601
31627
|
} else {
|
|
31602
31628
|
const created = await client.post("/editor", {
|
|
@@ -31703,7 +31729,10 @@ registerTool("list_projects", {
|
|
|
31703
31729
|
}, tool(async (args, client) => {
|
|
31704
31730
|
const cap = Math.min(100, Math.max(1, args.limit ?? 25));
|
|
31705
31731
|
const [shortsRaw, factoriesRaw] = await Promise.all([
|
|
31706
|
-
client.get("/shorts",
|
|
31732
|
+
client.get("/shorts", {
|
|
31733
|
+
...args.include_completed ? { include_completed: "true" } : {},
|
|
31734
|
+
per_page: String(cap)
|
|
31735
|
+
}).catch(() => ({ data: [] })),
|
|
31707
31736
|
client.get("/video-factories").catch(() => ({ data: [] }))
|
|
31708
31737
|
]);
|
|
31709
31738
|
const rows = (raw) => {
|
|
@@ -31728,16 +31757,17 @@ registerTool("create_short", {
|
|
|
31728
31757
|
description: "Creates a short draft from a product prompt (min 10 chars). A short is a 12s vertical (two 6s AI " + "segments + an on-screen title overlay + music; 14s when an end-card poster is set). Returns the slug, " + "costs 0 credits. Follow with generate_short to render. " + "BE PROACTIVE: don't ship a bare clip — set a headline (the on-screen TITLE) and subheadline (secondary " + "title), and pick a music_vibe plus visual_language that fit the brand. To brand it further, attach a product image " + "(set_short_product) or an end-card poster (set_short_poster) before generate_short. (Shorts have no logo " + "overlay — that's an editor-only feature; use create_editor_ad for logo branding.)",
|
|
31729
31758
|
inputSchema: {
|
|
31730
31759
|
product_prompt: exports_external.string().min(10).describe("What the short should be about"),
|
|
31731
|
-
language: exports_external.
|
|
31760
|
+
language: exports_external.enum(LANGUAGES).optional().describe('Language code, e.g. "en" (default). Bare ISO-639-1 code only — no region/script subtag like "en-US".'),
|
|
31732
31761
|
headline: exports_external.string().max(160).optional().describe("The on-screen TITLE composited over the short (poster text overlay). ≤160 chars. Set this."),
|
|
31733
31762
|
subheadline: exports_external.string().max(200).optional().describe("The SECONDARY title / supporting line under the headline. ≤200 chars."),
|
|
31734
31763
|
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Optional structure: problem_solution, mistake_fix, myth_vs_reality, before_after, proof_demo, product_reveal. Omit for Auto."),
|
|
31735
31764
|
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("Visual language for Veo direction and render styling. Good default: kinetic_creator."),
|
|
31736
31765
|
theme: exports_external.string().optional().describe("Deprecated for shorts: legacy visual theme used only when visual_language is unset."),
|
|
31737
31766
|
music_vibe: exports_external.string().optional().describe("Background-music mood. Recognized: Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz."),
|
|
31738
|
-
music_instruments: exports_external.array(exports_external.string()).optional().describe('Optional instrument hints, e.g. ["piano", "strings"]'),
|
|
31767
|
+
music_instruments: exports_external.array(exports_external.string().max(40)).max(10).optional().describe('Optional instrument hints (≤10, each ≤40 chars), e.g. ["piano", "strings"]'),
|
|
31739
31768
|
text_position: exports_external.enum(["top", "center", "bottom"]).optional().describe("Where the title overlay sits (default bottom)"),
|
|
31740
31769
|
text_animation: exports_external.enum([
|
|
31770
|
+
"auto",
|
|
31741
31771
|
"reveal",
|
|
31742
31772
|
"typewriter",
|
|
31743
31773
|
"fade_in",
|
|
@@ -31745,16 +31775,17 @@ registerTool("create_short", {
|
|
|
31745
31775
|
"bounce",
|
|
31746
31776
|
"word_stagger",
|
|
31747
31777
|
"word_spotlight"
|
|
31748
|
-
]).optional().describe("Title overlay animation (
|
|
31749
|
-
font_family: exports_external.string().optional().describe("Overlay font (
|
|
31778
|
+
]).optional().describe("Title overlay animation. Defaults to auto (follow the visual_language's own animation). " + "Every concrete value (including fade_in) is an explicit pick that always wins."),
|
|
31779
|
+
font_family: exports_external.string().optional().describe("Overlay font. Defaults to auto (follow the visual_language's typography). Every concrete face " + "is an explicit pick that always wins. One of: auto, ShortFontSpaceGrotesk, ShortFontMontserrat, " + "ShortFontTheBold, ShortFontImpact, ShortFontLato, ShortFontAnton, " + "ShortFontBebasNeue, ShortFontOswald, ShortFontArchivoBlack, ShortFontPoppins, ShortFontInter, " + "ShortFontTikTokSans, ShortFontBangers, ShortFontDMSerif, ShortFontPermanentMarker."),
|
|
31750
31780
|
headline_color: exports_external.string().optional().describe("Headline hex color, e.g. #ffffff"),
|
|
31751
31781
|
subheadline_color: exports_external.string().optional().describe("Subheadline hex color, e.g. #ffffff"),
|
|
31752
31782
|
accent_color: exports_external.string().optional().describe("6-digit brand accent hex color, e.g. #09EFBE"),
|
|
31753
31783
|
offer_text: exports_external.string().max(16).optional().describe('Optional offer chip, e.g. "-40%" or "2 FOR 1"'),
|
|
31754
31784
|
cta_text: exports_external.string().max(24).optional().describe('Optional CTA pill, e.g. "Shop now"'),
|
|
31755
31785
|
badge_text: exports_external.string().max(24).optional().describe('Optional badge stamp, e.g. "BEST SELLER"'),
|
|
31756
|
-
star_rating: exports_external.number().min(0).max(5).optional().describe("Optional 0..5 star rating under the subheadline"),
|
|
31757
|
-
text_beats: exports_external.array(exports_external.string().max(120)).max(8).optional().describe("Optional caption beats shown sequentially instead of the static subheadline.")
|
|
31786
|
+
star_rating: exports_external.number().int().min(0).max(5).optional().describe("Optional 0..5 star rating under the subheadline"),
|
|
31787
|
+
text_beats: exports_external.array(exports_external.string().max(120)).max(8).optional().describe("Optional caption beats shown sequentially instead of the static subheadline."),
|
|
31788
|
+
brand_profile_id: exports_external.number().int().optional().describe("Seed brand defaults — colors/font/CTA — from a saved brand profile (id from " + "list_brand_profiles). Its creative format, visual language, font, accent, and default CTA fill " + "any blank short fields and a snapshot is frozen onto the short. Must be one of your own profiles.")
|
|
31758
31789
|
},
|
|
31759
31790
|
outputSchema: createDraftOutput,
|
|
31760
31791
|
annotations: { title: "Create short", ...WRITE, idempotentHint: true }
|
|
@@ -31800,13 +31831,103 @@ registerTool("create_short", {
|
|
|
31800
31831
|
body.star_rating = args.star_rating;
|
|
31801
31832
|
if (args.text_beats !== undefined)
|
|
31802
31833
|
body.text_beats = args.text_beats;
|
|
31803
|
-
|
|
31834
|
+
if (args.brand_profile_id !== undefined)
|
|
31835
|
+
body.brand_profile_id = args.brand_profile_id;
|
|
31836
|
+
const res = await client.post("/shorts", body, idemKey("create-short", args.product_prompt, args.language ?? "", args.headline ?? "", args.subheadline ?? "", args.creative_format ?? "", args.visual_language ?? "", args.theme ?? "", args.music_vibe ?? "", JSON.stringify(args.music_instruments ?? []), args.text_position ?? "", args.text_animation ?? "", args.font_family ?? "", args.headline_color ?? "", args.subheadline_color ?? "", args.accent_color ?? "", args.offer_text ?? "", args.cta_text ?? "", args.badge_text ?? "", String(args.star_rating ?? ""), JSON.stringify(args.text_beats ?? []), String(args.brand_profile_id ?? "")));
|
|
31804
31837
|
return ok({
|
|
31805
31838
|
slug: res.data.slug,
|
|
31806
31839
|
kind: "short",
|
|
31807
31840
|
next: "set_short_product / set_short_poster (optional branding), then generate_short"
|
|
31808
31841
|
});
|
|
31809
31842
|
}));
|
|
31843
|
+
registerTool("update_short", {
|
|
31844
|
+
title: "Update a short draft (0 credits)",
|
|
31845
|
+
description: "Patches an existing short draft — only the fields you pass change; omitted fields are left untouched. " + "0 credits, no re-render (call generate_short afterwards to apply). Use this to edit the brief, copy, " + "styling, or the optional conversion graphics after create_short. " + "CLEARING a field: the conversion graphics (offer_text, cta_text, badge_text, star_rating) and " + 'text_beats are OPT-IN and OFF by default — pass "" (or 0 for star_rating, [] for text_beats) to REMOVE ' + "one that was set. The four conversion graphics are user-supplied only and never written by the AI copy " + "assist; text_beats (like headline/subheadline) IS rewritten by generate_short_text, so hand-crafted " + "beats don't survive a later assist call. " + "404 for an unknown/foreign slug.",
|
|
31846
|
+
inputSchema: {
|
|
31847
|
+
slug: exports_external.string().describe("Short slug (from create_short)"),
|
|
31848
|
+
product_prompt: exports_external.string().min(10).optional().describe("Rewrite the brief (what the short is about, ≥10 chars); drives the next generate_short."),
|
|
31849
|
+
language: exports_external.enum(LANGUAGES).optional().describe('Language code, e.g. "en". Bare ISO-639-1 code only — no region/script subtag like "en-US".'),
|
|
31850
|
+
headline: exports_external.string().max(160).optional().describe("On-screen TITLE overlay. ≤160 chars."),
|
|
31851
|
+
subheadline: exports_external.string().max(200).optional().describe("Secondary title / supporting line. ≤200 chars."),
|
|
31852
|
+
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Structure: problem_solution, mistake_fix, myth_vs_reality, before_after, proof_demo, product_reveal."),
|
|
31853
|
+
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("Visual language for Veo direction and render styling."),
|
|
31854
|
+
theme: exports_external.string().optional().describe("Deprecated for shorts: legacy visual theme used only when visual_language is unset."),
|
|
31855
|
+
music_vibe: exports_external.string().optional().describe("Background-music mood: Upbeat, Cinematic, Minimal, Luxury, Playful, Jazz."),
|
|
31856
|
+
music_instruments: exports_external.array(exports_external.string().max(40)).max(10).optional().describe('Instrument hints (≤10, each ≤40 chars), e.g. ["piano", "strings"]'),
|
|
31857
|
+
text_position: exports_external.enum(["top", "center", "bottom"]).optional().describe("Where the title overlay sits."),
|
|
31858
|
+
text_animation: exports_external.enum([
|
|
31859
|
+
"auto",
|
|
31860
|
+
"reveal",
|
|
31861
|
+
"typewriter",
|
|
31862
|
+
"fade_in",
|
|
31863
|
+
"pop",
|
|
31864
|
+
"bounce",
|
|
31865
|
+
"word_stagger",
|
|
31866
|
+
"word_spotlight"
|
|
31867
|
+
]).optional().describe("Title overlay animation. Defaults to auto (follow the visual_language's own animation). " + "Every concrete value (including fade_in) is an explicit pick that always wins."),
|
|
31868
|
+
font_family: exports_external.string().optional().describe("Overlay font. Defaults to auto (follow the visual_language's typography). Every concrete face " + "is an explicit pick that always wins. One of: auto, ShortFontSpaceGrotesk, ShortFontMontserrat, ShortFontTheBold, ShortFontImpact, " + "ShortFontLato, ShortFontAnton, ShortFontBebasNeue, ShortFontOswald, ShortFontArchivoBlack, " + "ShortFontPoppins, ShortFontInter, ShortFontTikTokSans, ShortFontBangers, ShortFontDMSerif, " + "ShortFontPermanentMarker."),
|
|
31869
|
+
headline_color: exports_external.string().optional().describe("Headline hex color, e.g. #ffffff"),
|
|
31870
|
+
subheadline_color: exports_external.string().optional().describe("Subheadline hex color, e.g. #ffffff"),
|
|
31871
|
+
accent_color: exports_external.string().optional().describe("6-digit brand accent hex color, e.g. #09EFBE"),
|
|
31872
|
+
offer_text: exports_external.string().max(16).optional().describe('Offer chip (opt-in), e.g. "-40%". Pass "" to remove it.'),
|
|
31873
|
+
cta_text: exports_external.string().max(24).optional().describe('CTA pill (opt-in), e.g. "Shop now". Pass "" to remove it.'),
|
|
31874
|
+
badge_text: exports_external.string().max(24).optional().describe('Badge stamp (opt-in), e.g. "BEST SELLER". Pass "" to remove it.'),
|
|
31875
|
+
star_rating: exports_external.number().int().min(0).max(5).optional().describe("0..5 star rating under the subheadline (opt-in). Pass 0 to remove it."),
|
|
31876
|
+
text_beats: exports_external.array(exports_external.string().max(120)).max(8).optional().describe("Caption beats shown sequentially instead of the static subheadline. Pass [] to clear."),
|
|
31877
|
+
brand_profile_id: exports_external.number().int().optional().describe("Seed brand defaults — colors/font/CTA — from a saved brand profile (id from " + "list_brand_profiles). Best set at create_short time (that path also freezes a brand snapshot); " + "on update it sets the linked profile. Must be one of your own profiles.")
|
|
31878
|
+
},
|
|
31879
|
+
annotations: {
|
|
31880
|
+
title: "Update short",
|
|
31881
|
+
...WRITE,
|
|
31882
|
+
idempotentHint: true
|
|
31883
|
+
}
|
|
31884
|
+
}, tool(async (args, client) => {
|
|
31885
|
+
const body = {};
|
|
31886
|
+
if (args.product_prompt !== undefined)
|
|
31887
|
+
body.product_prompt = args.product_prompt;
|
|
31888
|
+
if (args.language !== undefined)
|
|
31889
|
+
body.language = args.language;
|
|
31890
|
+
if (args.headline !== undefined)
|
|
31891
|
+
body.headline = args.headline;
|
|
31892
|
+
if (args.subheadline !== undefined)
|
|
31893
|
+
body.subheadline = args.subheadline;
|
|
31894
|
+
if (args.creative_format !== undefined)
|
|
31895
|
+
body.creative_format = args.creative_format;
|
|
31896
|
+
if (args.visual_language !== undefined)
|
|
31897
|
+
body.visual_language = args.visual_language;
|
|
31898
|
+
if (args.theme !== undefined)
|
|
31899
|
+
body.theme = args.theme;
|
|
31900
|
+
if (args.music_vibe !== undefined)
|
|
31901
|
+
body.music_vibe = args.music_vibe;
|
|
31902
|
+
if (args.music_instruments !== undefined)
|
|
31903
|
+
body.music_instruments = args.music_instruments;
|
|
31904
|
+
if (args.text_position !== undefined)
|
|
31905
|
+
body.short_text_position = args.text_position;
|
|
31906
|
+
if (args.text_animation !== undefined)
|
|
31907
|
+
body.short_text_animation = args.text_animation;
|
|
31908
|
+
if (args.font_family !== undefined)
|
|
31909
|
+
body.short_font_family = args.font_family;
|
|
31910
|
+
if (args.headline_color !== undefined)
|
|
31911
|
+
body.headline_color = args.headline_color;
|
|
31912
|
+
if (args.subheadline_color !== undefined)
|
|
31913
|
+
body.subheadline_color = args.subheadline_color;
|
|
31914
|
+
if (args.accent_color !== undefined)
|
|
31915
|
+
body.accent_color = args.accent_color;
|
|
31916
|
+
if (args.offer_text !== undefined)
|
|
31917
|
+
body.offer_text = args.offer_text;
|
|
31918
|
+
if (args.cta_text !== undefined)
|
|
31919
|
+
body.cta_text = args.cta_text;
|
|
31920
|
+
if (args.badge_text !== undefined)
|
|
31921
|
+
body.badge_text = args.badge_text;
|
|
31922
|
+
if (args.star_rating !== undefined)
|
|
31923
|
+
body.star_rating = args.star_rating;
|
|
31924
|
+
if (args.text_beats !== undefined)
|
|
31925
|
+
body.text_beats = args.text_beats;
|
|
31926
|
+
if (args.brand_profile_id !== undefined)
|
|
31927
|
+
body.brand_profile_id = args.brand_profile_id;
|
|
31928
|
+
const res = await client.patch(`/shorts/${args.slug}`, body);
|
|
31929
|
+
return ok(asRecord(res).data ?? res);
|
|
31930
|
+
}));
|
|
31810
31931
|
async function getTracking(client, slug, deadline) {
|
|
31811
31932
|
const res = await client.get(`/tracking/${slug}`, undefined, deadline);
|
|
31812
31933
|
return res.data;
|
|
@@ -32178,7 +32299,7 @@ registerTool("create_editor_ad", {
|
|
|
32178
32299
|
product_prompt: exports_external.string().min(10).describe("Brief for the ad (min 10 chars)"),
|
|
32179
32300
|
product_subject: exports_external.string().max(300).optional().describe('The concrete product or main subject the video must be about (e.g. "Bordeaux red wine, dark green ' + 'bottle with a gold label"). Distinct from the freeform brief: hard-grounds the AI scenario + narration ' + "so it cannot invent an unrelated product. Strongly recommended for ads."),
|
|
32180
32301
|
project_intent: exports_external.enum(["social_ad", "creative_story"]).optional().describe("social_ad for promotional/ad content (product focus + CTA), creative_story for brand narratives " + "(default)."),
|
|
32181
|
-
language: exports_external.
|
|
32302
|
+
language: exports_external.enum(LANGUAGES).optional().describe('Language (default "en"). Bare ISO-639-1 code only — no region/script subtag (use "es", not "es-MX"). ' + "Drives the WHOLE video: scenario, narration script, narration voice, and captions. " + "Ask the user / infer from their locale."),
|
|
32182
32303
|
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Narrative arc / segment structure. Omit for Auto/generic."),
|
|
32183
32304
|
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("Visual style direction and render look. Good default: kinetic_creator. When set it drives the look and a theme of 'none' is ignored."),
|
|
32184
32305
|
theme: exports_external.string().optional().describe('Visual theme / genre overlay (default "realistic"). One of: none (no imposed style — segments follow your prompts literally), realistic, cinematic, anime, sci_fi, fantasy, noir, superhero, horror, mockumentary, sports, gaming, retro_80s, minimalist, cyberpunk.'),
|
|
@@ -32215,7 +32336,7 @@ registerTool("start_autopilot", {
|
|
|
32215
32336
|
description: "Starts server-orchestrated autopilot (scenario → segments → narration → voice → music → render) on an " + "EXISTING editor project/draft. Spends credits — preview the estimate first with the autopilot/cost endpoint " + "or make_video({ dry_run:true }). Each call is a genuine start attempt (like tapping Launch in the app): " + "if a run is already in progress for this slug the server returns already_running — it never double-starts " + "or double-charges. Once finished, failed, or cancelled, calling again relaunches it (resume after a top-up, raising your spend cap, or a mid-pipeline failure). Then poll with " + 'wait_for_completion({ slug, kind: "editor" }).',
|
|
32216
32337
|
inputSchema: {
|
|
32217
32338
|
slug: exports_external.string().describe("Editor project slug"),
|
|
32218
|
-
language: exports_external.
|
|
32339
|
+
language: exports_external.enum(LANGUAGES).optional().describe('Optional language override applied before launch. Bare ISO-639-1 code only, e.g. "fr" — no ' + "region/script subtag. Drives scenario, narration, voice, and captions."),
|
|
32219
32340
|
product_subject: exports_external.string().max(300).optional().describe("Optional locked product / main subject applied before launch so the AI cannot invent an " + "unrelated product."),
|
|
32220
32341
|
product_prompt: exports_external.string().optional().describe("Optional freeform brief to set/replace before launch.")
|
|
32221
32342
|
},
|
|
@@ -32266,7 +32387,7 @@ registerTool("create_editor_draft", {
|
|
|
32266
32387
|
inputSchema: {
|
|
32267
32388
|
product_prompt: exports_external.string().min(10).max(5000).optional().describe("Brief for the ad — 10–5000 chars, or omit entirely"),
|
|
32268
32389
|
product_subject: exports_external.string().max(300).optional().describe("The concrete product / main subject the video must be about. Hard-grounds the AI so it cannot " + "invent an unrelated product. Recommended for ads."),
|
|
32269
|
-
language: exports_external.
|
|
32390
|
+
language: exports_external.enum(LANGUAGES).optional().describe('Language code, e.g. "en" (default). Bare ISO-639-1 code only — no region/script subtag like ' + '"en-US". Drives scenario, narration, voice, and captions.'),
|
|
32270
32391
|
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Narrative arc / segment structure. Omit for Auto/generic."),
|
|
32271
32392
|
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("Visual style direction and render look. Good default: kinetic_creator. When set it drives the look and a theme of 'none' is ignored."),
|
|
32272
32393
|
theme: exports_external.string().optional().describe('Visual theme / genre overlay (default "realistic"). One of: none (no imposed style — segments follow your prompts literally), realistic, cinematic, anime, sci_fi, fantasy, noir, superhero, horror, mockumentary, sports, gaming, retro_80s, minimalist, cyberpunk.'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubfluencer/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Monocursive <contact@monocursive.com>",
|
package/src/index.ts
CHANGED
|
@@ -463,6 +463,26 @@ const VISUAL_LANGUAGES = [
|
|
|
463
463
|
"startup_explainer",
|
|
464
464
|
"luxury_minimal",
|
|
465
465
|
] as const;
|
|
466
|
+
// The languages the API's VideoFactory.language accepts (video_factory.ex
|
|
467
|
+
// @supported_languages, validate_inclusion at create + autopilot). It's a CLOSED
|
|
468
|
+
// set of bare ISO-639-1 codes — the server rejects region/script subtags ("en",
|
|
469
|
+
// never "en-US"/"pt-BR") — so every tool that writes a factory language exposes
|
|
470
|
+
// this exact enum rather than a free-form string.
|
|
471
|
+
const LANGUAGES = [
|
|
472
|
+
"en",
|
|
473
|
+
"fr",
|
|
474
|
+
"es",
|
|
475
|
+
"de",
|
|
476
|
+
"it",
|
|
477
|
+
"nl",
|
|
478
|
+
"pl",
|
|
479
|
+
"pt",
|
|
480
|
+
"ru",
|
|
481
|
+
"zh",
|
|
482
|
+
"ja",
|
|
483
|
+
"ko",
|
|
484
|
+
"ar",
|
|
485
|
+
] as const;
|
|
466
486
|
|
|
467
487
|
const RO = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
|
|
468
488
|
const WRITE = {
|
|
@@ -652,10 +672,11 @@ registerTool(
|
|
|
652
672
|
"EDITOR only: social_ad for promotional content (product focus + CTA) or creative_story (default).",
|
|
653
673
|
),
|
|
654
674
|
language: z
|
|
655
|
-
.
|
|
675
|
+
.enum(LANGUAGES)
|
|
656
676
|
.optional()
|
|
657
677
|
.describe(
|
|
658
|
-
'Language
|
|
678
|
+
'Language (default "en"). Bare ISO-639-1 code only — no region/script subtag ' +
|
|
679
|
+
'(use "pt", never "pt-BR"/"en-US"). Drives scenario, narration, voice, and captions.',
|
|
659
680
|
),
|
|
660
681
|
aspect: z
|
|
661
682
|
.string()
|
|
@@ -711,6 +732,9 @@ registerTool(
|
|
|
711
732
|
),
|
|
712
733
|
star_rating: z
|
|
713
734
|
.number()
|
|
735
|
+
.int()
|
|
736
|
+
.min(0)
|
|
737
|
+
.max(5)
|
|
714
738
|
.optional()
|
|
715
739
|
.describe(
|
|
716
740
|
"SHORTS only: optional 0..5 star rating under the subheadline",
|
|
@@ -721,6 +745,7 @@ registerTool(
|
|
|
721
745
|
.describe("SHORTS only: title overlay position (default bottom)"),
|
|
722
746
|
text_animation: z
|
|
723
747
|
.enum([
|
|
748
|
+
"auto",
|
|
724
749
|
"reveal",
|
|
725
750
|
"typewriter",
|
|
726
751
|
"fade_in",
|
|
@@ -730,12 +755,17 @@ registerTool(
|
|
|
730
755
|
"word_spotlight",
|
|
731
756
|
])
|
|
732
757
|
.optional()
|
|
733
|
-
.describe(
|
|
758
|
+
.describe(
|
|
759
|
+
"SHORTS only: title overlay animation. Defaults to auto (follow the visual_language's own " +
|
|
760
|
+
"animation). Every concrete value (including fade_in) is an explicit pick that always wins.",
|
|
761
|
+
),
|
|
734
762
|
font_family: z
|
|
735
763
|
.string()
|
|
736
764
|
.optional()
|
|
737
765
|
.describe(
|
|
738
|
-
"SHORTS only: overlay font
|
|
766
|
+
"SHORTS only: overlay font. Defaults to auto (follow the visual_language's typography), or pass " +
|
|
767
|
+
"one of the 15 ShortFont* faces (ShortFontSpaceGrotesk, ShortFontAnton, …). Every concrete face " +
|
|
768
|
+
"is an explicit pick that always wins.",
|
|
739
769
|
),
|
|
740
770
|
music_vibe: z
|
|
741
771
|
.string()
|
|
@@ -743,6 +773,12 @@ registerTool(
|
|
|
743
773
|
.describe(
|
|
744
774
|
"SHORTS only: music mood — Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz",
|
|
745
775
|
),
|
|
776
|
+
music_instruments: z
|
|
777
|
+
.array(z.string())
|
|
778
|
+
.optional()
|
|
779
|
+
.describe(
|
|
780
|
+
'SHORTS only: instrument hints woven into the music (≤10, each ≤40 chars), e.g. ["piano", "strings"]',
|
|
781
|
+
),
|
|
746
782
|
creative_format: z
|
|
747
783
|
.enum(CREATIVE_FORMATS)
|
|
748
784
|
.optional()
|
|
@@ -816,6 +852,7 @@ registerTool(
|
|
|
816
852
|
text_animation?: string;
|
|
817
853
|
font_family?: string;
|
|
818
854
|
music_vibe?: string;
|
|
855
|
+
music_instruments?: string[];
|
|
819
856
|
creative_format?: string;
|
|
820
857
|
visual_language?: string;
|
|
821
858
|
theme?: string;
|
|
@@ -865,6 +902,16 @@ registerTool(
|
|
|
865
902
|
return fail("each text_beats entry must be at most 120 characters.");
|
|
866
903
|
}
|
|
867
904
|
}
|
|
905
|
+
if (args.music_instruments !== undefined) {
|
|
906
|
+
if (args.music_instruments.length > 10) {
|
|
907
|
+
return fail("music_instruments must have at most 10 entries.");
|
|
908
|
+
}
|
|
909
|
+
if (args.music_instruments.some((i) => i.length > 40)) {
|
|
910
|
+
return fail(
|
|
911
|
+
"each music_instruments entry must be at most 40 characters.",
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
868
915
|
|
|
869
916
|
// Validate the save_path up front so we fail before spending credits.
|
|
870
917
|
if (args.save_path) resolveSavePath(args.save_path);
|
|
@@ -897,6 +944,7 @@ registerTool(
|
|
|
897
944
|
short_text_animation: args.text_animation,
|
|
898
945
|
short_font_family: args.font_family,
|
|
899
946
|
music_vibe: args.music_vibe,
|
|
947
|
+
music_instruments: args.music_instruments,
|
|
900
948
|
creative_format: args.creative_format,
|
|
901
949
|
visual_language: args.visual_language,
|
|
902
950
|
theme: args.theme,
|
|
@@ -922,6 +970,7 @@ registerTool(
|
|
|
922
970
|
args.text_animation ?? "",
|
|
923
971
|
args.font_family ?? "",
|
|
924
972
|
args.music_vibe ?? "",
|
|
973
|
+
JSON.stringify(args.music_instruments ?? []),
|
|
925
974
|
args.creative_format ?? "",
|
|
926
975
|
args.visual_language ?? "",
|
|
927
976
|
args.theme ?? "",
|
|
@@ -1184,10 +1233,10 @@ registerTool(
|
|
|
1184
1233
|
const cap = Math.min(100, Math.max(1, args.limit ?? 25));
|
|
1185
1234
|
const [shortsRaw, factoriesRaw] = await Promise.all([
|
|
1186
1235
|
client
|
|
1187
|
-
.get<{ data: unknown }>(
|
|
1188
|
-
"
|
|
1189
|
-
|
|
1190
|
-
)
|
|
1236
|
+
.get<{ data: unknown }>("/shorts", {
|
|
1237
|
+
...(args.include_completed ? { include_completed: "true" } : {}),
|
|
1238
|
+
per_page: String(cap),
|
|
1239
|
+
})
|
|
1191
1240
|
.catch(() => ({ data: [] })),
|
|
1192
1241
|
client
|
|
1193
1242
|
.get<{ data: unknown }>("/video-factories")
|
|
@@ -1244,9 +1293,11 @@ registerTool(
|
|
|
1244
1293
|
.min(10)
|
|
1245
1294
|
.describe("What the short should be about"),
|
|
1246
1295
|
language: z
|
|
1247
|
-
.
|
|
1296
|
+
.enum(LANGUAGES)
|
|
1248
1297
|
.optional()
|
|
1249
|
-
.describe(
|
|
1298
|
+
.describe(
|
|
1299
|
+
'Language code, e.g. "en" (default). Bare ISO-639-1 code only — no region/script subtag like "en-US".',
|
|
1300
|
+
),
|
|
1250
1301
|
headline: z
|
|
1251
1302
|
.string()
|
|
1252
1303
|
.max(160)
|
|
@@ -1286,15 +1337,19 @@ registerTool(
|
|
|
1286
1337
|
"Background-music mood. Recognized: Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz.",
|
|
1287
1338
|
),
|
|
1288
1339
|
music_instruments: z
|
|
1289
|
-
.array(z.string())
|
|
1340
|
+
.array(z.string().max(40))
|
|
1341
|
+
.max(10)
|
|
1290
1342
|
.optional()
|
|
1291
|
-
.describe(
|
|
1343
|
+
.describe(
|
|
1344
|
+
'Optional instrument hints (≤10, each ≤40 chars), e.g. ["piano", "strings"]',
|
|
1345
|
+
),
|
|
1292
1346
|
text_position: z
|
|
1293
1347
|
.enum(["top", "center", "bottom"])
|
|
1294
1348
|
.optional()
|
|
1295
1349
|
.describe("Where the title overlay sits (default bottom)"),
|
|
1296
1350
|
text_animation: z
|
|
1297
1351
|
.enum([
|
|
1352
|
+
"auto",
|
|
1298
1353
|
"reveal",
|
|
1299
1354
|
"typewriter",
|
|
1300
1355
|
"fade_in",
|
|
@@ -1304,12 +1359,16 @@ registerTool(
|
|
|
1304
1359
|
"word_spotlight",
|
|
1305
1360
|
])
|
|
1306
1361
|
.optional()
|
|
1307
|
-
.describe(
|
|
1362
|
+
.describe(
|
|
1363
|
+
"Title overlay animation. Defaults to auto (follow the visual_language's own animation). " +
|
|
1364
|
+
"Every concrete value (including fade_in) is an explicit pick that always wins.",
|
|
1365
|
+
),
|
|
1308
1366
|
font_family: z
|
|
1309
1367
|
.string()
|
|
1310
1368
|
.optional()
|
|
1311
1369
|
.describe(
|
|
1312
|
-
"Overlay font (
|
|
1370
|
+
"Overlay font. Defaults to auto (follow the visual_language's typography). Every concrete face " +
|
|
1371
|
+
"is an explicit pick that always wins. One of: auto, ShortFontSpaceGrotesk, ShortFontMontserrat, " +
|
|
1313
1372
|
"ShortFontTheBold, ShortFontImpact, ShortFontLato, ShortFontAnton, " +
|
|
1314
1373
|
"ShortFontBebasNeue, ShortFontOswald, ShortFontArchivoBlack, ShortFontPoppins, ShortFontInter, " +
|
|
1315
1374
|
"ShortFontTikTokSans, ShortFontBangers, ShortFontDMSerif, ShortFontPermanentMarker.",
|
|
@@ -1343,6 +1402,7 @@ registerTool(
|
|
|
1343
1402
|
.describe('Optional badge stamp, e.g. "BEST SELLER"'),
|
|
1344
1403
|
star_rating: z
|
|
1345
1404
|
.number()
|
|
1405
|
+
.int()
|
|
1346
1406
|
.min(0)
|
|
1347
1407
|
.max(5)
|
|
1348
1408
|
.optional()
|
|
@@ -1354,6 +1414,15 @@ registerTool(
|
|
|
1354
1414
|
.describe(
|
|
1355
1415
|
"Optional caption beats shown sequentially instead of the static subheadline.",
|
|
1356
1416
|
),
|
|
1417
|
+
brand_profile_id: z
|
|
1418
|
+
.number()
|
|
1419
|
+
.int()
|
|
1420
|
+
.optional()
|
|
1421
|
+
.describe(
|
|
1422
|
+
"Seed brand defaults — colors/font/CTA — from a saved brand profile (id from " +
|
|
1423
|
+
"list_brand_profiles). Its creative format, visual language, font, accent, and default CTA fill " +
|
|
1424
|
+
"any blank short fields and a snapshot is frozen onto the short. Must be one of your own profiles.",
|
|
1425
|
+
),
|
|
1357
1426
|
},
|
|
1358
1427
|
outputSchema: createDraftOutput,
|
|
1359
1428
|
annotations: { title: "Create short", ...WRITE, idempotentHint: true },
|
|
@@ -1381,6 +1450,7 @@ registerTool(
|
|
|
1381
1450
|
badge_text?: string;
|
|
1382
1451
|
star_rating?: number;
|
|
1383
1452
|
text_beats?: string[];
|
|
1453
|
+
brand_profile_id?: number;
|
|
1384
1454
|
},
|
|
1385
1455
|
client,
|
|
1386
1456
|
) => {
|
|
@@ -1416,6 +1486,8 @@ registerTool(
|
|
|
1416
1486
|
if (args.badge_text !== undefined) body.badge_text = args.badge_text;
|
|
1417
1487
|
if (args.star_rating !== undefined) body.star_rating = args.star_rating;
|
|
1418
1488
|
if (args.text_beats !== undefined) body.text_beats = args.text_beats;
|
|
1489
|
+
if (args.brand_profile_id !== undefined)
|
|
1490
|
+
body.brand_profile_id = args.brand_profile_id;
|
|
1419
1491
|
const res = await client.post<{ data: { slug: string } }>(
|
|
1420
1492
|
"/shorts",
|
|
1421
1493
|
body,
|
|
@@ -1441,6 +1513,7 @@ registerTool(
|
|
|
1441
1513
|
args.badge_text ?? "",
|
|
1442
1514
|
String(args.star_rating ?? ""),
|
|
1443
1515
|
JSON.stringify(args.text_beats ?? []),
|
|
1516
|
+
String(args.brand_profile_id ?? ""),
|
|
1444
1517
|
),
|
|
1445
1518
|
);
|
|
1446
1519
|
return ok({
|
|
@@ -1452,6 +1525,242 @@ registerTool(
|
|
|
1452
1525
|
),
|
|
1453
1526
|
);
|
|
1454
1527
|
|
|
1528
|
+
registerTool(
|
|
1529
|
+
"update_short",
|
|
1530
|
+
{
|
|
1531
|
+
title: "Update a short draft (0 credits)",
|
|
1532
|
+
description:
|
|
1533
|
+
"Patches an existing short draft — only the fields you pass change; omitted fields are left untouched. " +
|
|
1534
|
+
"0 credits, no re-render (call generate_short afterwards to apply). Use this to edit the brief, copy, " +
|
|
1535
|
+
"styling, or the optional conversion graphics after create_short. " +
|
|
1536
|
+
"CLEARING a field: the conversion graphics (offer_text, cta_text, badge_text, star_rating) and " +
|
|
1537
|
+
'text_beats are OPT-IN and OFF by default — pass "" (or 0 for star_rating, [] for text_beats) to REMOVE ' +
|
|
1538
|
+
"one that was set. The four conversion graphics are user-supplied only and never written by the AI copy " +
|
|
1539
|
+
"assist; text_beats (like headline/subheadline) IS rewritten by generate_short_text, so hand-crafted " +
|
|
1540
|
+
"beats don't survive a later assist call. " +
|
|
1541
|
+
"404 for an unknown/foreign slug.",
|
|
1542
|
+
inputSchema: {
|
|
1543
|
+
slug: z.string().describe("Short slug (from create_short)"),
|
|
1544
|
+
product_prompt: z
|
|
1545
|
+
.string()
|
|
1546
|
+
.min(10)
|
|
1547
|
+
.optional()
|
|
1548
|
+
.describe(
|
|
1549
|
+
"Rewrite the brief (what the short is about, ≥10 chars); drives the next generate_short.",
|
|
1550
|
+
),
|
|
1551
|
+
language: z
|
|
1552
|
+
.enum(LANGUAGES)
|
|
1553
|
+
.optional()
|
|
1554
|
+
.describe(
|
|
1555
|
+
'Language code, e.g. "en". Bare ISO-639-1 code only — no region/script subtag like "en-US".',
|
|
1556
|
+
),
|
|
1557
|
+
headline: z
|
|
1558
|
+
.string()
|
|
1559
|
+
.max(160)
|
|
1560
|
+
.optional()
|
|
1561
|
+
.describe("On-screen TITLE overlay. ≤160 chars."),
|
|
1562
|
+
subheadline: z
|
|
1563
|
+
.string()
|
|
1564
|
+
.max(200)
|
|
1565
|
+
.optional()
|
|
1566
|
+
.describe("Secondary title / supporting line. ≤200 chars."),
|
|
1567
|
+
creative_format: z
|
|
1568
|
+
.enum(CREATIVE_FORMATS)
|
|
1569
|
+
.optional()
|
|
1570
|
+
.describe(
|
|
1571
|
+
"Structure: problem_solution, mistake_fix, myth_vs_reality, before_after, proof_demo, product_reveal.",
|
|
1572
|
+
),
|
|
1573
|
+
visual_language: z
|
|
1574
|
+
.enum(VISUAL_LANGUAGES)
|
|
1575
|
+
.optional()
|
|
1576
|
+
.describe("Visual language for Veo direction and render styling."),
|
|
1577
|
+
theme: z
|
|
1578
|
+
.string()
|
|
1579
|
+
.optional()
|
|
1580
|
+
.describe(
|
|
1581
|
+
"Deprecated for shorts: legacy visual theme used only when visual_language is unset.",
|
|
1582
|
+
),
|
|
1583
|
+
music_vibe: z
|
|
1584
|
+
.string()
|
|
1585
|
+
.optional()
|
|
1586
|
+
.describe(
|
|
1587
|
+
"Background-music mood: Upbeat, Cinematic, Minimal, Luxury, Playful, Jazz.",
|
|
1588
|
+
),
|
|
1589
|
+
music_instruments: z
|
|
1590
|
+
.array(z.string().max(40))
|
|
1591
|
+
.max(10)
|
|
1592
|
+
.optional()
|
|
1593
|
+
.describe(
|
|
1594
|
+
'Instrument hints (≤10, each ≤40 chars), e.g. ["piano", "strings"]',
|
|
1595
|
+
),
|
|
1596
|
+
text_position: z
|
|
1597
|
+
.enum(["top", "center", "bottom"])
|
|
1598
|
+
.optional()
|
|
1599
|
+
.describe("Where the title overlay sits."),
|
|
1600
|
+
text_animation: z
|
|
1601
|
+
.enum([
|
|
1602
|
+
"auto",
|
|
1603
|
+
"reveal",
|
|
1604
|
+
"typewriter",
|
|
1605
|
+
"fade_in",
|
|
1606
|
+
"pop",
|
|
1607
|
+
"bounce",
|
|
1608
|
+
"word_stagger",
|
|
1609
|
+
"word_spotlight",
|
|
1610
|
+
])
|
|
1611
|
+
.optional()
|
|
1612
|
+
.describe(
|
|
1613
|
+
"Title overlay animation. Defaults to auto (follow the visual_language's own animation). " +
|
|
1614
|
+
"Every concrete value (including fade_in) is an explicit pick that always wins.",
|
|
1615
|
+
),
|
|
1616
|
+
font_family: z
|
|
1617
|
+
.string()
|
|
1618
|
+
.optional()
|
|
1619
|
+
.describe(
|
|
1620
|
+
"Overlay font. Defaults to auto (follow the visual_language's typography). Every concrete face " +
|
|
1621
|
+
"is an explicit pick that always wins. One of: auto, ShortFontSpaceGrotesk, ShortFontMontserrat, ShortFontTheBold, ShortFontImpact, " +
|
|
1622
|
+
"ShortFontLato, ShortFontAnton, ShortFontBebasNeue, ShortFontOswald, ShortFontArchivoBlack, " +
|
|
1623
|
+
"ShortFontPoppins, ShortFontInter, ShortFontTikTokSans, ShortFontBangers, ShortFontDMSerif, " +
|
|
1624
|
+
"ShortFontPermanentMarker.",
|
|
1625
|
+
),
|
|
1626
|
+
headline_color: z
|
|
1627
|
+
.string()
|
|
1628
|
+
.optional()
|
|
1629
|
+
.describe("Headline hex color, e.g. #ffffff"),
|
|
1630
|
+
subheadline_color: z
|
|
1631
|
+
.string()
|
|
1632
|
+
.optional()
|
|
1633
|
+
.describe("Subheadline hex color, e.g. #ffffff"),
|
|
1634
|
+
accent_color: z
|
|
1635
|
+
.string()
|
|
1636
|
+
.optional()
|
|
1637
|
+
.describe("6-digit brand accent hex color, e.g. #09EFBE"),
|
|
1638
|
+
offer_text: z
|
|
1639
|
+
.string()
|
|
1640
|
+
.max(16)
|
|
1641
|
+
.optional()
|
|
1642
|
+
.describe('Offer chip (opt-in), e.g. "-40%". Pass "" to remove it.'),
|
|
1643
|
+
cta_text: z
|
|
1644
|
+
.string()
|
|
1645
|
+
.max(24)
|
|
1646
|
+
.optional()
|
|
1647
|
+
.describe('CTA pill (opt-in), e.g. "Shop now". Pass "" to remove it.'),
|
|
1648
|
+
badge_text: z
|
|
1649
|
+
.string()
|
|
1650
|
+
.max(24)
|
|
1651
|
+
.optional()
|
|
1652
|
+
.describe(
|
|
1653
|
+
'Badge stamp (opt-in), e.g. "BEST SELLER". Pass "" to remove it.',
|
|
1654
|
+
),
|
|
1655
|
+
star_rating: z
|
|
1656
|
+
.number()
|
|
1657
|
+
.int()
|
|
1658
|
+
.min(0)
|
|
1659
|
+
.max(5)
|
|
1660
|
+
.optional()
|
|
1661
|
+
.describe(
|
|
1662
|
+
"0..5 star rating under the subheadline (opt-in). Pass 0 to remove it.",
|
|
1663
|
+
),
|
|
1664
|
+
text_beats: z
|
|
1665
|
+
.array(z.string().max(120))
|
|
1666
|
+
.max(8)
|
|
1667
|
+
.optional()
|
|
1668
|
+
.describe(
|
|
1669
|
+
"Caption beats shown sequentially instead of the static subheadline. Pass [] to clear.",
|
|
1670
|
+
),
|
|
1671
|
+
brand_profile_id: z
|
|
1672
|
+
.number()
|
|
1673
|
+
.int()
|
|
1674
|
+
.optional()
|
|
1675
|
+
.describe(
|
|
1676
|
+
"Seed brand defaults — colors/font/CTA — from a saved brand profile (id from " +
|
|
1677
|
+
"list_brand_profiles). Best set at create_short time (that path also freezes a brand snapshot); " +
|
|
1678
|
+
"on update it sets the linked profile. Must be one of your own profiles.",
|
|
1679
|
+
),
|
|
1680
|
+
},
|
|
1681
|
+
annotations: {
|
|
1682
|
+
title: "Update short",
|
|
1683
|
+
...WRITE,
|
|
1684
|
+
// PATCH by slug → replaying the same fields converges to the same state.
|
|
1685
|
+
idempotentHint: true,
|
|
1686
|
+
},
|
|
1687
|
+
},
|
|
1688
|
+
tool(
|
|
1689
|
+
async (
|
|
1690
|
+
args: {
|
|
1691
|
+
slug: string;
|
|
1692
|
+
product_prompt?: string;
|
|
1693
|
+
language?: string;
|
|
1694
|
+
headline?: string;
|
|
1695
|
+
subheadline?: string;
|
|
1696
|
+
creative_format?: string;
|
|
1697
|
+
visual_language?: string;
|
|
1698
|
+
theme?: string;
|
|
1699
|
+
music_vibe?: string;
|
|
1700
|
+
music_instruments?: string[];
|
|
1701
|
+
text_position?: string;
|
|
1702
|
+
text_animation?: string;
|
|
1703
|
+
font_family?: string;
|
|
1704
|
+
headline_color?: string;
|
|
1705
|
+
subheadline_color?: string;
|
|
1706
|
+
accent_color?: string;
|
|
1707
|
+
offer_text?: string;
|
|
1708
|
+
cta_text?: string;
|
|
1709
|
+
badge_text?: string;
|
|
1710
|
+
star_rating?: number;
|
|
1711
|
+
text_beats?: string[];
|
|
1712
|
+
brand_profile_id?: number;
|
|
1713
|
+
},
|
|
1714
|
+
client,
|
|
1715
|
+
) => {
|
|
1716
|
+
// Only forward keys the caller actually passed. A present "" / [] / 0 is
|
|
1717
|
+
// intentional — the API's update is present-aware, so it clears that
|
|
1718
|
+
// field (the four conversion graphics + beats are opt-in and this is
|
|
1719
|
+
// how an agent turns one back off). Field names map onto the API's
|
|
1720
|
+
// short_* params exactly as create_short does.
|
|
1721
|
+
const body: Record<string, unknown> = {};
|
|
1722
|
+
if (args.product_prompt !== undefined)
|
|
1723
|
+
body.product_prompt = args.product_prompt;
|
|
1724
|
+
if (args.language !== undefined) body.language = args.language;
|
|
1725
|
+
if (args.headline !== undefined) body.headline = args.headline;
|
|
1726
|
+
if (args.subheadline !== undefined) body.subheadline = args.subheadline;
|
|
1727
|
+
if (args.creative_format !== undefined)
|
|
1728
|
+
body.creative_format = args.creative_format;
|
|
1729
|
+
if (args.visual_language !== undefined)
|
|
1730
|
+
body.visual_language = args.visual_language;
|
|
1731
|
+
if (args.theme !== undefined) body.theme = args.theme;
|
|
1732
|
+
if (args.music_vibe !== undefined) body.music_vibe = args.music_vibe;
|
|
1733
|
+
if (args.music_instruments !== undefined)
|
|
1734
|
+
body.music_instruments = args.music_instruments;
|
|
1735
|
+
if (args.text_position !== undefined)
|
|
1736
|
+
body.short_text_position = args.text_position;
|
|
1737
|
+
if (args.text_animation !== undefined)
|
|
1738
|
+
body.short_text_animation = args.text_animation;
|
|
1739
|
+
if (args.font_family !== undefined)
|
|
1740
|
+
body.short_font_family = args.font_family;
|
|
1741
|
+
if (args.headline_color !== undefined)
|
|
1742
|
+
body.headline_color = args.headline_color;
|
|
1743
|
+
if (args.subheadline_color !== undefined)
|
|
1744
|
+
body.subheadline_color = args.subheadline_color;
|
|
1745
|
+
if (args.accent_color !== undefined)
|
|
1746
|
+
body.accent_color = args.accent_color;
|
|
1747
|
+
if (args.offer_text !== undefined) body.offer_text = args.offer_text;
|
|
1748
|
+
if (args.cta_text !== undefined) body.cta_text = args.cta_text;
|
|
1749
|
+
if (args.badge_text !== undefined) body.badge_text = args.badge_text;
|
|
1750
|
+
if (args.star_rating !== undefined) body.star_rating = args.star_rating;
|
|
1751
|
+
if (args.text_beats !== undefined) body.text_beats = args.text_beats;
|
|
1752
|
+
if (args.brand_profile_id !== undefined)
|
|
1753
|
+
body.brand_profile_id = args.brand_profile_id;
|
|
1754
|
+
|
|
1755
|
+
const res = await client.patch<{ data: unknown }>(
|
|
1756
|
+
`/shorts/${args.slug}`,
|
|
1757
|
+
body,
|
|
1758
|
+
);
|
|
1759
|
+
return ok(asRecord(res).data ?? res);
|
|
1760
|
+
},
|
|
1761
|
+
),
|
|
1762
|
+
);
|
|
1763
|
+
|
|
1455
1764
|
interface TrackingData {
|
|
1456
1765
|
slug: string;
|
|
1457
1766
|
status: string;
|
|
@@ -2292,11 +2601,12 @@ registerTool(
|
|
|
2292
2601
|
"(default).",
|
|
2293
2602
|
),
|
|
2294
2603
|
language: z
|
|
2295
|
-
.
|
|
2604
|
+
.enum(LANGUAGES)
|
|
2296
2605
|
.optional()
|
|
2297
2606
|
.describe(
|
|
2298
|
-
'Language
|
|
2299
|
-
"
|
|
2607
|
+
'Language (default "en"). Bare ISO-639-1 code only — no region/script subtag (use "es", not "es-MX"). ' +
|
|
2608
|
+
"Drives the WHOLE video: scenario, narration script, narration voice, and captions. " +
|
|
2609
|
+
"Ask the user / infer from their locale.",
|
|
2300
2610
|
),
|
|
2301
2611
|
creative_format: z
|
|
2302
2612
|
.enum(CREATIVE_FORMATS)
|
|
@@ -2399,11 +2709,11 @@ registerTool(
|
|
|
2399
2709
|
inputSchema: {
|
|
2400
2710
|
slug: z.string().describe("Editor project slug"),
|
|
2401
2711
|
language: z
|
|
2402
|
-
.
|
|
2712
|
+
.enum(LANGUAGES)
|
|
2403
2713
|
.optional()
|
|
2404
2714
|
.describe(
|
|
2405
|
-
'Optional language override applied before launch
|
|
2406
|
-
"narration, voice, and captions.",
|
|
2715
|
+
'Optional language override applied before launch. Bare ISO-639-1 code only, e.g. "fr" — no ' +
|
|
2716
|
+
"region/script subtag. Drives scenario, narration, voice, and captions.",
|
|
2407
2717
|
),
|
|
2408
2718
|
product_subject: z
|
|
2409
2719
|
.string()
|
|
@@ -2545,12 +2855,11 @@ registerTool(
|
|
|
2545
2855
|
"invent an unrelated product. Recommended for ads.",
|
|
2546
2856
|
),
|
|
2547
2857
|
language: z
|
|
2548
|
-
.
|
|
2549
|
-
.min(2)
|
|
2550
|
-
.max(10)
|
|
2858
|
+
.enum(LANGUAGES)
|
|
2551
2859
|
.optional()
|
|
2552
2860
|
.describe(
|
|
2553
|
-
'Language code, e.g. "en" (default).
|
|
2861
|
+
'Language code, e.g. "en" (default). Bare ISO-639-1 code only — no region/script subtag like ' +
|
|
2862
|
+
'"en-US". Drives scenario, narration, voice, and captions.',
|
|
2554
2863
|
),
|
|
2555
2864
|
creative_format: z
|
|
2556
2865
|
.enum(CREATIVE_FORMATS)
|