@hubfluencer/mcp 0.16.0 → 0.18.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 +101 -45
- package/dist/index.js +12988 -7820
- package/package.json +12 -6
- package/server.json +56 -0
- package/skill/hubfluencer-create/SKILL.md +169 -0
- package/skill/hubfluencer-create/references/full-guide.md +228 -0
- package/src/client.ts +2 -2
- package/src/core.ts +303 -431
- package/src/doctor.ts +96 -0
- package/src/generation-summary.ts +258 -0
- package/src/index.ts +947 -312
- package/src/output-schemas.ts +85 -12
- package/src/polling.ts +18 -5
- package/src/skill-installer.ts +100 -0
- package/src/tool-profile.ts +92 -0
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubfluencer/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
|
+
"mcpName": "io.github.monocursive/hubfluencer",
|
|
4
5
|
"description": "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"author": "Monocursive <contact@monocursive.com>",
|
|
@@ -19,6 +20,8 @@
|
|
|
19
20
|
"main": "./dist/index.js",
|
|
20
21
|
"files": [
|
|
21
22
|
"dist",
|
|
23
|
+
"skill",
|
|
24
|
+
"server.json",
|
|
22
25
|
"src",
|
|
23
26
|
"tsconfig.json",
|
|
24
27
|
"README.md",
|
|
@@ -32,15 +35,16 @@
|
|
|
32
35
|
"test": "bun test",
|
|
33
36
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
34
37
|
"lint": "biome check src",
|
|
35
|
-
"release": "bun run typecheck && bun run lint && bun test && npm publish --access public"
|
|
38
|
+
"release": "bun run typecheck && bun run lint && bun test && npm publish --access public",
|
|
39
|
+
"release:registry": "mcp-publisher publish"
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
42
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
-
"zod": "
|
|
43
|
+
"zod": "4.3.6"
|
|
40
44
|
},
|
|
41
45
|
"devDependencies": {
|
|
42
|
-
"@biomejs/biome": "^2.
|
|
43
|
-
"@types/node": "^22.
|
|
46
|
+
"@biomejs/biome": "^2.5.4",
|
|
47
|
+
"@types/node": "^22.20.1",
|
|
44
48
|
"typescript": "^5.9.3"
|
|
45
49
|
},
|
|
46
50
|
"engines": {
|
|
@@ -53,6 +57,8 @@
|
|
|
53
57
|
"ai",
|
|
54
58
|
"video",
|
|
55
59
|
"ads",
|
|
56
|
-
"claude"
|
|
60
|
+
"claude",
|
|
61
|
+
"codex",
|
|
62
|
+
"agent-skills"
|
|
57
63
|
]
|
|
58
64
|
}
|
package/server.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.monocursive/hubfluencer",
|
|
4
|
+
"title": "Hubfluencer",
|
|
5
|
+
"description": "Create videos, carousels, campaigns, and content operations with Hubfluencer.",
|
|
6
|
+
"websiteUrl": "https://hubfluencer.com/developers",
|
|
7
|
+
"repository": {
|
|
8
|
+
"url": "https://github.com/monocursive/monohub",
|
|
9
|
+
"source": "github",
|
|
10
|
+
"subfolder": "packages/mcp"
|
|
11
|
+
},
|
|
12
|
+
"version": "0.18.0",
|
|
13
|
+
"packages": [
|
|
14
|
+
{
|
|
15
|
+
"registryType": "npm",
|
|
16
|
+
"identifier": "@hubfluencer/mcp",
|
|
17
|
+
"version": "0.18.0",
|
|
18
|
+
"transport": {
|
|
19
|
+
"type": "stdio"
|
|
20
|
+
},
|
|
21
|
+
"packageArguments": [
|
|
22
|
+
{
|
|
23
|
+
"type": "positional",
|
|
24
|
+
"value": "--profile"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"type": "positional",
|
|
28
|
+
"value": "creator"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"environmentVariables": [
|
|
32
|
+
{
|
|
33
|
+
"name": "HUBFLUENCER_API_TOKEN",
|
|
34
|
+
"description": "Scoped Hubfluencer access token; optional after device-link login.",
|
|
35
|
+
"format": "string",
|
|
36
|
+
"isRequired": false,
|
|
37
|
+
"isSecret": true
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "HUBFLUENCER_INPUT_DIR",
|
|
41
|
+
"description": "Directory from which the agent may read local upload files.",
|
|
42
|
+
"format": "filepath",
|
|
43
|
+
"isRequired": false,
|
|
44
|
+
"isSecret": false
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "HUBFLUENCER_OUTPUT_DIR",
|
|
48
|
+
"description": "Directory in which the agent may save downloaded MP4 files.",
|
|
49
|
+
"format": "filepath",
|
|
50
|
+
"isRequired": false,
|
|
51
|
+
"isSecret": false
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hubfluencer-create
|
|
3
|
+
description: Generate finished video ads, shorts, image carousels, or tracking-overlay clips with Hubfluencer, and optionally run the surrounding content operation from product research through review, scheduling, and performance. Use whenever a user asks an agent to make, iterate, recover, or deliver social creative with Hubfluencer.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create with Hubfluencer
|
|
7
|
+
|
|
8
|
+
Use the connected `hubfluencer` MCP server. Prefer its high-level tools; load
|
|
9
|
+
`references/full-guide.md` only when the request needs granular Editor control,
|
|
10
|
+
campaign operations, asset-catalog work, recurring series, or an uncommon
|
|
11
|
+
recovery path.
|
|
12
|
+
|
|
13
|
+
The recommended `creator` MCP profile intentionally exposes only high-level
|
|
14
|
+
single-asset creation, recovery, and delivery. If an advanced tool from the
|
|
15
|
+
reference is absent, tell the user to change the server argument to
|
|
16
|
+
`--profile full` and restart the agent; do not invent a REST call as a hidden
|
|
17
|
+
substitute.
|
|
18
|
+
|
|
19
|
+
## Before creating
|
|
20
|
+
|
|
21
|
+
If Hubfluencer is not connected, tell the user to run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx -y @hubfluencer/mcp@0.18.0 login
|
|
25
|
+
npx -y @hubfluencer/mcp@0.18.0 doctor --profile creator
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Never invent, request in chat, or echo a token. A PAT may instead be supplied to
|
|
29
|
+
the MCP process as `HUBFLUENCER_API_TOKEN`.
|
|
30
|
+
|
|
31
|
+
Credits are prepaid money. Before a paid action:
|
|
32
|
+
|
|
33
|
+
1. Use `get_credits`.
|
|
34
|
+
2. Obtain a free quote or dry run.
|
|
35
|
+
3. State the credit cap and approximate dollar cost.
|
|
36
|
+
4. Spend only after the user gives explicit approval for that cap.
|
|
37
|
+
|
|
38
|
+
Draft creation and editing are free. Current charged operations are described by
|
|
39
|
+
the tool itself; do not rely on remembered prices when the returned quote differs.
|
|
40
|
+
Never unlock an exhausted AI-assist quota with a credit unless the user approves.
|
|
41
|
+
|
|
42
|
+
## Lifecycle authority
|
|
43
|
+
|
|
44
|
+
Every Editor and Short product read includes a non-null `generation_summary`.
|
|
45
|
+
Treat it as the sole lifecycle authority.
|
|
46
|
+
|
|
47
|
+
- Poll only while `generation_summary.timing.poll_after_ms` is a positive
|
|
48
|
+
integer, using that cadence. `null` means stop.
|
|
49
|
+
- Invoke only a tool advertised by
|
|
50
|
+
`generation_summary.available_actions`, including every run, revision, step,
|
|
51
|
+
or other fence required by that tool.
|
|
52
|
+
- A terminal summary may require input, review, retry, or a new start. Terminal
|
|
53
|
+
does not automatically mean downloadable.
|
|
54
|
+
- Download only when `download_result` is advertised.
|
|
55
|
+
- Product projection fields are display metadata or explicit route fences only;
|
|
56
|
+
never derive polling, retry, cancel, or download availability from `stage`,
|
|
57
|
+
child status, `autopilot_status`, or `latest_render`.
|
|
58
|
+
- `wait_for_completion` follows server timing for up to its budget. If it
|
|
59
|
+
returns `timed_out`, call it again with the same slug. If polling has stopped,
|
|
60
|
+
follow the advertised action instead of polling harder.
|
|
61
|
+
|
|
62
|
+
Editor delivery freshness is separate: an old MP4 can remain while edits make it
|
|
63
|
+
stale. When status reports `stale:true`, render again before delivery.
|
|
64
|
+
|
|
65
|
+
## Default workflow
|
|
66
|
+
|
|
67
|
+
For a straightforward request, call:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
make_video({
|
|
71
|
+
prompt: "...",
|
|
72
|
+
dry_run: true
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The dry run creates a free draft and returns a quote. After approval, call
|
|
77
|
+
`make_video` again with the returned slug or approved prompt, the quoted
|
|
78
|
+
`max_credits`, and an `.mp4` `save_path` when the user wants a local file.
|
|
79
|
+
|
|
80
|
+
Use `kind:"short"` for a fast single-clip social ad and `kind:"editor"` for a
|
|
81
|
+
multi-scene story. Leave `kind:"auto"` when the request does not decide.
|
|
82
|
+
For Shorts, include concise `headline`, `subheadline`, `cta_text`, a fitting
|
|
83
|
+
`music_vibe`, and an explicit `visual_language` when the brief supports them.
|
|
84
|
+
Do not invent an offer.
|
|
85
|
+
|
|
86
|
+
When exact duration matters for Editor, set `segments_count`; each generated
|
|
87
|
+
Editor scene is approximately eight seconds. Three scenes is a sensible concise
|
|
88
|
+
default.
|
|
89
|
+
|
|
90
|
+
## Product-specific paths
|
|
91
|
+
|
|
92
|
+
Short:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
create_short -> edit/attach assets -> generate_short
|
|
96
|
+
-> wait_for_completion -> download_result
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`generate_short` is the paid footage/music generation. After it completes,
|
|
100
|
+
`update_short` plus `rerender_short` applies text/style/CTA/end-card/logo changes
|
|
101
|
+
over the existing footage for free. A paid Short run cannot be stopped once
|
|
102
|
+
started: there is no cancel tool or route, and the draft cannot be deleted until
|
|
103
|
+
the run reaches a terminal state. A failed run still refunds its 15 credits.
|
|
104
|
+
|
|
105
|
+
Editor ad:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
create_editor_ad (free quote)
|
|
109
|
+
-> start_autopilot({slug,max_credits})
|
|
110
|
+
-> wait_for_completion -> download_result
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
A bare `start_autopilot({slug})` quotes without starting. If the summary
|
|
114
|
+
advertises `edit_required_input` or `review_spend_cap`, use
|
|
115
|
+
`provide_generation_input`. Use `retry_editor_batch`, `cancel_editor_batch`,
|
|
116
|
+
`retry_segment_generation`, or `cancel_autopilot` only when advertised.
|
|
117
|
+
|
|
118
|
+
Carousel:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
create_slider -> generate_slider -> get_slider
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Sliders deliver image URLs, caption, and hashtags; they do not use
|
|
125
|
+
`wait_for_completion` or `download_result`. Free iteration uses
|
|
126
|
+
`restyle_slider` and `edit_slider_slide`.
|
|
127
|
+
|
|
128
|
+
Tracking overlay:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
create_tracking_video({video_path,...})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This one-shot flow uploads a local clip, renders the tracking overlay, waits,
|
|
135
|
+
and optionally saves an MP4. Resume an unfinished returned slug; create a new
|
|
136
|
+
project for different classes or color after completion.
|
|
137
|
+
|
|
138
|
+
## Creative quality rules
|
|
139
|
+
|
|
140
|
+
- Production prompts are visual direction. Narration scripts are spoken
|
|
141
|
+
verbatim: never put camera notes, labels, or prompt syntax in a script.
|
|
142
|
+
- Do not ask a video model to spell essential brand names, UI, prices, offers,
|
|
143
|
+
or CTAs. Put exact copy in overlays, narration, or supplied artwork.
|
|
144
|
+
- Use a tight transparent wordmark for a logo. Use square or full-frame artwork
|
|
145
|
+
as a poster/end card. Keep important poster content in the central 9:16 area.
|
|
146
|
+
- If supplied poster art already contains its logo and CTA, set
|
|
147
|
+
`poster_includes_lockup:true` and do not add a duplicate end-card logo.
|
|
148
|
+
- Review the rendered asset as a viewer before delivery: message clarity,
|
|
149
|
+
legibility, crop, continuity, audio, CTA, and product truthfulness.
|
|
150
|
+
|
|
151
|
+
## Local files and delivery
|
|
152
|
+
|
|
153
|
+
Uploads must resolve within `HUBFLUENCER_INPUT_DIR` and downloads within
|
|
154
|
+
`HUBFLUENCER_OUTPUT_DIR`; both default to the MCP process working directory.
|
|
155
|
+
Downloaded files must be `.mp4`. Presigned links expire, so save deliverables
|
|
156
|
+
promptly. Never claim Hubfluencer auto-publishes: its schedule tools create
|
|
157
|
+
reminders, and a person still posts to social platforms.
|
|
158
|
+
|
|
159
|
+
Return the saved file or fresh result link, slug, actual credits charged, and a
|
|
160
|
+
short summary of what was made. If generation failed, report the canonical
|
|
161
|
+
failure and advertised recovery action without pretending the asset is ready.
|
|
162
|
+
|
|
163
|
+
## Load advanced guidance only when needed
|
|
164
|
+
|
|
165
|
+
Read `references/full-guide.md` before using granular scene/narration/voice/music
|
|
166
|
+
tools, campaign packs, product and brand profiles, review links, performance
|
|
167
|
+
recommendations, recurring series, scheduled posts, or reusable catalog assets.
|
|
168
|
+
For raw REST integration, use the public `/developers/api` reference instead of
|
|
169
|
+
guessing endpoint or idempotency contracts.
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hubfluencer-create
|
|
3
|
+
description: Generate a finished video ad, short, image carousel, or tracking-overlay clip with Hubfluencer — and run the surrounding content operation (plan → draft → review → schedule → measure). Use whenever the user wants to make a video — e.g. "use Hubfluencer to make a 15s ad for X", "create a short for my product", "make me a TikTok ad", "turn my product page into a campaign", "plan a month of ads for X". Drives Hubfluencer end-to-end through the @hubfluencer/mcp MCP server.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hubfluencer: create a video
|
|
7
|
+
|
|
8
|
+
You turn a plain-language request into a finished MP4 using the Hubfluencer MCP server. The user buys credits and creates an access token in the Hubfluencer app; you spend those credits to generate the video. Credits are real money — **$0.99 each** bought singly, less in bulk — so state what a generation will cost (credits + ~dollars) and get the user's go-ahead before the first paid call.
|
|
9
|
+
|
|
10
|
+
## Creative export contract
|
|
11
|
+
|
|
12
|
+
- Production prompts (`product_prompt`, `scenario_prompt`, and scene `prompt`) are visual directions. Narration scripts are read aloud verbatim, so `set_narration_script.script` must contain only the exact spoken words—never camera notes, prompt syntax, labels, or scene directions. Never copy a creative brief into `script`; use `generate_narration` or Autopilot unless the user supplied final voice-over copy.
|
|
13
|
+
- Do not rely on the video model to spell essential brand names, UI, signs, prices, offers, or CTAs. Put exact copy in Hubfluencer overlays, narration, or supplied ending artwork.
|
|
14
|
+
- A short logo position is a preferred corner: the renderer moves it away from headline, captions, CTA, offer, and badge areas. Omit `treatment` so transparent marks default to `throughout` and opaque square lockups default to `end_card`. Use a tight transparent wordmark; use square or full-bleed art as the poster instead. If poster artwork already has its logo/CTA, set `poster_includes_lockup: true` and do not add an `end_card` logo.
|
|
15
|
+
- Short posters fill the ending frame: phone-shaped portrait art is cover-cropped, while square, classic portrait, and landscape art is contained over a blurred backdrop. Editor closing images are always cover-cropped. Keep important content in the central 9:16 safe area. An Editor closing image gets no generic CTA, so put the CTA in the artwork or state it in the preceding narration/scene.
|
|
16
|
+
- Pass `segments_count` (3–10) to `create_editor_ad` when length matters. It is the exact structural target used by the cost quote and Autopilot launch.
|
|
17
|
+
- Each Editor AI scene is 8 seconds. Default a concise social ad to 3 scenes (about 24 seconds) unless the user asks for a longer story.
|
|
18
|
+
|
|
19
|
+
## Generation lifecycle authority
|
|
20
|
+
|
|
21
|
+
Every Editor and Short product read includes a non-null `generation_summary`. Treat it as the sole
|
|
22
|
+
lifecycle authority even when product projections such as `stage`, `autopilot_status`, `batch_generation_status`, child
|
|
23
|
+
statuses, or `latest_render` disagree. Poll only while
|
|
24
|
+
`generation_summary.timing.poll_after_ms` is a positive integer, at that server cadence; `null`
|
|
25
|
+
means stop polling. Read terminality from `generation_summary.status`, invoke only
|
|
26
|
+
`generation_summary.available_actions`, and download only when `download_result` is advertised for
|
|
27
|
+
a verified delivery artifact. Product projection fields are display metadata or explicit route fences only;
|
|
28
|
+
never derive polling, retry, cancel, or download availability from them.
|
|
29
|
+
|
|
30
|
+
When a stopped summary advertises `edit_required_input` or `review_spend_cap`, call
|
|
31
|
+
`provide_generation_input` with that action and its non-empty `input` object or approved
|
|
32
|
+
`max_credits`. For a failed `editor.scene.generate` or `editor.scene.regenerate` summary that
|
|
33
|
+
advertises `retry_generation`, call `retry_segment_generation { slug, segment_id }`. Both tools
|
|
34
|
+
re-read the product and echo the run/revision/step fences; never call them from product projection fields
|
|
35
|
+
or guess a fence. Re-read status after the command.
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
- The `hubfluencer` MCP server must be connected (tools like `make_video`, `get_credits`, `wait_for_completion` are available).
|
|
40
|
+
- The user must have an access token configured (via `HUBFLUENCER_API_TOKEN`, or by having run `npx -y @hubfluencer/mcp@0.18.0 login` — a scoped token is then saved to `~/.hubfluencer/credentials.json` and the server reads it automatically).
|
|
41
|
+
- **Never invent a token, and never echo a token in your output.** If no token is configured, tell the user to create one in the app (Settings → Access tokens, "Generate videos" scope) or to run `npx -y @hubfluencer/mcp@0.18.0 login`, then stop.
|
|
42
|
+
|
|
43
|
+
## Native macOS Studio
|
|
44
|
+
|
|
45
|
+
If the user connected the bearer-gated `hubfluencer-studio` localhost MCP server, use its advertised
|
|
46
|
+
local editing tools for that open project. Call `get_project_digest` first and send its exact required
|
|
47
|
+
`doc_rev` with every call. Read+edit is the default surface; generation is opt-in and always leaves a
|
|
48
|
+
visible approval card in Studio. Let the app handle explicit cloud-context disclosure—do not bypass it
|
|
49
|
+
with a direct Director REST request.
|
|
50
|
+
|
|
51
|
+
## The simple path (use this by default)
|
|
52
|
+
|
|
53
|
+
Call **`make_video({ prompt, save_path })`**. It creates the project, starts generation, polls to completion, and downloads the finished MP4 in one step. Shorts charge atomically at launch; editor Autopilot authorizes `max_credits` and spends incrementally as work starts. Unless the user already approved the spend, run `make_video({ prompt, dry_run: true })` first (free; returns the estimate + a draft `slug`), quote the credits and ~dollar cost, and generate only after a clear yes.
|
|
54
|
+
|
|
55
|
+
- `prompt`: a vivid description of the video the user wants.
|
|
56
|
+
- `save_path`: an absolute path where the MP4 should be written.
|
|
57
|
+
- `kind` defaults to `auto` — a fast single-clip **short** for simple prompts, a multi-scene **editor** ad for story / multi-scene prompts. Override only when the user is explicit: `kind: "short"` or `kind: "editor"`.
|
|
58
|
+
|
|
59
|
+
If `make_video` returns `terminal: false`, the render is still going. Call **`wait_for_completion({ slug, kind })`** again (reuse the `slug` and `kind` it returned). Generation takes a few minutes — that is normal, not a failure. For paid shorts, report `segments_completed / segments_total` and `active_segment_positions` from status; these are the authoritative clip-progress signals.
|
|
60
|
+
|
|
61
|
+
**Don't ship a bare short.** For a short, pass `headline` (the on-screen TITLE, ≈4 words / ≤40 chars), `subheadline` (secondary title), and a fitting `music_vibe` right in the `make_video` call — and **always `cta_text`** (a short without a CTA doesn't convert; add `offer_text` only if there's a real deal). Set `visual_language` explicitly (`kinetic_creator` is a strong default). These are SHORTS fields (`theme` applies to both kinds; `headline`/`subheadline`/`music_vibe` are ignored for editor). If copy is left blank the server auto-writes the headline + caption beats at generate time (`skip_auto_text: true` renders a deliberately bare clip). `make_video` cannot attach local files; use `create_editor_ad`'s asset paths for an editor, or the granular tools below for a short. If the user gave no title or assets, infer a sensible `headline`/`subheadline` from the prompt, or ask.
|
|
62
|
+
|
|
63
|
+
## The granular path (control / recovery)
|
|
64
|
+
|
|
65
|
+
Use this when you need fine control, or to recover after an interruption:
|
|
66
|
+
|
|
67
|
+
1. `get_credits` — check the balance before spending.
|
|
68
|
+
2. `create_short` **or** `create_editor_ad` — creates the draft (0 credits).
|
|
69
|
+
3. `generate_short` (for a short) **or** `start_autopilot { slug, max_credits }` (for an editor ad) — starts the render. A bare `start_autopilot { slug }` only returns a fresh quote; launch only after approval with a cap at least as high as that quote.
|
|
70
|
+
4. `wait_for_completion({ slug, kind })` — poll until done.
|
|
71
|
+
5. `download_result` — fetch the finished MP4 to disk.
|
|
72
|
+
|
|
73
|
+
Act on Editor batch recovery only when `generation_summary.available_actions` advertises
|
|
74
|
+
`retry_generation` or `cancel_generation`; then call `retry_editor_batch { slug }` or
|
|
75
|
+
`cancel_editor_batch { slug }` respectively. The MCP tools re-read the canonical summary before writing.
|
|
76
|
+
Raw REST retry callers send `generation_run_id` and `generation_revision` from that summary; cancel
|
|
77
|
+
callers send `generation_run_id`. Product batch fields are display projections, never command fences or
|
|
78
|
+
action availability. A stale 409 means re-read and issue a new command only if the action is still
|
|
79
|
+
advertised; never guess fences. Retry targets the exact failed canonical step. Only a scene named by
|
|
80
|
+
`current_step` may be repaired before retry. Resolve the child batch before starting Autopilot again;
|
|
81
|
+
retrying it does not authorize a new parent run. For an insufficient-credit blocker, top up or reduce
|
|
82
|
+
scope and follow the action in the newly read summary.
|
|
83
|
+
|
|
84
|
+
Cancellation is run-fenced. A paid short is not cancellable once `generate_short` starts, and its draft cannot be deleted until the run is terminal; a failure still refunds its 15 credits. `cancel_segment_generation { slug, segment_id, generation_claim_token }` uses the current token from `get_editor` and refunds an individually generated scene; batch/Autopilot scenes use workflow controls. `cancel_autopilot { slug }` re-reads and forwards the summary run/start fence, stopping only that parent chain while keeping completed work and already charged scenes. Re-read state after a stale fence error; never guess a fence value.
|
|
85
|
+
|
|
86
|
+
**Minimum sellable short** — check all seven before you spend:
|
|
87
|
+
|
|
88
|
+
- hook (`headline`) ≤4 words
|
|
89
|
+
- `text_beats` = hook → proof → payoff
|
|
90
|
+
- `cta_text` set (a short without a CTA doesn't convert)
|
|
91
|
+
- product image attached (`set_short_product`) AND described
|
|
92
|
+
- poster (`set_short_poster`) or closing lockup (`brand_name` / `closing_claim`) carries the ask
|
|
93
|
+
- `visual_language` set explicitly (`kinetic_creator` is a strong default)
|
|
94
|
+
- iterate: hooks are free to re-render (`update_short` → `rerender_short`, 0 credits after the first generate)
|
|
95
|
+
|
|
96
|
+
Iterate the hook before you spend: `generate_hook_variations` writes 3–6 hook options on the free assist quota. If `headline`/`subheadline`/`text_beats` are all blank, the server auto-writes the headline + caption beats at generate time (pass `skip_auto_text: true` to `generate_short` for a deliberately bare clip).
|
|
97
|
+
|
|
98
|
+
**Never re-roll — iteration is free after the first generate.** The first `generate_short` (15 credits) buys the footage + music; after that, every overlay edit re-renders over them for **0 credits**: `update_short` the fields, then `rerender_short { slug }` (REST: `POST /api/shorts/:slug/rerender`, no body, `Idempotency-Key` honored, rate limit 10/min).
|
|
99
|
+
|
|
100
|
+
- **Free to re-render:** `headline`, `subheadline`, `text_beats`, `cta_text`, `offer_text`, `badge_text`, `star_rating`, headline/subheadline/accent colors, `font_family`, `text_position`, `text_animation`, `closing_claim`, `brand_name`, poster (+ `poster_includes_lockup`), `end_card`, logo overlay (`set_short_logo` / `short_logo_position` / `short_logo_treatment`).
|
|
101
|
+
- **Needs `generate_short` again (15 credits):** `product_prompt`, product image, `creative_format`, `visual_language`, `theme`, `music_vibe`, `music_instruments`, `language`.
|
|
102
|
+
|
|
103
|
+
Edits that add/remove the end card (poster, `end_card`) can shift the video between 12s and 14s. The pinned music bed is reused: when it is shorter than the export it crossfades into a repeated continuation, then fades at the export end, without charging for regenerated music.
|
|
104
|
+
|
|
105
|
+
Iteration recipe: generate once → `update_short` a hook/CTA variant → `rerender_short` (free) → compare → repeat → keep the winner. **409** = a render is already running (keep polling); **422 `short_not_ready`** = nothing generated yet — run `generate_short` first; **422 `short_paid_regeneration_required`** = a footage/music input changed — run `generate_short` again. A re-render is never a credit error.
|
|
106
|
+
|
|
107
|
+
`update_short` returns a compact receipt (`updated`, `end_card_mode`, `resolved_cta_text`, `next`), not the full Short. Use `get_status` or the REST GET for authoritative state.
|
|
108
|
+
|
|
109
|
+
**Short creative fields** (`create_short` / `update_short`, also carried by `make_video`): `headline` (on-screen TITLE — ≈4 words; ≤40 chars hits hardest; hard cap 80), `subheadline` (secondary title, ≤200), `visual_language` (kinetic_creator / premium_editorial / cinematic_product / ugc_realism / startup_explainer / luxury_minimal — drives Veo direction AND the render's typographic identity), `creative_format` (problem_solution / mistake_fix / myth_vs_reality / before_after / proof_demo / product_reveal), `music_vibe` (Upbeat default / Cinematic / Minimal / Luxury / Playful / Jazz), `theme` (deprecated for shorts — legacy fallback when `visual_language` is unset), and fine styling `text_position` (top/center/bottom), `text_animation` (**auto** default / reveal / typewriter / fade_in / pop / bounce / word_stagger / word_spotlight), `font_family` (**auto** default, or one of 15 ShortFont* faces) — both default to `auto`, which follows the visual_language's typography; every concrete value (incl. fade_in / ShortFontSpaceGrotesk) is an explicit pick that wins — `headline_color` / `subheadline_color` / `accent_color`, `text_beats` (≤8 caption lines), `music_instruments` (≤10, each ≤40 chars), plus conversion graphics: **always set `cta_text`** (≤24, e.g. "Shop now" — blank `cta_text` → the server renders a neutral localized ask (e.g. "Learn more"); a truly CTA-less short is not currently supported), `offer_text` (≤16) only if there's a real deal, and `badge_text` (≤24) / `star_rating` (0–5) — opt-in, user-supplied only, never AI-written; **only ratings/badges you can substantiate — fabricated social proof is deceptive advertising**. End-card fields: `closing_claim` (≤80 — end-card claim, falls back to the headline), `brand_name` (≤40 — shown on the closing brand-lockup slate when no poster is set), `end_card` (`"auto"` default — poster or brand-lockup slate, 14s; `"none"` — no end card, 12s, loop-style). Populate at least the title/subtitle and a visual_language. `brand_profile_id` (from `list_brand_profiles`) on `create_short` seeds blank fields from a saved brand. `language` is a closed set — `en fr es de it nl pl pt ru zh ja ko ar` (no region subtags like `en-US`).
|
|
110
|
+
|
|
111
|
+
Other useful tools: `get_status` (check progress without blocking), `list_voices` (choose a voice whose normalized `languages` contains the editor language), `list_projects` (`include_completed: true` to recover a finished short's slug), `list_renders { slug }` (every editor render version plus thumbnail, duration, retryable/stale flags, snapshot hash, and presigned URL), `retry_render { slug, render_id }` (re-run a **failed** editor render from its saved snapshot, **0 credits**).
|
|
112
|
+
|
|
113
|
+
## Image carousels (sliders)
|
|
114
|
+
|
|
115
|
+
A **slider** is an image carousel (N still slides + a ready-to-post caption + hashtags), not a video — so `wait_for_completion`/`download_result` do **not** apply; poll `get_slider` instead.
|
|
116
|
+
|
|
117
|
+
1. `create_slider { prompt, mode, slide_count? }` — one prompt → a carousel draft (0 credits). `mode` is `creative` (story-driven) or `ad_driven` (facts-forward); `slide_count` 3–10.
|
|
118
|
+
2. `generate_slider { slug }` — render it (copy → AI backgrounds → composite). **1 credit per slide.**
|
|
119
|
+
3. Poll `get_slider { slug }` until `completed`, then download each `slides[].image_url` and post them with the returned `caption` + `hashtags`.
|
|
120
|
+
|
|
121
|
+
Tweak a finished carousel without re-paying: `restyle_slider { slug, ... }` (new template/accent/position — re-renders every slide, reusing the AI backgrounds) or `edit_slider_slide { slug, ... }` (rewrite one slide's headline/body/kicker and re-composite just that slide). Both **0 credits**; poll `get_slider` again until `completed`.
|
|
122
|
+
|
|
123
|
+
## Tracking-overlay videos (burn a CV overlay on your own clip)
|
|
124
|
+
|
|
125
|
+
`create_tracking_video { video_path, save_path?, classes?, color? }` — one shot: uploads a local clip, burns a computer-vision object-tracking overlay (convex-hull polygons hugging each moving subject + HUD brackets + telemetry), renders, polls, and downloads. Source clip **≤ 60s, ≤ 1080p**. Costs **1 credit** on render (refunded on failure). `classes` = COCO-80 class ids to track (e.g. `[0]` person, `[16]` dog; omit to track all subjects); `color` = `[r,g,b]` or `[r,g,b,a]`, each 0–255 (omit for white).
|
|
126
|
+
|
|
127
|
+
- On `terminal: false` (slow upload / cold GPU), call the tool **again with the returned `slug`** to resume — it reuses the in-flight upload (no re-upload, no re-charge). Or poll `wait_for_completion({ slug, kind: "tracking" })` then `download_result`.
|
|
128
|
+
- A completed slug is **final** (its source clip is deleted on success). For a variant (different `classes`/`color`), start a **new** project — omit `slug`, pass `video_path`. Re-uploading the same clip charges a fresh render.
|
|
129
|
+
|
|
130
|
+
## Granular control (direct every step, no autopilot)
|
|
131
|
+
|
|
132
|
+
When you want to author the ad yourself rather than let autopilot decide, drive the editor step by step. Autopilot (`create_editor_ad`) stays the default; this is the controlled path — all agent-driven, no human approval:
|
|
133
|
+
|
|
134
|
+
1. `create_editor_draft { product_prompt, language?, theme?, voice_id?, export_aspect_ratio?, project_intent? }` — creates an editor project **without** autopilot (0 credits).
|
|
135
|
+
2. `generate_scenario { slug, segments_count? }` (1 AI assist) **or** `set_scenario { slug, scenario_prompt }` (free) — `segments_count` is 3..10.
|
|
136
|
+
3. `get_editor { slug, response_format: "concise" }` — **review** scenario, `segments[]`, `narration_status`, `current_audio`, `current_music`, `latest_render`, and `ai_assist_quota` before spending. Omit the format only when you need the backward-compatible full payload.
|
|
137
|
+
4. `set_scene_count { slug, count }` (1..20; only removes trailing un-generated scenes). Each AI scene is a fixed **8s**, so this sets the ad's length (`count × 8s`); uploaded clips keep their own length.
|
|
138
|
+
5. `set_segment_prompt { slug, segment_id, prompt }` per scene (free, 1..2000 chars).
|
|
139
|
+
6. `generate_segment` one at a time, or `generate_all_segments { slug }` to run pending scenes sequentially (continuity preserved). 5 credits/scene; stop on first 402.
|
|
140
|
+
7. `set_narration_script { slug, script }` (free, ≤10000) **or** `generate_narration { slug }` (1 assist). AI narration requires every scene completed with a known positive duration and at least one scene without retained original audio; an exact retry deduplicates for free. Then add a music `description` (≤1200).
|
|
141
|
+
8. `generate_voice { slug, voice_id }` (3 credits; `voice_id` required — from `list_voices`); `generate_music { slug, description, mood?, genre?, tempo?, instruments? }` (5 credits). `instruments` is an array of up to 10 non-empty strings (≤80 chars each), not a comma-separated string.
|
|
142
|
+
9. `render { slug }` (0 credits — charges ungenerated or previously failed scenes) → `wait_for_completion({ slug, kind: "editor" })` → `download_result`.
|
|
143
|
+
|
|
144
|
+
## Bring your own media (local uploads)
|
|
145
|
+
|
|
146
|
+
An **editor** project can use the user's **own local files** — video clips on the timeline, or a product / closing / logo image — all **0 credits**:
|
|
147
|
+
|
|
148
|
+
1. `upload_video { slug, file_path, add_to_timeline? }` — uploads a local clip and waits until it's processed (`status: ready`). With `add_to_timeline` (the default) it also appends the clip as a finished scene; otherwise place it later with `add_segment_from_upload { slug, upload_id }`, or set a specific scene with `use_asset_for_segment { slug, segment_id, upload_id }` (also takes `source_segment_id` to reuse a finished scene).
|
|
149
|
+
2. Images: `set_product { slug, file_path, description? }`, `set_closing_image { slug, file_path }`, `set_logo { slug, file_path }`. For ordinary/new projects, the Editor product photo is a required product-identity reference for every generated AI scene; its confirmed description grounds scenario/narration, and the exact photo becomes the closing image unless a separate one was authored. Small generated label text can still vary. Replacing/removing it or changing its identity description invalidates completed AI scenes; if generation races the change and returns `editor_references_changed`, re-read and retry.
|
|
150
|
+
|
|
151
|
+
A **short** can also take an optional product reference image — `set_short_product { slug, file_path, description? }` — an exact deterministic logo overlay — `set_short_logo { slug, file_path, position?, treatment? }` (jpeg/png ≤5 MiB; never sent to the video model) — and an end-card poster — `set_short_poster { slug, file_path, poster_includes_lockup? }` (a closing still that extends the render to 14s; set `poster_includes_lockup: true` if the poster already contains brand name/CTA/badges, so the overlaid claim/CTA is suppressed and doesn't collide). Product/poster images are 0 credits and jpeg/png ≤20 MiB (20,971,520 bytes).
|
|
152
|
+
|
|
153
|
+
Video: mp4 / mov / webm / mkv, **≤500 MB decimal (500,000,000 bytes), ≤5 min**; multipart starts at **50 MiB (52,428,800 bytes)**. Editor product images: jpeg/png, **≤8 MiB (8,388,608 bytes)**. Editor closing and short images: jpeg/png, **≤20 MiB (20,971,520 bytes)**. Editor and short logos: jpeg/png, **≤5 MiB (5,242,880 bytes)**. No WebP. A freshly uploaded clip must finish processing before it can be placed — `upload_video` waits for you, so don't place an upload it reports as still processing. **Only upload files the user explicitly gave you** — never read arbitrary local paths.
|
|
154
|
+
|
|
155
|
+
An editor `video_url` may be the prior delivered MP4 after edits. Treat it as current only when normalized status has `ready:true` and `stale:false`; unknown/absent staleness is not evidence that it matches the live timeline.
|
|
156
|
+
|
|
157
|
+
## AI assists
|
|
158
|
+
|
|
159
|
+
Helper calls that have the model write content draw from a **free daily quota of 20** per account (separate from credits). Check with `get_ai_assists`; buy more with `unlock_ai_assists` (**1 credit → +10**).
|
|
160
|
+
|
|
161
|
+
- **Consume 1 assist:** `generate_scenario`, `enhance_prompt`, `suggest_next_scene`, `suggest_music_prompt`, `generate_narration` (per-segment regeneration is REST-only — `POST /api/editor/:slug/segments/:id/regenerate-narration`, not an MCP tool), `generate_short_text`.
|
|
162
|
+
- **Free writes:** `set_scenario`, `set_segment_prompt`, `set_narration_script`, add/reorder, `apply_scenario`, `render`.
|
|
163
|
+
- **Cost credits (not assists):** segment 5, regenerate 4, voice 3, music 5.
|
|
164
|
+
|
|
165
|
+
**Narration vs voice vs music are three separate meters — don't conflate them:** *drafting the narration script* (`generate_narration`) is **1 free-quota assist** (its "free" summary means free of *credits*, not quota); *synthesizing the voice-over* (`generate_voice`) is **3 credits**; *generating music* (`generate_music`) is **5 credits**. **429 `ai_assist_quota_exceeded`** = out of assists (unlock once or write it yourself; never loop); **402 `credits_insufficient`** = out of credits (stop).
|
|
166
|
+
|
|
167
|
+
## Delegate the whole content operation (plan → draft → review → schedule → measure)
|
|
168
|
+
|
|
169
|
+
Beyond one asset, you can run the surrounding content operation. **Everything through drafting is $0** — the planning tools draw the free AI-assist quota (not credits), and materializing a plan produces **0-credit drafts**. **Generating/rendering each draft is a separate, explicit, paid step** — create the drafts, then generate only the ones the user approves; never auto-spend a month of content. The whole loop is agent-scoped (`video:read` + `video:generate`); nothing needs `account:admin`.
|
|
170
|
+
|
|
171
|
+
1. **Onboard a product.** `extract_product_page { url }` pulls facts + candidate image/logo URLs from a public page (SSRF-guarded server-side, 0 credits). Persist the identity once so later drafts inherit it: `create_product_profile { name, ... }` + `create_brand_profile { name, ... }` (`list_*` / `update_*` manage them). Pull the primary image + logo in with `import_product_image { product_profile_id, url, link_as }` (`primary_image` default, or `logo`).
|
|
172
|
+
2. **Plan (free quota).** `plan_campaign { product_profile_id | product }` proposes angles/formats + a per-item credit **estimate**; `generate_hook_variations { ..., brand_profile_id? }` writes 3–6 draftable hook options (each a script + caption + hashtags + suggested format). On 429, `unlock_ai_assists` once or write the copy yourself.
|
|
173
|
+
3. **Pack & materialize ($0).** `create_campaign_pack { title?, product_profile_id?, brand_profile_id? }` is the container; `add_pack_variations { pack_id, variations, materialize: true }` persists the picks and turns each into an editable **draft** (≤12/call). `materialize_pack_item { pack_id, item_id }` (re)tries one; `get_campaign_pack { id }` / `list_campaign_packs` show each item's status + **draft slug** + estimate. Shortcut: `create_campaign { url | product_profile_id, brand_profile_id?, item_count?, formats? }` runs steps 1–3 in one call.
|
|
174
|
+
4. **Generate per draft (credits — a deliberate step).** For each approved video draft, call `start_autopilot { slug }` to get the free quote, then—only after approval—launch with `start_autopilot { slug, max_credits: approved_cap }`. Use `generate_slider { slug }` for an approved carousel. Nothing above this line spent video credits.
|
|
175
|
+
5. **Review (no-login).** `create_review_link { video_result_id | slider_id }` mints an expiring page where a human approves / requests changes / comments with **no account**. The token + URL come back **once** — surface them immediately. `list_review_links` shows activity; `revoke_review_link { id }` kills a link. Gate spend or posting on approval.
|
|
176
|
+
6. **Calendar (reminder-only).** `schedule_post { video_result_id, platform, scheduled_at }` stages a calendar entry that pushes a **reminder to the user** at the due time — it does **not** auto-publish. `list_scheduled_posts` is the calendar; `reschedule_post` / `cancel_scheduled_post` manage it; after the user posts, `mark_scheduled_posted { id }`. All 0 credits, all `video:generate`.
|
|
177
|
+
7. **Measure → double down.** `record_performance { item_id, platform, views, ... }` logs a snapshot for a pack item; `set_item_attributes { item_id, ... }` tags its creative. `get_recommendations` then returns a **cautious**, sample-size-aware read of your own best hook/format — **present its `confidence` + `note` verbatim; never claim causation, ROAS, or attribution.** `make_more_like_winner { item_id }` spins fresh hooks from a proven item → feed a chosen one back into `add_pack_variations` (step 3).
|
|
178
|
+
8. **Recurring shows.** `create_series { name, template, brand_profile_id?, product_profile_id? }` defines a repeating format; `plan_series_episodes { series_id }` drafts upcoming ideas (free quota); `materialize_episode { series_id, episode_id }` turns one into a 0-credit draft; `get_series_dashboard { series_id }` shows the upcoming / drafted / published lanes; `mark_episode_posted { series_id, episode_id }` closes the loop after a manual post.
|
|
179
|
+
|
|
180
|
+
**Reusable assets.** `list_assets` / `get_asset_quota` / `upload_asset { file_path, ... }` manage a cross-project media catalog (images + videos) so a clip/logo can be reused across projects. These need an **active subscription** (402 `subscription_required` otherwise) and cost **0 credits**; `upload_asset` reads are confined to `HUBFLUENCER_INPUT_DIR`.
|
|
181
|
+
|
|
182
|
+
## Field limits
|
|
183
|
+
|
|
184
|
+
| Field | Min | Max |
|
|
185
|
+
|-------|-----|-----|
|
|
186
|
+
| editor `product_prompt` (create) | 0 or 10 | 5000 |
|
|
187
|
+
| `scenario_prompt` | 1 | 50000 |
|
|
188
|
+
| `generate_scenario` `segments_count` | 3 | 10 |
|
|
189
|
+
| scene count (`set_scene_count`) | 1 | 20 |
|
|
190
|
+
| segment `prompt` | 1 | 2000 |
|
|
191
|
+
| `narration_script` | — | 10000 |
|
|
192
|
+
| music `description` (silently truncated) | — | 1200 |
|
|
193
|
+
| `voice_id` (`^[A-Za-z0-9_-]+$`, required) | 1 | 64 |
|
|
194
|
+
| `language` | 2 | 10 |
|
|
195
|
+
| short `headline` (title; keep ≈4 words / ≤40 chars) / `subheadline` | — | 80 / 200 |
|
|
196
|
+
| short `closing_claim` / `brand_name` (end card) | — | 80 / 40 |
|
|
197
|
+
| short `music_vibe` | — | 80 |
|
|
198
|
+
| AI assist daily quota | 0 | 20 |
|
|
199
|
+
|
|
200
|
+
## Credits
|
|
201
|
+
|
|
202
|
+
- **A credit is $0.99** bought singly; bulk packs and subscriptions cost less per credit. Translate credit counts into dollars for the user: a short (15cr) ≈ **$15 max**, an autopilot ad (~28cr) ≈ **$28 max**.
|
|
203
|
+
- **Confirm before spending:** quote the estimated credits + approximate dollar cost and get an explicit yes before the first charging call; for a batch, confirm the total once up front. Skip only when the user already approved that specific spend.
|
|
204
|
+
- Any draft create / edit / reorder = **0 credits**.
|
|
205
|
+
- Rendering a short = **15 credits**.
|
|
206
|
+
- Granular editor steps: generate segment 5, regenerate 4, **batch per-segment (batch of ≥3) 4** (so `render` bills any still-ungenerated scenes at the batch rate), voice 3, music 5, render 0 (charges ungenerated or previously failed scenes). Unlock AI assists = 1 credit → +10.
|
|
207
|
+
- Multi-scene editor ads (autopilot) ≈ **28** — preflight before rendering (the MCP surfaces this via the editor autopilot cost check; or `make_video` `dry_run`).
|
|
208
|
+
- Carousel render = **1 / slide** (3–10 slides); carousel restyle / per-slide edit = 0. Tracking render = **1** (refunded on failure).
|
|
209
|
+
- Onboarding / planning / packs / series / calendar / performance / review / profiles = **0 credits** (planning tools draw the free assist quota).
|
|
210
|
+
- Check the balance any time with `get_credits`.
|
|
211
|
+
|
|
212
|
+
## Result URLs
|
|
213
|
+
|
|
214
|
+
Downloaded result URLs are **presigned and expire in about 24 hours** — they are not permalinks. Download promptly via `make_video`'s `save_path` or `download_result`, and give the user the local file path.
|
|
215
|
+
|
|
216
|
+
## Failure handling
|
|
217
|
+
|
|
218
|
+
- **402 credits_insufficient** → tell the user the required vs. available credits, then **stop**. Do not loop or retry.
|
|
219
|
+
- **429 ai_assist_quota_exceeded** → daily AI-assist quota used up (NOT a credit problem). `unlock_ai_assists` once or write the content yourself; never loop.
|
|
220
|
+
- **409 already running** → a render is already in progress. **Keep polling** with `wait_for_completion` / `get_status`; do **not** start a new generation.
|
|
221
|
+
- **403 insufficient_scope** → the token is missing the `video:generate` scope. Ask the user to create a token with the "Generate videos" scope.
|
|
222
|
+
- **Long waits are normal.** A few minutes per render is expected; don't treat a slow poll as an error.
|
|
223
|
+
|
|
224
|
+
## Out of scope: publishing
|
|
225
|
+
|
|
226
|
+
Publishing directly to TikTok / Instagram / etc. requires a human-linked social account (interactive OAuth) and `account:admin`, which agent tokens never carry. **Do not auto-publish.** When the asset is ready, return the finished MP4 (or carousel images) plus a ready-to-paste caption so the user can post it themselves. You **can** stage a **reminder-only** calendar with `schedule_post` (it pushes a reminder to the user, never a publish) and close the loop with `mark_scheduled_posted` after they post — that's the supported "run my calendar" path.
|
|
227
|
+
|
|
228
|
+
**Label synthetic media.** These renders are AI-generated: TikTok requires an AI-generated content label (and attaches C2PA content credentials since 2025), and Meta requires disclosure for AI-generated/altered content in paid ads. Whenever you hand over an export or stage a posting reminder, tell the user to enable the platform's AI label.
|
package/src/client.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
isRetryableStatus,
|
|
15
15
|
} from "./core.js";
|
|
16
16
|
import { readStoredCredentials } from "./credentials.js";
|
|
17
|
-
import { USER_AGENT } from "./version.js";
|
|
17
|
+
import { USER_AGENT, VERSION } from "./version.js";
|
|
18
18
|
|
|
19
19
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
20
20
|
|
|
@@ -349,7 +349,7 @@ export function clientFromEnv(): HubfluencerClient {
|
|
|
349
349
|
|
|
350
350
|
if (!token) {
|
|
351
351
|
throw new Error(
|
|
352
|
-
|
|
352
|
+
`Not connected to Hubfluencer. Run \`npx -y @hubfluencer/mcp@${VERSION} login\` to connect, or set ` +
|
|
353
353
|
"HUBFLUENCER_API_TOKEN (create one in the app: Settings → Access tokens).",
|
|
354
354
|
);
|
|
355
355
|
}
|