# Hubfluencer — make videos from a prompt

You have access to **Hubfluencer** through an MCP server (`@hubfluencer/mcp`).
Hubfluencer turns a text prompt into a finished, post-ready MP4 video — a fast
single-clip short, or a multi-scene ad. Use it whenever the user asks for a
video ad, a short, a TikTok/Reel/Shorts clip, or any social video.

## Connecting (the user does this once — never invent or echo a token)

Two ways to connect. Both are done by the user; you never create a token and
never print a token in your output.

- **Option A — access token:** In the Hubfluencer app the user buys credits,
  then goes to **Settings → Access tokens** and creates an access token with the
  **Generate videos** scope (`video:generate` + `video:read`). That token is
  pasted into the MCP config as `HUBFLUENCER_API_TOKEN`.
- **Option B — device link (no copy-paste):** The user runs
  `npx -y @hubfluencer/mcp login`. It prints a URL and a short code;
  the user approves the connection in the signed-in app. A scoped token is saved
  to `~/.hubfluencer/credentials.json` and the MCP server picks it up
  automatically.

If a tool call fails with **403 insufficient_scope**, the connected token is
missing the `video:generate` scope — tell the user to create a token with the
**Generate videos** scope. Do not try to work around it.

## The simplest path — `make_video`

For almost every request, call **`make_video`** with a `prompt` and a
`save_path`:

```
make_video({
  prompt: "A punchy 15-second ad for a cold-brew coffee brand, energetic, urban morning vibe",
  save_path: "./out/coffee-ad.mp4"
})
```

`make_video` does the whole pipeline: it creates the project, starts generation,
polls to completion, and downloads the finished MP4.

- `kind` defaults to **`auto`** — a fast single-clip **short** for simple
  prompts, a multi-scene **editor** ad for story / multi-scene prompts.
- Override with `kind: "short"` or `kind: "editor"` when the user is explicit.
- If `make_video` returns **`terminal: false`**, the render is still running.
  Call **`wait_for_completion({ slug, kind })`** again. Generation takes a few
  minutes — that is normal, keep waiting, do not restart.

**Don't ship a bare short.** For a short, pass `headline` (the on-screen TITLE),
`subheadline` (secondary title), and a `music_vibe`/`theme` right in the
`make_video` call so it isn't title-less — these are SHORTS fields (`theme`
applies to both kinds; `headline`/`subheadline`/`music_vibe` are ignored for
editor). For the user's own product image, brand logo, or closing card, the
one-shot can't attach files — use the granular path. If no title/assets were
given, infer a sensible `headline`/`subheadline` from the prompt, or ask.

## Granular path (control / recovery)

Use these when you need step-by-step control or are recovering from an
interruption:

1. `get_credits` — check the balance before spending.
2. `create_short` or `create_editor_ad` — creates the draft (**0 credits**).
3. `generate_short` (short) or autopilot (editor ad) — starts the render.
4. `wait_for_completion({ slug, kind })` — poll until done.
5. `download_result` — save the finished MP4.

**Short creative fields** (`create_short`, also carried by `make_video`):
`headline` (on-screen TITLE, ≤160), `subheadline` (≤200), `music_vibe` (Upbeat
default / Cinematic / Minimal / Luxury / Playful / Jazz), `theme` (none /
realistic default / cinematic / anime / sci_fi / fantasy / noir / superhero /
horror / mockumentary / sports / gaming / retro_80s / minimalist / cyberpunk),
plus `text_position` (top/center/bottom), `text_animation`
(reveal/typewriter/fade_in/pop/bounce), `font_family`, `music_instruments`.
Populate at least the title/subtitle and a vibe.

Other useful tools: `get_status`, `list_voices`, `list_projects`.

## Granular control (author the ad yourself, no autopilot)

When the user wants you to direct every step — the scenario, the exact number of
scenes, each scene's prompt, the narration — drive the editor by hand instead of
autopilot. Autopilot (`create_editor_ad`) stays the default; this is the
controlled path, and it is fully agent-driven (no human approval step):

1. `create_editor_draft { product_prompt, language?, theme?, voice_id?, export_aspect_ratio?, project_intent? }`
   — creates an editor project **without** starting autopilot (0 credits).
2. `generate_scenario { slug, segments_count? }` (1 AI assist) **or**
   `set_scenario { slug, scenario_prompt }` (free). `segments_count` is 3..10.
3. `get_editor { slug }` — **review** the scenario, `segments[]`, narration,
   music, `latest_render`, and `ai_assist_quota` before you spend anything.
