@hubfluencer/mcp 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.js +48 -41
- package/package.json +1 -1
- package/src/index.ts +117 -43
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ claude mcp add hubfluencer --env HUBFLUENCER_API_TOKEN=YOUR_TOKEN -- npx -y @hub
|
|
|
67
67
|
| `create_short` / `generate_short` / `generate_short_text` | Create a short draft (0 credits) / generate editable overlay copy (1 AI assist) / render it (15 credits) |
|
|
68
68
|
| `create_slider` / `generate_slider` / `get_slider` | Create an image carousel draft (0 credits) / render it (1 credit per slide, 3–10) / read the per-slide image URLs + caption + hashtags. One prompt → N still slides + ready-to-post text. |
|
|
69
69
|
| `restyle_slider` / `edit_slider_slide` | Re-composite a **completed** carousel for **free** (0 credits): swap the template/accent on every slide, or rewrite one slide's headline/body/kicker. Reuses the AI backgrounds; poll `get_slider` until `completed`. |
|
|
70
|
-
| `create_editor_ad` | Create an editor project **and** run autopilot end-to-end |
|
|
70
|
+
| `create_editor_ad` | Create an editor project **and** run autopilot end-to-end. Style it with `creative_format` (narrative arc) + `visual_language` (render look) + `theme` (genre overlay). |
|
|
71
71
|
| `start_autopilot` | Run autopilot on an **existing** editor draft (e.g. resume a `make_video` `dry_run`, or after topping up) |
|
|
72
72
|
| `get_status` / `wait_for_completion` | Normalized `{stage, terminal, ready, video_url, error}`; block-poll until terminal (bounded) |
|
|
73
73
|
| `download_result` | Get (or save) the finished MP4 URL |
|
|
@@ -77,8 +77,8 @@ claude mcp add hubfluencer --env HUBFLUENCER_API_TOKEN=YOUR_TOKEN -- npx -y @hub
|
|
|
77
77
|
|
|
78
78
|
| Tool | What it does |
|
|
79
79
|
|---|---|
|
|
80
|
-
| `create_editor_draft` | Editor project, no autopilot (0 credits) |
|
|
81
|
-
| `generate_scenario` / `set_scenario` | AI-draft the scenario (1 assist) **or** write your own (free) |
|
|
80
|
+
| `create_editor_draft` | Editor project, no autopilot (0 credits). Accepts `creative_format` / `visual_language` / `theme`. |
|
|
81
|
+
| `generate_scenario` / `set_scenario` | AI-draft the scenario (1 assist) **or** write your own (free). `generate_scenario` can also persist `creative_format` / `visual_language` / `theme`. |
|
|
82
82
|
| `get_editor` | Full project state — the review step |
|
|
83
83
|
| `set_scene_count` | Grow/shrink to N scenes (1–20) |
|
|
84
84
|
| `set_segment_prompt` / `generate_segment` / `generate_all_segments` | Write a scene prompt (free); render one (5 credits) or every scene in order |
|
package/dist/index.js
CHANGED
|
@@ -29988,13 +29988,29 @@ function tool(fn) {
|
|
|
29988
29988
|
}
|
|
29989
29989
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
29990
29990
|
var kindSchema = exports_external.enum(["short", "editor"]).describe("Project kind");
|
|
29991
|
+
var CREATIVE_FORMATS = [
|
|
29992
|
+
"problem_solution",
|
|
29993
|
+
"mistake_fix",
|
|
29994
|
+
"myth_vs_reality",
|
|
29995
|
+
"before_after",
|
|
29996
|
+
"proof_demo",
|
|
29997
|
+
"product_reveal"
|
|
29998
|
+
];
|
|
29999
|
+
var VISUAL_LANGUAGES = [
|
|
30000
|
+
"kinetic_creator",
|
|
30001
|
+
"premium_editorial",
|
|
30002
|
+
"cinematic_product",
|
|
30003
|
+
"ugc_realism",
|
|
30004
|
+
"startup_explainer",
|
|
30005
|
+
"luxury_minimal"
|
|
30006
|
+
];
|
|
29991
30007
|
var RO = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
|
|
29992
30008
|
var WRITE = {
|
|
29993
30009
|
readOnlyHint: false,
|
|
29994
30010
|
destructiveHint: false,
|
|
29995
30011
|
openWorldHint: true
|
|
29996
30012
|
};
|
|
29997
|
-
var server = new McpServer({ name: "hubfluencer", version: "0.
|
|
30013
|
+
var server = new McpServer({ name: "hubfluencer", version: "0.5.0" });
|
|
29998
30014
|
var registerTool = server.registerTool.bind(server);
|
|
29999
30015
|
async function pollToTerminal(client, kind, slug, extra, budgetMs, intervalMs) {
|
|
30000
30016
|
const deadline = Date.now() + budgetMs;
|
|
@@ -30035,7 +30051,7 @@ async function pollSegmentToTerminal(client, slug, segmentId, extra, budgetMs, i
|
|
|
30035
30051
|
}
|
|
30036
30052
|
registerTool("make_video", {
|
|
30037
30053
|
title: "Make a video from a prompt (one shot)",
|
|
30038
|
-
description: "The simplest path: give a prompt, get a finished MP4. Creates the project (free), PRICES it against " + "your live credit balance, then — only if it's affordable and within max_credits — starts generation, " + "polls to completion (emitting progress), and (if save_path is given) downloads the result. " + "Spends credits (15 for a short; a multi-scene editor ad ~28). Pass dry_run:true to preview the cost " + "WITHOUT charging (returns {estimated_credits, available_credits, slug}); pass max_credits to cap the " + "spend. kind defaults to 'auto' — a multi-scene editor ad for ad/promo/story briefs, a single-clip short " + "for simple/short ones; the chosen kind is reported back as kind_inferred. If it returns terminal=false " + "the render is still running — call wait_for_completion with the returned slug to finish. " + "BE PROACTIVE WITH BRANDING: pass headline (the on-screen TITLE) and subheadline (secondary title) plus " + "music_vibe
|
|
30054
|
+
description: "The simplest path: give a prompt, get a finished MP4. Creates the project (free), PRICES it against " + "your live credit balance, then — only if it's affordable and within max_credits — starts generation, " + "polls to completion (emitting progress), and (if save_path is given) downloads the result. " + "Spends credits (15 for a short; a multi-scene editor ad ~28). Pass dry_run:true to preview the cost " + "WITHOUT charging (returns {estimated_credits, available_credits, slug}); pass max_credits to cap the " + "spend. kind defaults to 'auto' — a multi-scene editor ad for ad/promo/story briefs, a single-clip short " + "for simple/short ones; the chosen kind is reported back as kind_inferred. If it returns terminal=false " + "the render is still running — call wait_for_completion with the returned slug to finish. " + "BE PROACTIVE WITH BRANDING: pass headline (the on-screen TITLE) and subheadline (secondary title) plus " + "music_vibe so a one-shot short isn't bare. creative_format + visual_language apply to BOTH kinds (editor and " + "shorts); headline/subheadline/music_vibe/text_* and the conversion graphics are SHORTS-only and ignored for " + "editor; theme applies to editor (genre overlay) and is legacy-only for shorts (used when visual_language is unset). " + "For richer branding — a product image, brand logo, or " + "closing card — drive the granular path instead: create_short + set_short_product/set_short_poster, or " + "create_editor_draft + set_product/set_logo/set_closing_image, then start_autopilot/generate_short.",
|
|
30039
30055
|
inputSchema: {
|
|
30040
30056
|
prompt: exports_external.string().describe("What the ad/video should be about (min 10 chars)"),
|
|
30041
30057
|
kind: exports_external.string().optional().describe("'short' (fast, 1 clip), 'editor' (multi-scene), or 'auto' (default — inferred)"),
|
|
@@ -30064,23 +30080,9 @@ registerTool("make_video", {
|
|
|
30064
30080
|
]).optional().describe("SHORTS only: title overlay animation (default fade_in)"),
|
|
30065
30081
|
font_family: exports_external.string().optional().describe("SHORTS only: overlay font family, e.g. ShortFontSpaceGrotesk"),
|
|
30066
30082
|
music_vibe: exports_external.string().optional().describe("SHORTS only: music mood — Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz"),
|
|
30067
|
-
creative_format: exports_external.enum(
|
|
30068
|
-
|
|
30069
|
-
|
|
30070
|
-
"myth_vs_reality",
|
|
30071
|
-
"before_after",
|
|
30072
|
-
"proof_demo",
|
|
30073
|
-
"product_reveal"
|
|
30074
|
-
]).optional().describe("SHORTS only: segment structure. Omit for Auto/generic."),
|
|
30075
|
-
visual_language: exports_external.enum([
|
|
30076
|
-
"kinetic_creator",
|
|
30077
|
-
"premium_editorial",
|
|
30078
|
-
"cinematic_product",
|
|
30079
|
-
"ugc_realism",
|
|
30080
|
-
"startup_explainer",
|
|
30081
|
-
"luxury_minimal"
|
|
30082
|
-
]).optional().describe("SHORTS only: visual style direction and render look. Default app choice is kinetic_creator."),
|
|
30083
|
-
theme: exports_external.string().optional().describe("Visual theme: editor style, and legacy shorts fallback only when visual_language is unset. " + "none (literal — no imposed style), realistic (default), cinematic, " + "anime, sci_fi, fantasy, noir, superhero, horror, mockumentary, sports, gaming, retro_80s, " + "minimalist, cyberpunk"),
|
|
30083
|
+
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("SHORTS + EDITOR: narrative arc / segment structure. Omit for Auto/generic."),
|
|
30084
|
+
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("SHORTS + EDITOR: visual style direction and render look. Default app choice is kinetic_creator."),
|
|
30085
|
+
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"),
|
|
30084
30086
|
save_path: exports_external.string().optional().describe("Optional .mp4 path to download to (confined to HUBFLUENCER_OUTPUT_DIR or cwd)"),
|
|
30085
30087
|
max_wait_seconds: exports_external.number().optional().describe("Block budget seconds (default 240, capped 10–280)"),
|
|
30086
30088
|
dry_run: exports_external.boolean().optional().describe("Preview only: create a free draft, price it, and STOP before spending credits. " + "Returns {estimated_credits, available_credits, slug}. Resume with generate_short / start_autopilot."),
|
|
@@ -30127,8 +30129,10 @@ registerTool("make_video", {
|
|
|
30127
30129
|
product_prompt: args.prompt,
|
|
30128
30130
|
export_aspect_ratio: args.aspect,
|
|
30129
30131
|
voice_id: args.voice_id,
|
|
30132
|
+
creative_format: args.creative_format,
|
|
30133
|
+
visual_language: args.visual_language,
|
|
30130
30134
|
theme: args.theme
|
|
30131
|
-
}, idemKey("make-editor", args.prompt, args.language ?? "en", args.aspect ?? "", args.voice_id ?? "", args.theme ?? ""));
|
|
30135
|
+
}, idemKey("make-editor", args.prompt, args.language ?? "en", args.aspect ?? "", args.voice_id ?? "", args.creative_format ?? "", args.visual_language ?? "", args.theme ?? ""));
|
|
30132
30136
|
slug = created.data.slug;
|
|
30133
30137
|
}
|
|
30134
30138
|
const costPath = kind === "short" ? `/shorts/${slug}/cost` : `/editor/${slug}/autopilot/cost`;
|
|
@@ -30243,22 +30247,8 @@ registerTool("create_short", {
|
|
|
30243
30247
|
language: exports_external.string().optional().describe('Language code, e.g. "en" (default)'),
|
|
30244
30248
|
headline: exports_external.string().max(160).optional().describe("The on-screen TITLE composited over the short (poster text overlay). ≤160 chars. Set this."),
|
|
30245
30249
|
subheadline: exports_external.string().max(200).optional().describe("The SECONDARY title / supporting line under the headline. ≤200 chars."),
|
|
30246
|
-
creative_format: exports_external.enum(
|
|
30247
|
-
|
|
30248
|
-
"mistake_fix",
|
|
30249
|
-
"myth_vs_reality",
|
|
30250
|
-
"before_after",
|
|
30251
|
-
"proof_demo",
|
|
30252
|
-
"product_reveal"
|
|
30253
|
-
]).optional().describe("Optional structure: problem_solution, mistake_fix, myth_vs_reality, before_after, proof_demo, product_reveal. Omit for Auto."),
|
|
30254
|
-
visual_language: exports_external.enum([
|
|
30255
|
-
"kinetic_creator",
|
|
30256
|
-
"premium_editorial",
|
|
30257
|
-
"cinematic_product",
|
|
30258
|
-
"ugc_realism",
|
|
30259
|
-
"startup_explainer",
|
|
30260
|
-
"luxury_minimal"
|
|
30261
|
-
]).optional().describe("Visual language for Veo direction and render styling. Good default: kinetic_creator."),
|
|
30250
|
+
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."),
|
|
30251
|
+
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("Visual language for Veo direction and render styling. Good default: kinetic_creator."),
|
|
30262
30252
|
theme: exports_external.string().optional().describe("Deprecated for shorts: legacy visual theme used only when visual_language is unset."),
|
|
30263
30253
|
music_vibe: exports_external.string().optional().describe("Background-music mood. Recognized: Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz."),
|
|
30264
30254
|
music_instruments: exports_external.array(exports_external.string()).optional().describe('Optional instrument hints, e.g. ["piano", "strings"]'),
|
|
@@ -30564,7 +30554,9 @@ registerTool("create_editor_ad", {
|
|
|
30564
30554
|
inputSchema: {
|
|
30565
30555
|
product_prompt: exports_external.string().min(10).describe("Brief for the ad (min 10 chars)"),
|
|
30566
30556
|
language: exports_external.string().optional().describe('Language code, e.g. "en" (default)'),
|
|
30567
|
-
|
|
30557
|
+
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Narrative arc / segment structure. Omit for Auto/generic."),
|
|
30558
|
+
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."),
|
|
30559
|
+
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.'),
|
|
30568
30560
|
voice_id: exports_external.string().optional().describe("Preferred narration voice id (see list_voices); omit for the default voice"),
|
|
30569
30561
|
export_aspect_ratio: exports_external.enum(["9:16", "16:9", "1:1"]).optional().describe('Aspect ratio (default "9:16")')
|
|
30570
30562
|
},
|
|
@@ -30573,10 +30565,12 @@ registerTool("create_editor_ad", {
|
|
|
30573
30565
|
const created = await client.post("/editor", {
|
|
30574
30566
|
language: args.language ?? "en",
|
|
30575
30567
|
product_prompt: args.product_prompt,
|
|
30568
|
+
creative_format: args.creative_format,
|
|
30569
|
+
visual_language: args.visual_language,
|
|
30576
30570
|
theme: args.theme,
|
|
30577
30571
|
voice_id: args.voice_id,
|
|
30578
30572
|
export_aspect_ratio: args.export_aspect_ratio
|
|
30579
|
-
}, idemKey("create-editor", String(args.product_prompt ?? "")));
|
|
30573
|
+
}, idemKey("create-editor", String(args.product_prompt ?? ""), String(args.language ?? "en"), String(args.creative_format ?? ""), String(args.visual_language ?? ""), String(args.theme ?? ""), String(args.voice_id ?? ""), String(args.export_aspect_ratio ?? "")));
|
|
30580
30574
|
const slug = created.data.slug;
|
|
30581
30575
|
const started = await client.post(`/editor/${slug}/autopilot`, undefined, `autopilot:${slug}`);
|
|
30582
30576
|
const status = normalizeStatus("editor", slug, asRecord(started).data);
|
|
@@ -30619,7 +30613,9 @@ registerTool("create_editor_draft", {
|
|
|
30619
30613
|
inputSchema: {
|
|
30620
30614
|
product_prompt: exports_external.string().min(10).max(5000).optional().describe("Brief for the ad — 10–5000 chars, or omit entirely"),
|
|
30621
30615
|
language: exports_external.string().min(2).max(10).optional().describe('Language code, e.g. "en" (default)'),
|
|
30622
|
-
|
|
30616
|
+
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Narrative arc / segment structure. Omit for Auto/generic."),
|
|
30617
|
+
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."),
|
|
30618
|
+
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.'),
|
|
30623
30619
|
voice_id: exports_external.string().regex(/^[A-Za-z0-9_-]+$/).max(64).optional().describe("Preferred narration voice id (see list_voices); ≤64 chars, [A-Za-z0-9_-] only"),
|
|
30624
30620
|
export_aspect_ratio: exports_external.enum(["9:16", "16:9", "1:1"]).optional().describe('Aspect ratio (default "9:16")'),
|
|
30625
30621
|
project_intent: exports_external.enum(["social_ad", "creative_story"]).optional().describe("Project intent (optional)")
|
|
@@ -30633,6 +30629,8 @@ registerTool("create_editor_draft", {
|
|
|
30633
30629
|
const prompt = args.product_prompt?.trim();
|
|
30634
30630
|
const body = {
|
|
30635
30631
|
language: args.language ?? "en",
|
|
30632
|
+
creative_format: args.creative_format,
|
|
30633
|
+
visual_language: args.visual_language,
|
|
30636
30634
|
theme: args.theme,
|
|
30637
30635
|
voice_id: args.voice_id,
|
|
30638
30636
|
export_aspect_ratio: args.export_aspect_ratio,
|
|
@@ -30640,7 +30638,7 @@ registerTool("create_editor_draft", {
|
|
|
30640
30638
|
};
|
|
30641
30639
|
if (prompt)
|
|
30642
30640
|
body.product_prompt = prompt;
|
|
30643
|
-
const created = await client.post("/editor", body, idemKey("create-editor-draft", prompt ?? "", args.language ?? "en"));
|
|
30641
|
+
const created = await client.post("/editor", body, idemKey("create-editor-draft", prompt ?? "", args.language ?? "en", args.creative_format ?? "", args.visual_language ?? "", args.theme ?? ""));
|
|
30644
30642
|
return ok({
|
|
30645
30643
|
slug: created.data.slug,
|
|
30646
30644
|
kind: "editor",
|
|
@@ -30670,10 +30668,13 @@ registerTool("set_scenario", {
|
|
|
30670
30668
|
}));
|
|
30671
30669
|
registerTool("generate_scenario", {
|
|
30672
30670
|
title: "Generate the editor scenario (AI assist)",
|
|
30673
|
-
description: "Generates a scenario with AI. CONSUMES 1 AI ASSIST (free daily quota; check get_ai_assists). On 429 " + "(quota exhausted) either set auto_unlock:true to spend 1 credit for +10 assists and retry once, or " + "write the scenario yourself with set_scenario. Server default segments_count is 5.",
|
|
30671
|
+
description: "Generates a scenario with AI. CONSUMES 1 AI ASSIST (free daily quota; check get_ai_assists). On 429 " + "(quota exhausted) either set auto_unlock:true to spend 1 credit for +10 assists and retry once, or " + "write the scenario yourself with set_scenario. Server default segments_count is 5. " + "Optionally pass creative_format / visual_language / theme to persist the project’s creative style before " + "generating (this is the granular path’s way to set/change style; PATCH /scenario does not).",
|
|
30674
30672
|
inputSchema: {
|
|
30675
30673
|
slug: exports_external.string().describe("Editor project slug"),
|
|
30676
30674
|
segments_count: exports_external.number().int().min(3).max(10).optional().describe("How many scenes (3–10, server default 5)"),
|
|
30675
|
+
creative_format: exports_external.enum(CREATIVE_FORMATS).optional().describe("Persist the narrative arc / segment structure before generating. Omit to leave it unchanged; pass an empty string to clear."),
|
|
30676
|
+
visual_language: exports_external.enum(VISUAL_LANGUAGES).optional().describe("Persist the visual style / render look before generating. When set it drives the look and a theme of 'none' is ignored."),
|
|
30677
|
+
theme: exports_external.string().optional().describe("Persist the genre overlay / theme before generating (e.g. realistic, cinematic, anime, none)."),
|
|
30677
30678
|
auto_unlock: exports_external.boolean().optional().describe("On 429, spend 1 credit to unlock +10 assists and retry once (default false)")
|
|
30678
30679
|
},
|
|
30679
30680
|
annotations: {
|
|
@@ -30685,6 +30686,12 @@ registerTool("generate_scenario", {
|
|
|
30685
30686
|
const body = {};
|
|
30686
30687
|
if (args.segments_count !== undefined)
|
|
30687
30688
|
body.segments_count = args.segments_count;
|
|
30689
|
+
if (args.creative_format !== undefined)
|
|
30690
|
+
body.creative_format = args.creative_format;
|
|
30691
|
+
if (args.visual_language !== undefined)
|
|
30692
|
+
body.visual_language = args.visual_language;
|
|
30693
|
+
if (args.theme !== undefined)
|
|
30694
|
+
body.theme = args.theme;
|
|
30688
30695
|
const res = await withAssist(client, args.auto_unlock ?? false, () => client.post(`/editor/${args.slug}/generate-scenario`, body));
|
|
30689
30696
|
return ok(asRecord(res).data ?? res);
|
|
30690
30697
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubfluencer/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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
|
@@ -317,6 +317,27 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
|
317
317
|
|
|
318
318
|
const kindSchema = z.enum(["short", "editor"]).describe("Project kind");
|
|
319
319
|
|
|
320
|
+
// Creative-style value lists shared by SHORTS and EDITOR (single source of
|
|
321
|
+
// truth, mirroring the API's VideoFactory.creative_formats/0 +
|
|
322
|
+
// visual_languages/0). creative_format = narrative arc; visual_language =
|
|
323
|
+
// production treatment / render look.
|
|
324
|
+
const CREATIVE_FORMATS = [
|
|
325
|
+
"problem_solution",
|
|
326
|
+
"mistake_fix",
|
|
327
|
+
"myth_vs_reality",
|
|
328
|
+
"before_after",
|
|
329
|
+
"proof_demo",
|
|
330
|
+
"product_reveal",
|
|
331
|
+
] as const;
|
|
332
|
+
const VISUAL_LANGUAGES = [
|
|
333
|
+
"kinetic_creator",
|
|
334
|
+
"premium_editorial",
|
|
335
|
+
"cinematic_product",
|
|
336
|
+
"ugc_realism",
|
|
337
|
+
"startup_explainer",
|
|
338
|
+
"luxury_minimal",
|
|
339
|
+
] as const;
|
|
340
|
+
|
|
320
341
|
const RO = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
|
|
321
342
|
const WRITE = {
|
|
322
343
|
readOnlyHint: false,
|
|
@@ -324,7 +345,7 @@ const WRITE = {
|
|
|
324
345
|
openWorldHint: true,
|
|
325
346
|
};
|
|
326
347
|
|
|
327
|
-
const server = new McpServer({ name: "hubfluencer", version: "0.
|
|
348
|
+
const server = new McpServer({ name: "hubfluencer", version: "0.5.0" });
|
|
328
349
|
|
|
329
350
|
// The SDK's `registerTool` is generic over the Zod input shape; with this many
|
|
330
351
|
// tools its conditional types hit TS2589 ("excessively deep"). Inputs are still
|
|
@@ -427,8 +448,10 @@ registerTool(
|
|
|
427
448
|
"for simple/short ones; the chosen kind is reported back as kind_inferred. If it returns terminal=false " +
|
|
428
449
|
"the render is still running — call wait_for_completion with the returned slug to finish. " +
|
|
429
450
|
"BE PROACTIVE WITH BRANDING: pass headline (the on-screen TITLE) and subheadline (secondary title) plus " +
|
|
430
|
-
"music_vibe
|
|
431
|
-
"
|
|
451
|
+
"music_vibe so a one-shot short isn't bare. creative_format + visual_language apply to BOTH kinds (editor and " +
|
|
452
|
+
"shorts); headline/subheadline/music_vibe/text_* and the conversion graphics are SHORTS-only and ignored for " +
|
|
453
|
+
"editor; theme applies to editor (genre overlay) and is legacy-only for shorts (used when visual_language is unset). " +
|
|
454
|
+
"For richer branding — a product image, brand logo, or " +
|
|
432
455
|
"closing card — drive the granular path instead: create_short + set_short_product/set_short_poster, or " +
|
|
433
456
|
"create_editor_draft + set_product/set_logo/set_closing_image, then start_autopilot/generate_short.",
|
|
434
457
|
// Schema kept intentionally flat (no .min/.max/.int chains) — the SDK's
|
|
@@ -535,34 +558,23 @@ registerTool(
|
|
|
535
558
|
"SHORTS only: music mood — Upbeat (default), Cinematic, Minimal, Luxury, Playful, Jazz",
|
|
536
559
|
),
|
|
537
560
|
creative_format: z
|
|
538
|
-
.enum(
|
|
539
|
-
"problem_solution",
|
|
540
|
-
"mistake_fix",
|
|
541
|
-
"myth_vs_reality",
|
|
542
|
-
"before_after",
|
|
543
|
-
"proof_demo",
|
|
544
|
-
"product_reveal",
|
|
545
|
-
])
|
|
561
|
+
.enum(CREATIVE_FORMATS)
|
|
546
562
|
.optional()
|
|
547
|
-
.describe(
|
|
563
|
+
.describe(
|
|
564
|
+
"SHORTS + EDITOR: narrative arc / segment structure. Omit for Auto/generic.",
|
|
565
|
+
),
|
|
548
566
|
visual_language: z
|
|
549
|
-
.enum(
|
|
550
|
-
"kinetic_creator",
|
|
551
|
-
"premium_editorial",
|
|
552
|
-
"cinematic_product",
|
|
553
|
-
"ugc_realism",
|
|
554
|
-
"startup_explainer",
|
|
555
|
-
"luxury_minimal",
|
|
556
|
-
])
|
|
567
|
+
.enum(VISUAL_LANGUAGES)
|
|
557
568
|
.optional()
|
|
558
569
|
.describe(
|
|
559
|
-
"SHORTS
|
|
570
|
+
"SHORTS + EDITOR: visual style direction and render look. Default app choice is kinetic_creator.",
|
|
560
571
|
),
|
|
561
572
|
theme: z
|
|
562
573
|
.string()
|
|
563
574
|
.optional()
|
|
564
575
|
.describe(
|
|
565
|
-
"Visual theme: editor
|
|
576
|
+
"Visual theme: editor genre overlay, and legacy shorts fallback only when visual_language is unset. " +
|
|
577
|
+
"For editor, when visual_language is set it drives the look and 'none' is ignored. " +
|
|
566
578
|
"none (literal — no imposed style), realistic (default), cinematic, " +
|
|
567
579
|
"anime, sci_fi, fantasy, noir, superhero, horror, mockumentary, sports, gaming, retro_80s, " +
|
|
568
580
|
"minimalist, cyberpunk",
|
|
@@ -708,6 +720,8 @@ registerTool(
|
|
|
708
720
|
product_prompt: args.prompt,
|
|
709
721
|
export_aspect_ratio: args.aspect,
|
|
710
722
|
voice_id: args.voice_id,
|
|
723
|
+
creative_format: args.creative_format,
|
|
724
|
+
visual_language: args.visual_language,
|
|
711
725
|
theme: args.theme,
|
|
712
726
|
},
|
|
713
727
|
idemKey(
|
|
@@ -716,6 +730,8 @@ registerTool(
|
|
|
716
730
|
args.language ?? "en",
|
|
717
731
|
args.aspect ?? "",
|
|
718
732
|
args.voice_id ?? "",
|
|
733
|
+
args.creative_format ?? "",
|
|
734
|
+
args.visual_language ?? "",
|
|
719
735
|
args.theme ?? "",
|
|
720
736
|
),
|
|
721
737
|
);
|
|
@@ -995,27 +1011,13 @@ registerTool(
|
|
|
995
1011
|
"The SECONDARY title / supporting line under the headline. ≤200 chars.",
|
|
996
1012
|
),
|
|
997
1013
|
creative_format: z
|
|
998
|
-
.enum(
|
|
999
|
-
"problem_solution",
|
|
1000
|
-
"mistake_fix",
|
|
1001
|
-
"myth_vs_reality",
|
|
1002
|
-
"before_after",
|
|
1003
|
-
"proof_demo",
|
|
1004
|
-
"product_reveal",
|
|
1005
|
-
])
|
|
1014
|
+
.enum(CREATIVE_FORMATS)
|
|
1006
1015
|
.optional()
|
|
1007
1016
|
.describe(
|
|
1008
1017
|
"Optional structure: problem_solution, mistake_fix, myth_vs_reality, before_after, proof_demo, product_reveal. Omit for Auto.",
|
|
1009
1018
|
),
|
|
1010
1019
|
visual_language: z
|
|
1011
|
-
.enum(
|
|
1012
|
-
"kinetic_creator",
|
|
1013
|
-
"premium_editorial",
|
|
1014
|
-
"cinematic_product",
|
|
1015
|
-
"ugc_realism",
|
|
1016
|
-
"startup_explainer",
|
|
1017
|
-
"luxury_minimal",
|
|
1018
|
-
])
|
|
1020
|
+
.enum(VISUAL_LANGUAGES)
|
|
1019
1021
|
.optional()
|
|
1020
1022
|
.describe(
|
|
1021
1023
|
"Visual language for Veo direction and render styling. Good default: kinetic_creator.",
|
|
@@ -1652,11 +1654,21 @@ registerTool(
|
|
|
1652
1654
|
.string()
|
|
1653
1655
|
.optional()
|
|
1654
1656
|
.describe('Language code, e.g. "en" (default)'),
|
|
1657
|
+
creative_format: z
|
|
1658
|
+
.enum(CREATIVE_FORMATS)
|
|
1659
|
+
.optional()
|
|
1660
|
+
.describe("Narrative arc / segment structure. Omit for Auto/generic."),
|
|
1661
|
+
visual_language: z
|
|
1662
|
+
.enum(VISUAL_LANGUAGES)
|
|
1663
|
+
.optional()
|
|
1664
|
+
.describe(
|
|
1665
|
+
"Visual style direction and render look. Good default: kinetic_creator. When set it drives the look and a theme of 'none' is ignored.",
|
|
1666
|
+
),
|
|
1655
1667
|
theme: z
|
|
1656
1668
|
.string()
|
|
1657
1669
|
.optional()
|
|
1658
1670
|
.describe(
|
|
1659
|
-
'Visual theme (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.',
|
|
1671
|
+
'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.',
|
|
1660
1672
|
),
|
|
1661
1673
|
voice_id: z
|
|
1662
1674
|
.string()
|
|
@@ -1677,11 +1689,24 @@ registerTool(
|
|
|
1677
1689
|
{
|
|
1678
1690
|
language: args.language ?? "en",
|
|
1679
1691
|
product_prompt: args.product_prompt,
|
|
1692
|
+
creative_format: args.creative_format,
|
|
1693
|
+
visual_language: args.visual_language,
|
|
1680
1694
|
theme: args.theme,
|
|
1681
1695
|
voice_id: args.voice_id,
|
|
1682
1696
|
export_aspect_ratio: args.export_aspect_ratio,
|
|
1683
1697
|
},
|
|
1684
|
-
|
|
1698
|
+
// Fold every create-affecting param into the key so two calls that
|
|
1699
|
+
// differ only by style/theme/voice/aspect get distinct drafts.
|
|
1700
|
+
idemKey(
|
|
1701
|
+
"create-editor",
|
|
1702
|
+
String(args.product_prompt ?? ""),
|
|
1703
|
+
String(args.language ?? "en"),
|
|
1704
|
+
String(args.creative_format ?? ""),
|
|
1705
|
+
String(args.visual_language ?? ""),
|
|
1706
|
+
String(args.theme ?? ""),
|
|
1707
|
+
String(args.voice_id ?? ""),
|
|
1708
|
+
String(args.export_aspect_ratio ?? ""),
|
|
1709
|
+
),
|
|
1685
1710
|
);
|
|
1686
1711
|
const slug = created.data.slug;
|
|
1687
1712
|
const started = await client.post<{ data: unknown }>(
|
|
@@ -1799,11 +1824,21 @@ registerTool(
|
|
|
1799
1824
|
.max(10)
|
|
1800
1825
|
.optional()
|
|
1801
1826
|
.describe('Language code, e.g. "en" (default)'),
|
|
1827
|
+
creative_format: z
|
|
1828
|
+
.enum(CREATIVE_FORMATS)
|
|
1829
|
+
.optional()
|
|
1830
|
+
.describe("Narrative arc / segment structure. Omit for Auto/generic."),
|
|
1831
|
+
visual_language: z
|
|
1832
|
+
.enum(VISUAL_LANGUAGES)
|
|
1833
|
+
.optional()
|
|
1834
|
+
.describe(
|
|
1835
|
+
"Visual style direction and render look. Good default: kinetic_creator. When set it drives the look and a theme of 'none' is ignored.",
|
|
1836
|
+
),
|
|
1802
1837
|
theme: z
|
|
1803
1838
|
.string()
|
|
1804
1839
|
.optional()
|
|
1805
1840
|
.describe(
|
|
1806
|
-
'Visual theme (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.',
|
|
1841
|
+
'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.',
|
|
1807
1842
|
),
|
|
1808
1843
|
voice_id: z
|
|
1809
1844
|
.string()
|
|
@@ -1833,6 +1868,8 @@ registerTool(
|
|
|
1833
1868
|
args: {
|
|
1834
1869
|
product_prompt?: string;
|
|
1835
1870
|
language?: string;
|
|
1871
|
+
creative_format?: string;
|
|
1872
|
+
visual_language?: string;
|
|
1836
1873
|
theme?: string;
|
|
1837
1874
|
voice_id?: string;
|
|
1838
1875
|
export_aspect_ratio?: string;
|
|
@@ -1843,6 +1880,8 @@ registerTool(
|
|
|
1843
1880
|
const prompt = args.product_prompt?.trim();
|
|
1844
1881
|
const body: Record<string, unknown> = {
|
|
1845
1882
|
language: args.language ?? "en",
|
|
1883
|
+
creative_format: args.creative_format,
|
|
1884
|
+
visual_language: args.visual_language,
|
|
1846
1885
|
theme: args.theme,
|
|
1847
1886
|
voice_id: args.voice_id,
|
|
1848
1887
|
export_aspect_ratio: args.export_aspect_ratio,
|
|
@@ -1852,7 +1891,14 @@ registerTool(
|
|
|
1852
1891
|
const created = await client.post<{ data: { slug: string } }>(
|
|
1853
1892
|
"/editor",
|
|
1854
1893
|
body,
|
|
1855
|
-
idemKey(
|
|
1894
|
+
idemKey(
|
|
1895
|
+
"create-editor-draft",
|
|
1896
|
+
prompt ?? "",
|
|
1897
|
+
args.language ?? "en",
|
|
1898
|
+
args.creative_format ?? "",
|
|
1899
|
+
args.visual_language ?? "",
|
|
1900
|
+
args.theme ?? "",
|
|
1901
|
+
),
|
|
1856
1902
|
);
|
|
1857
1903
|
return ok({
|
|
1858
1904
|
slug: created.data.slug,
|
|
@@ -1913,7 +1959,9 @@ registerTool(
|
|
|
1913
1959
|
description:
|
|
1914
1960
|
"Generates a scenario with AI. CONSUMES 1 AI ASSIST (free daily quota; check get_ai_assists). On 429 " +
|
|
1915
1961
|
"(quota exhausted) either set auto_unlock:true to spend 1 credit for +10 assists and retry once, or " +
|
|
1916
|
-
"write the scenario yourself with set_scenario. Server default segments_count is 5."
|
|
1962
|
+
"write the scenario yourself with set_scenario. Server default segments_count is 5. " +
|
|
1963
|
+
"Optionally pass creative_format / visual_language / theme to persist the project\u2019s creative style before " +
|
|
1964
|
+
"generating (this is the granular path\u2019s way to set/change style; PATCH /scenario does not).",
|
|
1917
1965
|
inputSchema: {
|
|
1918
1966
|
slug: z.string().describe("Editor project slug"),
|
|
1919
1967
|
segments_count: z
|
|
@@ -1923,6 +1971,24 @@ registerTool(
|
|
|
1923
1971
|
.max(10)
|
|
1924
1972
|
.optional()
|
|
1925
1973
|
.describe("How many scenes (3–10, server default 5)"),
|
|
1974
|
+
creative_format: z
|
|
1975
|
+
.enum(CREATIVE_FORMATS)
|
|
1976
|
+
.optional()
|
|
1977
|
+
.describe(
|
|
1978
|
+
"Persist the narrative arc / segment structure before generating. Omit to leave it unchanged; pass an empty string to clear.",
|
|
1979
|
+
),
|
|
1980
|
+
visual_language: z
|
|
1981
|
+
.enum(VISUAL_LANGUAGES)
|
|
1982
|
+
.optional()
|
|
1983
|
+
.describe(
|
|
1984
|
+
"Persist the visual style / render look before generating. When set it drives the look and a theme of 'none' is ignored.",
|
|
1985
|
+
),
|
|
1986
|
+
theme: z
|
|
1987
|
+
.string()
|
|
1988
|
+
.optional()
|
|
1989
|
+
.describe(
|
|
1990
|
+
"Persist the genre overlay / theme before generating (e.g. realistic, cinematic, anime, none).",
|
|
1991
|
+
),
|
|
1926
1992
|
auto_unlock: z
|
|
1927
1993
|
.boolean()
|
|
1928
1994
|
.optional()
|
|
@@ -1941,6 +2007,9 @@ registerTool(
|
|
|
1941
2007
|
args: {
|
|
1942
2008
|
slug: string;
|
|
1943
2009
|
segments_count?: number;
|
|
2010
|
+
creative_format?: string;
|
|
2011
|
+
visual_language?: string;
|
|
2012
|
+
theme?: string;
|
|
1944
2013
|
auto_unlock?: boolean;
|
|
1945
2014
|
},
|
|
1946
2015
|
client,
|
|
@@ -1948,6 +2017,11 @@ registerTool(
|
|
|
1948
2017
|
const body: Record<string, unknown> = {};
|
|
1949
2018
|
if (args.segments_count !== undefined)
|
|
1950
2019
|
body.segments_count = args.segments_count;
|
|
2020
|
+
if (args.creative_format !== undefined)
|
|
2021
|
+
body.creative_format = args.creative_format;
|
|
2022
|
+
if (args.visual_language !== undefined)
|
|
2023
|
+
body.visual_language = args.visual_language;
|
|
2024
|
+
if (args.theme !== undefined) body.theme = args.theme;
|
|
1951
2025
|
const res = await withAssist(client, args.auto_unlock ?? false, () =>
|
|
1952
2026
|
client.post<{ data: unknown }>(
|
|
1953
2027
|
`/editor/${args.slug}/generate-scenario`,
|