@hubfluencer/mcp 0.13.0 → 0.14.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/dist/index.js CHANGED
@@ -29510,6 +29510,13 @@ function normalizeStatus(kind, slug, data) {
29510
29510
  status.failure_attempt = d.failure_attempt;
29511
29511
  if (typeof d.generation_attempt === "number")
29512
29512
  status.generation_attempt = d.generation_attempt;
29513
+ if (typeof d.segments_completed === "number")
29514
+ status.segments_completed = d.segments_completed;
29515
+ if (typeof d.segments_total === "number")
29516
+ status.segments_total = d.segments_total;
29517
+ if (Array.isArray(d.active_segment_positions) && d.active_segment_positions.every((position) => typeof position === "number")) {
29518
+ status.active_segment_positions = d.active_segment_positions;
29519
+ }
29513
29520
  if (typeof d.latest_render_free === "boolean") {
29514
29521
  status.latest_render_free = d.latest_render_free;
29515
29522
  }
@@ -30259,7 +30266,7 @@ function readStoredCredentials() {
30259
30266
  // package.json
30260
30267
  var package_default = {
30261
30268
  name: "@hubfluencer/mcp",
30262
- version: "0.13.0",
30269
+ version: "0.14.0",
30263
30270
  description: "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
30264
30271
  license: "MIT",
30265
30272
  author: "Monocursive <contact@monocursive.com>",
@@ -30578,6 +30585,9 @@ var getStatusOutput = exports_external.object({
30578
30585
  failure_source: exports_external.string().optional().describe("Short only: worker/recovery layer that finalized the failure."),
30579
30586
  failure_attempt: exports_external.number().optional().describe("Short only: paid generation attempt number that failed."),
30580
30587
  generation_attempt: exports_external.number().optional().describe("Short only: current paid attempt; echo when cancelling."),
30588
+ segments_completed: exports_external.number().optional(),
30589
+ segments_total: exports_external.number().optional(),
30590
+ active_segment_positions: exports_external.array(exports_external.number()).optional(),
30581
30591
  latest_render_free: exports_external.boolean().optional(),
30582
30592
  last_free_rerender_failed: exports_external.boolean().optional()
30583
30593
  }).passthrough();
@@ -30598,6 +30608,9 @@ var waitForCompletionOutput = exports_external.object({
30598
30608
  failure_source: exports_external.string().optional().describe("Short only: worker/recovery layer that finalized the failure."),
30599
30609
  failure_attempt: exports_external.number().optional().describe("Short only: paid generation attempt number that failed."),
30600
30610
  generation_attempt: exports_external.number().optional().describe("Short only: current paid attempt; echo when cancelling."),
30611
+ segments_completed: exports_external.number().optional(),
30612
+ segments_total: exports_external.number().optional(),
30613
+ active_segment_positions: exports_external.array(exports_external.number()).optional(),
30601
30614
  latest_render_free: exports_external.boolean().optional(),
30602
30615
  last_free_rerender_failed: exports_external.boolean().optional(),
30603
30616
  saved_to: exports_external.union([exports_external.string(), exports_external.null()]).optional(),
@@ -32130,7 +32143,30 @@ registerTool("list_voices", {
32130
32143
  description: "Lists available narration voices (id + name). Pass a voice id as voice_id to create_editor_ad / " + "make_video to pick the narration voice for an editor ad. Shorts have no voiceover.",
32131
32144
  inputSchema: {},
32132
32145
  annotations: { title: "List voices", ...RO }
32133
- }, tool(async (_args, client) => ok(await client.get("/voices"))));
32146
+ }, tool(async (_args, client) => {
32147
+ const response = asRecord(await client.get("/voices"));
32148
+ const data = asRecord(response.data ?? response);
32149
+ const voices = Array.isArray(data.voices) ? data.voices.map(asRecord) : [];
32150
+ return ok({
32151
+ voices: voices.map((voice) => {
32152
+ const verified = Array.isArray(voice.verified_languages) ? voice.verified_languages.map(asRecord) : [];
32153
+ return {
32154
+ voice_id: voice.voice_id,
32155
+ name: voice.name,
32156
+ category: voice.category,
32157
+ description: voice.description,
32158
+ labels: voice.labels,
32159
+ preview_url: voice.preview_url,
32160
+ languages: [
32161
+ ...new Set(verified.map((language) => language.language).filter((language) => typeof language === "string"))
32162
+ ]
32163
+ };
32164
+ }),
32165
+ has_more: data.has_more,
32166
+ total_count: data.total_count,
32167
+ next_page_token: data.next_page_token
32168
+ });
32169
+ }));
32134
32170
  registerTool("list_projects", {
32135
32171
  title: "List recent projects",
32136
32172
  description: "Lists the caller's recent shorts and editor/video-factory projects, each normalized to " + "{slug, stage, terminal, ready, video_url}. By default the shorts listing is limited to " + "in-progress/failed; pass include_completed:true to also surface finished shorts — useful to " + "recover a lost slug. Capped to the most recent items per kind (use get_status for authoritative state).",
@@ -32369,7 +32405,13 @@ registerTool("update_short", {
32369
32405
  const res = await client.patch(`/shorts/${args.slug}`, body);
32370
32406
  const data = asRecord(res).data ?? res;
32371
32407
  return ok({
32372
- ...asRecord(data),
32408
+ slug: args.slug,
32409
+ kind: "short",
32410
+ stage: asRecord(data).stage,
32411
+ status: asRecord(data).status,
32412
+ updated: body,
32413
+ end_card_mode: asRecord(data).end_card_mode,
32414
+ resolved_cta_text: asRecord(data).resolved_cta_text,
32373
32415
  next: "Saved. Only text/style/CTA/end-card/logo changed on an already-generated short? " + `rerender_short({ slug: "${args.slug}" }) applies it for 0 credits. Footage/music edits ` + "(prompt, product image, format, visual_language, theme, music_*, language) need " + "generate_short (15 credits)."
32374
32416
  });
32375
32417
  }));
@@ -33593,7 +33635,7 @@ registerTool("generate_music", {
33593
33635
  mood: exports_external.string().optional().describe("Optional mood hint"),
33594
33636
  genre: exports_external.string().optional().describe("Optional genre hint"),
33595
33637
  tempo: exports_external.string().optional().describe("Optional tempo hint"),
33596
- instruments: exports_external.string().optional().describe("Optional instruments hint")
33638
+ instruments: exports_external.array(exports_external.string().min(1).max(80)).max(10).optional().describe('Optional instrument hints, e.g. ["piano", "strings"]')
33597
33639
  },
33598
33640
  annotations: { title: "Generate music", ...WRITE, idempotentHint: false }
33599
33641
  }, tool(async (args, client) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubfluencer/mcp",
3
- "version": "0.13.0",
3
+ "version": "0.14.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/core.ts CHANGED
@@ -277,6 +277,12 @@ export interface NormalizedStatus {
277
277
  failure_attempt?: number;
278
278
  /** Short only: current paid attempt; echo when cancelling. */
279
279
  generation_attempt?: number;
280
+ /** Short only: completed/current Veo clip count for useful long-poll progress. */
281
+ segments_completed?: number;
282
+ /** Short only: total current Veo clips in the paid attempt. */
283
+ segments_total?: number;
284
+ /** Short only: clip positions that are pending or processing. */
285
+ active_segment_positions?: number[];
280
286
  /** Editor only: the newest delivered render no longer matches the live project. */
281
287
  stale?: boolean;
282
288
  /** Editor only: current scene ids that are still generating. */
@@ -365,6 +371,18 @@ export function normalizeStatus(
365
371
  status.failure_attempt = d.failure_attempt;
366
372
  if (typeof d.generation_attempt === "number")
367
373
  status.generation_attempt = d.generation_attempt;
374
+ if (typeof d.segments_completed === "number")
375
+ status.segments_completed = d.segments_completed;
376
+ if (typeof d.segments_total === "number")
377
+ status.segments_total = d.segments_total;
378
+ if (
379
+ Array.isArray(d.active_segment_positions) &&
380
+ d.active_segment_positions.every(
381
+ (position) => typeof position === "number",
382
+ )
383
+ ) {
384
+ status.active_segment_positions = d.active_segment_positions as number[];
385
+ }
368
386
  // Additive free-re-render bookkeeping (2026-07 API): surfaced only when
369
387
  // the server actually sends the booleans, so older payloads normalize
370
388
  // byte-identically. A failed free re-render does NOT flip stage to
package/src/index.ts CHANGED
@@ -1417,7 +1417,40 @@ registerTool(
1417
1417
  inputSchema: {},
1418
1418
  annotations: { title: "List voices", ...RO },
1419
1419
  },
1420
- tool(async (_args, client) => ok(await client.get("/voices"))),
1420
+ tool(async (_args, client) => {
1421
+ const response = asRecord(await client.get("/voices"));
1422
+ const data = asRecord(response.data ?? response);
1423
+ const voices = Array.isArray(data.voices) ? data.voices.map(asRecord) : [];
1424
+
1425
+ return ok({
1426
+ voices: voices.map((voice) => {
1427
+ const verified = Array.isArray(voice.verified_languages)
1428
+ ? voice.verified_languages.map(asRecord)
1429
+ : [];
1430
+ return {
1431
+ voice_id: voice.voice_id,
1432
+ name: voice.name,
1433
+ category: voice.category,
1434
+ description: voice.description,
1435
+ labels: voice.labels,
1436
+ preview_url: voice.preview_url,
1437
+ languages: [
1438
+ ...new Set(
1439
+ verified
1440
+ .map((language) => language.language)
1441
+ .filter(
1442
+ (language): language is string =>
1443
+ typeof language === "string",
1444
+ ),
1445
+ ),
1446
+ ],
1447
+ };
1448
+ }),
1449
+ has_more: data.has_more,
1450
+ total_count: data.total_count,
1451
+ next_page_token: data.next_page_token,
1452
+ });
1453
+ }),
1421
1454
  );
1422
1455
 
1423
1456
  // ── Projects ──────────────────────────────────────────────────────────────
@@ -2113,7 +2146,13 @@ registerTool(
2113
2146
  );
2114
2147
  const data = asRecord(res).data ?? res;
2115
2148
  return ok({
2116
- ...asRecord(data),
2149
+ slug: args.slug,
2150
+ kind: "short",
2151
+ stage: asRecord(data).stage,
2152
+ status: asRecord(data).status,
2153
+ updated: body,
2154
+ end_card_mode: asRecord(data).end_card_mode,
2155
+ resolved_cta_text: asRecord(data).resolved_cta_text,
2117
2156
  next:
2118
2157
  "Saved. Only text/style/CTA/end-card/logo changed on an already-generated short? " +
2119
2158
  `rerender_short({ slug: "${args.slug}" }) applies it for 0 credits. Footage/music edits ` +
@@ -4778,7 +4817,11 @@ registerTool(
4778
4817
  mood: z.string().optional().describe("Optional mood hint"),
4779
4818
  genre: z.string().optional().describe("Optional genre hint"),
4780
4819
  tempo: z.string().optional().describe("Optional tempo hint"),
4781
- instruments: z.string().optional().describe("Optional instruments hint"),
4820
+ instruments: z
4821
+ .array(z.string().min(1).max(80))
4822
+ .max(10)
4823
+ .optional()
4824
+ .describe('Optional instrument hints, e.g. ["piano", "strings"]'),
4782
4825
  },
4783
4826
  annotations: { title: "Generate music", ...WRITE, idempotentHint: false },
4784
4827
  },
@@ -4790,7 +4833,7 @@ registerTool(
4790
4833
  mood?: string;
4791
4834
  genre?: string;
4792
4835
  tempo?: string;
4793
- instruments?: string;
4836
+ instruments?: string[];
4794
4837
  },
4795
4838
  client,
4796
4839
  ) => {
@@ -95,6 +95,9 @@ export const getStatusOutput = z
95
95
  .number()
96
96
  .optional()
97
97
  .describe("Short only: current paid attempt; echo when cancelling."),
98
+ segments_completed: z.number().optional(),
99
+ segments_total: z.number().optional(),
100
+ active_segment_positions: z.array(z.number()).optional(),
98
101
  latest_render_free: z.boolean().optional(),
99
102
  last_free_rerender_failed: z.boolean().optional(),
100
103
  })
@@ -142,6 +145,9 @@ export const waitForCompletionOutput = z
142
145
  .number()
143
146
  .optional()
144
147
  .describe("Short only: current paid attempt; echo when cancelling."),
148
+ segments_completed: z.number().optional(),
149
+ segments_total: z.number().optional(),
150
+ active_segment_positions: z.array(z.number()).optional(),
145
151
  latest_render_free: z.boolean().optional(),
146
152
  last_free_rerender_failed: z.boolean().optional(),
147
153
  saved_to: z.union([z.string(), z.null()]).optional(),