@kolbo/mcp 1.113.1 → 1.113.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.113.1",
3
+ "version": "1.113.2",
4
4
  "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/apps/index.js CHANGED
@@ -366,6 +366,9 @@ async function modelInfoMap(client) {
366
366
  const DISPLAY_ALIASES = {
367
367
  'flux-3-draft-enhance': { base: 'seedance-2-5', name: 'Seedance 2.5 Draft Enhance' },
368
368
  'video-draft-enhance': { base: 'seedance-2-5', name: 'Seedance 2.5 Draft Enhance' },
369
+ // Billing SKU every ElevenLabs CLONED voice reports; its version shows in the
370
+ // separate "Model V4/V3/V2" chip, so the model chip is just the provider.
371
+ 'fal_elevenlabs_tts': { base: 'eleven_v4', name: 'ElevenLabs' },
369
372
  };
370
373
  // Operations whose engine is picked server-side when no model is passed.
371
374
  const OPERATION_ENGINES = { draft_enhance: 'flux-3-draft-enhance' };
@@ -530,10 +533,16 @@ async function resolveCatalogAspectRatio(client, modelId, requested, type) {
530
533
 
531
534
  // Same shape and TTL as the model catalog above: the voice catalog is stable,
532
535
  // and a per-generation /v1/voices round trip would be paid on every speech card.
533
- const voiceCache = new Map(); // apiBase → { at, byKey }
536
+ // Keyed per caller, not per server: /v1/voices includes the caller's own cloned
537
+ // voices, so one shared entry (the remote connector serves every user from one
538
+ // process) left everyone else's clones unresolved for the whole TTL.
539
+ const voiceCache = new Map(); // apiBase + key hash → { at, byKey }
534
540
 
535
541
  async function voiceInfoMap(client) {
536
- const cacheKey = client.apiBase || 'default';
542
+ const keyHash = client.apiKey
543
+ ? crypto.createHash('sha256').update(String(client.apiKey)).digest('hex').slice(0, 16)
544
+ : 'anon';
545
+ const cacheKey = `${client.apiBase || 'default'}|${keyHash}`;
537
546
  const hit = voiceCache.get(cacheKey);
538
547
  if (hit && Date.now() - hit.at < ICON_TTL_MS) return hit.byKey;
539
548
  const byKey = new Map();
@@ -1201,7 +1201,8 @@ function registerGenerateTools(server, client, options = {}) {
1201
1201
  // the card was labelling those "Smart Select", which is not even a text-to-
1202
1202
  // speech option, and naming the voice the caller typed rather than the one
1203
1203
  // that spoke. Same resolution addDisplayNames does for the polling path.
1204
- const ranVoice = (await voiceInfo(client, result.result?.voice).catch(() => null)) || voiceRecord;
1204
+ const ranVoice = statusVoice(result.result)
1205
+ || (await voiceInfo(client, result.result?.voice).catch(() => null)) || voiceRecord;
1205
1206
  return uiCompleted({
1206
1207
  tool: 'generate_speech', kind: 'audio', gen, client, prompt: text,
1207
1208
  model: result.result?.model || model,
@@ -1314,13 +1315,21 @@ function registerGenerateTools(server, client, options = {}) {
1314
1315
  r.model_name = (info && info.name) || r.model;
1315
1316
  r.model_icon = (info && info.icon) || null;
1316
1317
  }
1317
- if (r.voice) {
1318
+ // The API names a cloned voice itself (voice_name/voice_thumbnail): clones
1319
+ // are not reliably in the cached catalog, so keep the API's answer.
1320
+ if (r.voice && !statusVoice(r)) {
1318
1321
  const v = await voiceInfo(client, r.voice).catch(() => null);
1319
1322
  r.voice_name = (v && v.name) || r.voice;
1320
1323
  r.voice_thumbnail = (v && v.thumbnail) || null;
1321
1324
  }
1322
1325
  }
1323
1326
 
1327
+ function statusVoice(r) {
1328
+ if (!r || !r.voice_name) return null;
1329
+ const id = /^custom_/i.test(String(r.requested_voice_id || '')) ? r.requested_voice_id : r.voice;
1330
+ return { id, name: r.voice_name, thumbnail: r.voice_thumbnail || null };
1331
+ }
1332
+
1324
1333
  // ─── get_generation_status ─────────────────────────────────
1325
1334
  server.tool(
1326
1335
  'get_generation_status',