4. `set_scene_count { slug, count }` — 1..20; only removes trailing
   un-generated scenes, never a completed or in-progress one. Each AI scene is a
   fixed **8s**, so this sets the ad's length (`count × 8s`); uploaded clips keep
   their own native length.
5. `set_segment_prompt { slug, segment_id, prompt }` per scene (free, 1..2000
   chars).
6. `generate_segment { slug, segment_id }` one at a time, **or**
   `generate_all_segments { slug }` to run pending scenes **sequentially** so
   visual continuity carries forward. 5 credits per scene; stop on the first 402.
7. `set_narration_script { slug, script }` (free, ≤12000 chars) **or**
   `generate_narration { slug }` (1 AI assist); then write a music `description`
   (≤1200 chars).
8. `generate_voice { slug, voice_id }` (3 credits; `voice_id` **required** —
   pick one from `list_voices`) and `generate_music { slug, description }`
   (5 credits).
9. `render { slug }` (0 credits — charges only ungenerated scenes) →
   `wait_for_completion({ slug, kind: "editor" })` → `download_result`.

## Bring your own media (local uploads)

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**:

1. `upload_video { slug, file_path, add_to_timeline? }` — uploads a local clip
   and waits until it is 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).
2. Images: `set_product { slug, file_path, description? }` (then
   `set_product_placement { slug, mode: "throughout"|"end" }`),
   `set_closing_image { slug, file_path }`, `set_logo { slug, file_path }`.

A **short** can also take a product image — `set_short_product { slug, file_path,
description? }` (used as the Veo image-to-video input) — and an end-card poster —
`set_short_poster { slug, file_path }` (a closing still that extends the render to
14s). Same 0 credits, jpeg/png ≤ 20 MB. Shorts have **no logo overlay**
(editor-only).

Video: mp4 / mov / webm / mkv, **≤ 500 MB, ≤ 5 min**. Image: jpeg / png,
**≤ 20 MB** (no WebP). A freshly uploaded clip must finish processing before it
can be placed — `upload_video` waits for you. **Only upload files the user
explicitly gave you** — never read arbitrary local paths.

## AI assists

Helper calls where the model writes content for you draw from a **free daily
quota of 20** per account, separate from credits. Check it with `get_ai_assists`;
buy more with `unlock_ai_assists` (**1 credit → +10 assists**).

- **Consume 1 assist:** `generate_scenario`, `enhance_prompt`,
  `suggest_next_scene`, `suggest_music_prompt`,
  `generate_narration` / `regenerate_segment_narration`.
- **Free writes:** `set_scenario`, `set_segment_prompt`, `set_narration_script`,
  add/reorder segments, `apply_scenario`, `render`.
- **Cost credits (not assists):** segment 5, regenerate 4, voice 3, music 5.

`generate_narration` **consumes a quota assist** even though its endpoint summary
says "free" — that means free of *credits*, not of *quota*. A **429
`ai_assist_quota_exceeded`** means you are out of assists (unlock once or write
the content yourself — never loop); a **402 `credits_insufficient`** means you
are out of credits (stop).

## Credits

- Creating a draft (`create_short` / `create_editor_ad` / `create_editor_draft`):
  **0 credits**.
- Rendering a short: **15 credits**.
- Granular editor steps: generate segment **5**, regenerate **4**, batch
  per-segment (≥3) **4**, voice **3**, music **5**, render **0** (charges only
  ungenerated scenes). Unlock AI assists = **1 credit → +10**.
- Multi-scene editor ads (autopilot) cost more — preflight the exact cost with
  `GET /api/editor/:slug/autopilot/cost` (or just check `get_credits`).
- Always check the balance with `get_credits` before a render the user did not
  explicitly pre-approve.

## Failure handling

- **402 credits_insufficient** — state the **required** vs **available** credits
  and **stop**. Do not loop or retry. Tell the user to buy more credits in the
  app.
- **429 ai_assist_quota_exceeded** — the daily AI-assist quota is used up (NOT a
  credit problem). `unlock_ai_assists` once or write the content yourself; never
  loop.
- **409 already-running** — a render is already in progress. **Keep polling**
  with `wait_for_completion`; do **not** start a new one.
- **403 insufficient_scope** — the connected token lacks `video:generate` (see
  Connecting above).
- Long waits are normal. A render taking a few minutes is expected, not a
  failure.

## Result links

Result URLs are **presigned and short-lived (~24h TTL)** — they are not
permanent links. Download the MP4 promptly via `download_result` (or the
`save_path` you passed to `make_video`).

## Out of scope — do not auto-publish

Publishing to TikTok / Instagram requires a human-linked social account. **Do
not** attempt to auto-publish. When the video is done, return the finished MP4
path **plus a ready-to-paste caption** so the user can post it themselves.
