beplus-mcp 0.41.0 → 0.42.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 CHANGED
@@ -103,7 +103,7 @@ Reinicie o cliente. Rode a tool **`whoami`** para confirmar o vínculo.
103
103
  | `generate_image` | Gera imagem (nano-banana / gpt-image-2). Bloqueante ~90s; retorna URL + imagem inline. Suporta refs, tamanho/qualidade, e extras do gpt-image-2 (background, moderation, output_format/compression). |
104
104
  | `generate_video` | Gera vídeo (Seedance / Kling). Aguarda ~120s; senão devolve o id pra `check_generation`. Suporta first/last frame, refs de imagem/vídeo/áudio, prompt negativo, áudio gerado, mode e motion-control. |
105
105
  | `upscale` | Melhora a resolução de uma imagem ou vídeo que já existe (Topaz). Detecta o tipo pelo arquivo. Imagem até 4x (~20s, a partir de 6💎); vídeo até 30s e saída até 1080p, custa por bloco de 10s e **descarta o áudio**. Mede o arquivo sozinho para o preço sair certo. ⚠️ Vídeo é restrito à equipe. |
106
- | `generate_audio` | Sintetiza fala (Gemini TTS, multi-locutor). Padrão `gemini-3.8-flash-lite-tts`; os modelos 3.8 aceitam vozes da biblioteca, tags vocais (`<laugh>`, `<sigh>`...) e `style` por locutor; os legados só as 30 vozes clássicas. Síncrono. |
106
+ | `generate_audio` | Sintetiza fala com Gemini TTS ou SeedAudio 1.0 com referência de voz. Padrão `gemini-3.8-flash-lite-tts`; os modelos 3.8 aceitam vozes da biblioteca, tags vocais (`<laugh>`, `<sigh>`...) e `style` por locutor; os legados só as 30 vozes clássicas. Síncrono. |
107
107
  | `list_tts_voices` | Lista as vozes da biblioteca do TTS 3.8 (filtros: idioma, gênero, tom, contexto, busca) com o id para usar em `voice`. Somente leitura. |
108
108
  | `list_custom_voices` | Lista suas vozes próprias do TTS 3.8 (máx. 3) com o `voice_xxx` para usar em `voice`. Clonar a própria voz só no app web. Somente leitura. |
109
109
  | `design_voice` | Cria uma voz própria a partir de uma descrição. Gasta 💎 e leva ~30 s. |
@@ -232,3 +232,13 @@ BEPLUS_API_TOKEN=pat_… npx @modelcontextprotocol/inspector node dist/index.js
232
232
  - **stdout** é o canal JSON-RPC — todo log vai pra **stderr** e o token nunca é logado.
233
233
  - Limites, créditos, visibilidade e logging são enforçados pelo backend; um PAT vazado é limitado aos caps/diamantes do dono e pode ser revogado no painel.
234
234
  - `list_models` é a fonte de verdade em runtime; os enums do pacote podem ficar atrás quando o backend adiciona modelos.
235
+
236
+ ### SeedAudio 1.0
237
+
238
+ Use `generate_audio` com `model: "seed-audio-1.0"`, `text` e direção opcional em `style_instruction`. Omita `voice` ou use `seed-auto`. Texto e direção somados: até 2700 caracteres. Sem diálogo nem vozes Gemini.
239
+
240
+ Para referência, passe `seed_reference` como arquivo local no stdio ou URL pública no conector remoto. Um áudio de 1 a 30 segundos, até 3 MiB, com `seed_consent: true` após autorização explícita da pessoa. O MCP envia os bytes diretamente ao backend, sem publicar um novo arquivo de referência. O modo remoto mantém a proteção de caminhos locais e rede privada.
241
+
242
+ A resposta inclui o ID da geração. Para repetir a voz, use esse ID em `seed_reference_generation_id` e confirme novamente `seed_consent`. Só referências de gerações da própria conta são aceitas. Essa referência por pedido é separada das vozes próprias do Gemini.
243
+
244
+ O conector remoto também precisa da nova versão do pacote no backend. Ordem de entrega: configurar `SEEDAUDIO_API_KEY` e disponibilizar o backend TTS, atualizar o pacote MCP no backend, depois disponibilizar IA Lab e Canvas.
package/dist/index.js CHANGED
@@ -21619,7 +21619,7 @@ var BeplusClient = class {
21619
21619
  });
21620
21620
  }
21621
21621
  tts(body2) {
21622
- return this.request("POST", "/tts", { body: body2, timeoutMs: 9e4 });
21622
+ return this.request("POST", "/tts", { body: body2, timeoutMs: body2.model === "seed-audio-1.0" ? 18e4 : 9e4 });
21623
21623
  }
21624
21624
  ttsVoices(query) {
21625
21625
  return this.request("GET", "/tts/voices", { query, timeoutMs: 2e4 });
@@ -21919,7 +21919,7 @@ function tooLarge(name, bytes) {
21919
21919
  `"${name}" ${size} acima do teto de 95 MB para subir pela API (o proxy da Cloudflare corta corpo acima de 100 MB antes de chegar no servidor). Comprima ou corte o arquivo (ex.: ffmpeg -i entrada.mp4 -crf 26 saida.mp4) e tente de novo.`
21920
21920
  );
21921
21921
  }
21922
- async function loadLocal(ref) {
21922
+ async function loadLocal(ref, maxBytes = MAX_UPLOAD_BYTES) {
21923
21923
  const abs = toLocalPath(ref);
21924
21924
  const name = basename(abs);
21925
21925
  let size;
@@ -21931,10 +21931,10 @@ async function loadLocal(ref) {
21931
21931
  `Arquivo local n\xE3o encontrado: "${ref}". Passe um caminho v\xE1lido ou uma URL p\xFAblica (http/https).`
21932
21932
  );
21933
21933
  }
21934
- if (size > MAX_UPLOAD_BYTES) throw tooLarge(name, size);
21934
+ if (size > maxBytes) throw maxBytes === MAX_UPLOAD_BYTES ? tooLarge(name, size) : new ApiError(413, `O arquivo deve ter at\xE9 ${mb(maxBytes)} MB.`);
21935
21935
  return { buf: await readFile2(abs), name, mime: mimeForName(abs), localPath: abs };
21936
21936
  }
21937
- async function loadRemote(url) {
21937
+ async function loadRemote(url, maxBytes = MAX_UPLOAD_BYTES) {
21938
21938
  const u = new URL(url);
21939
21939
  let name = decodeURIComponent(basename(u.pathname)) || "arquivo";
21940
21940
  let res;
@@ -21950,10 +21950,10 @@ async function loadRemote(url) {
21950
21950
  throw new ApiError(res.status, `N\xE3o consegui baixar ${url}: a origem respondeu HTTP ${res.status}.`);
21951
21951
  }
21952
21952
  const declared = Number(res.headers.get("content-length") ?? NaN);
21953
- if (Number.isFinite(declared) && declared > MAX_UPLOAD_BYTES) {
21953
+ if (Number.isFinite(declared) && declared > maxBytes) {
21954
21954
  await res.body?.cancel().catch(() => {
21955
21955
  });
21956
- throw tooLarge(name, declared);
21956
+ throw maxBytes === MAX_UPLOAD_BYTES ? tooLarge(name, declared) : new ApiError(413, `O arquivo deve ter at\xE9 ${mb(maxBytes)} MB.`);
21957
21957
  }
21958
21958
  const chunks = [];
21959
21959
  let total = 0;
@@ -21964,10 +21964,10 @@ async function loadRemote(url) {
21964
21964
  const { done, value } = await reader.read();
21965
21965
  if (done) break;
21966
21966
  total += value.byteLength;
21967
- if (total > MAX_UPLOAD_BYTES) {
21967
+ if (total > maxBytes) {
21968
21968
  await reader.cancel().catch(() => {
21969
21969
  });
21970
- throw tooLarge(name, null);
21970
+ throw maxBytes === MAX_UPLOAD_BYTES ? tooLarge(name, null) : new ApiError(413, `O arquivo deve ter at\xE9 ${mb(maxBytes)} MB.`);
21971
21971
  }
21972
21972
  chunks.push(Buffer.from(value.buffer, value.byteOffset, value.byteLength));
21973
21973
  }
@@ -22282,6 +22282,7 @@ var MUSIC_TASK_TYPES = ["create", "extend", "cover", "upload_extend", "upload_co
22282
22282
  var MUSIC_TOOLS = ["wav", "midi", "stems-basic", "stems-full", "bpm", "lyrics-timeline"];
22283
22283
  var VOCAL_GENDERS = ["f", "m"];
22284
22284
  var TTS_MODELS = [
22285
+ "seed-audio-1.0",
22285
22286
  "gemini-3.8-flash-lite-tts",
22286
22287
  "gemini-3.8-flash-tts",
22287
22288
  "gemini-3.1-flash-tts-preview",
@@ -23856,7 +23857,7 @@ var VOICE_HELP_TOP = `${VOICE_HELP} Ou o id de uma voz pr\xF3pria (voice_xxx, de
23856
23857
  var voiceSchema = external_exports.string().refine((v) => VOICES.includes(v) || LIBRARY_VOICE_ID.test(v), {
23857
23858
  message: "Voz inv\xE1lida: use uma das 30 vozes cl\xE1ssicas (ex.: Kore) ou um id da biblioteca (ex.: pt-br-advisor-10)."
23858
23859
  });
23859
- var topVoiceSchema = external_exports.string().refine((v) => VOICES.includes(v) || LIBRARY_VOICE_ID.test(v) || CUSTOM_VOICE_ID.test(v), {
23860
+ var topVoiceSchema = external_exports.string().refine((v) => v === "seed-auto" || VOICES.includes(v) || LIBRARY_VOICE_ID.test(v) || CUSTOM_VOICE_ID.test(v), {
23860
23861
  message: "Voz inv\xE1lida: use uma das 30 vozes cl\xE1ssicas (ex.: Kore), um id da biblioteca (ex.: pt-br-advisor-10) ou uma voz pr\xF3pria (voice_xxx)."
23861
23862
  });
23862
23863
  function isCustomVoice(v) {
@@ -23875,10 +23876,13 @@ function registerAudioTools(server, client, cfg) {
23875
23876
  "generate_audio",
23876
23877
  {
23877
23878
  title: "Gerar \xE1udio (TTS)",
23878
- description: 'Sintetiza fala a partir de texto (s\xEDncrono \u2014 retorna a URL na hora) usando o IA Lab da BePlus. Modelos Gemini 3.8 (`gemini-3.8-flash-lite-tts`, padr\xE3o, r\xE1pido e barato; `gemini-3.8-flash-tts`, qualidade de est\xFAdio, melhor para di\xE1logo e narra\xE7\xE3o longa) aceitam: vozes da biblioteca (milhares, por idioma; liste com `list_tts_voices` e passe o id em `voice`), tags vocais no texto como <laugh> <chuckle> <sigh> <breath> <gasp> <cough> <sneeze> <throat-clearing> <yawn> <groan> <scream> <whimper> <argh> <short pause> <long pause> (viram som de verdade), e estilo por locutor (`style` em cada speaker). No 3.8, `style_instruction` \xE9 dire\xE7\xE3o e N\xC3O \xE9 lida em voz alta. Os modelos legados (`gemini-3.1-flash-tts-preview`, `gemini-2.5-pro-preview-tts`) s\xF3 aceitam as 30 vozes cl\xE1ssicas (Kore, Puck, Charon...) e n\xE3o t\xEAm voz da biblioteca. Vozes pr\xF3prias (voice_xxx; liste com `list_custom_voices`, crie com `design_voice`) s\xF3 no 3.8 e sem `multi_speaker`. No 3.8, dire\xE7\xE3o por linha: `(sussurrando) texto` no in\xEDcio da linha vale at\xE9 a pr\xF3xima dire\xE7\xE3o; no di\xE1logo, `Ana (rindo): fala`; rea\xE7\xF5es `|hmm|` `|aham|` no meio da fala (s\xF3 di\xE1logo). Opcional `style_instruction` (ex.: "fale animado e pausado") e di\xE1logo com 2 locutores via `multi_speaker`. Consome diamantes por segundo de \xE1udio.' + TEAM_GATING_NOTE,
23879
+ description: 'Sintetiza fala a partir de texto e retorna a URL usando o IA Lab da BePlus. SeedAudio 1.0 (seed-audio-1.0) aceita dire\xE7\xE3o e uma refer\xEAncia de voz opcional via seed_reference ou seed_reference_generation_id, com seed_consent. Texto e dire\xE7\xE3o somados at\xE9 2700 caracteres. N\xE3o aceita di\xE1logo nem vozes Gemini. Modelos Gemini 3.8 (`gemini-3.8-flash-lite-tts`, padr\xE3o, r\xE1pido e barato; `gemini-3.8-flash-tts`, qualidade de est\xFAdio, melhor para di\xE1logo e narra\xE7\xE3o longa) aceitam: vozes da biblioteca (milhares, por idioma; liste com `list_tts_voices` e passe o id em `voice`), tags vocais no texto como <laugh> <chuckle> <sigh> <breath> <gasp> <cough> <sneeze> <throat-clearing> <yawn> <groan> <scream> <whimper> <argh> <short pause> <long pause> (viram som de verdade), e estilo por locutor (`style` em cada speaker). No 3.8, `style_instruction` \xE9 dire\xE7\xE3o e N\xC3O \xE9 lida em voz alta. Os modelos legados (`gemini-3.1-flash-tts-preview`, `gemini-2.5-pro-preview-tts`) s\xF3 aceitam as 30 vozes cl\xE1ssicas (Kore, Puck, Charon...) e n\xE3o t\xEAm voz da biblioteca. Vozes pr\xF3prias (voice_xxx; liste com `list_custom_voices`, crie com `design_voice`) s\xF3 no 3.8 e sem `multi_speaker`. No 3.8, dire\xE7\xE3o por linha: `(sussurrando) texto` no in\xEDcio da linha vale at\xE9 a pr\xF3xima dire\xE7\xE3o; no di\xE1logo, `Ana (rindo): fala`; rea\xE7\xF5es `|hmm|` `|aham|` no meio da fala (s\xF3 di\xE1logo). Opcional `style_instruction` (ex.: "fale animado e pausado") e di\xE1logo com 2 locutores via `multi_speaker`. Consome diamantes por segundo de \xE1udio.' + TEAM_GATING_NOTE,
23879
23880
  inputSchema: {
23880
23881
  text: external_exports.string().min(1).max(1e4).describe("Texto a ser falado."),
23881
- voice: topVoiceSchema.default(DEFAULTS.voice).describe(VOICE_HELP_TOP),
23882
+ voice: topVoiceSchema.optional().describe(VOICE_HELP_TOP + " No SeedAudio, omita ou use seed-auto."),
23883
+ seed_reference: external_exports.string().min(1).optional().describe("SeedAudio: arquivo local (stdio) ou URL p\xFAblica de uma voz autorizada, de 1 a 30 s e at\xE9 3 MiB. N\xE3o \xE9 publicado pelo MCP."),
23884
+ seed_reference_generation_id: external_exports.string().uuid().optional().describe("Reutiliza a refer\xEAncia de uma gera\xE7\xE3o SeedAudio sua. Alternativa a seed_reference."),
23885
+ seed_consent: external_exports.literal(true).optional().describe("Confirme apenas ap\xF3s autoriza\xE7\xE3o expl\xEDcita da pessoa para usar sua voz. Obrigat\xF3rio com refer\xEAncia."),
23882
23886
  model: external_exports.enum(TTS_MODELS).default(DEFAULTS.ttsModel).describe("Modelo TTS."),
23883
23887
  style_instruction: external_exports.string().max(2e3).optional().describe("Instru\xE7\xE3o de estilo/tom aplicada \xE0 fala."),
23884
23888
  multi_speaker: external_exports.object({
@@ -23890,9 +23894,17 @@ function registerAudioTools(server, client, cfg) {
23890
23894
  },
23891
23895
  async (args) => {
23892
23896
  try {
23897
+ const seed = args.model === "seed-audio-1.0";
23898
+ const voice = args.voice ?? (seed ? "seed-auto" : DEFAULTS.voice);
23899
+ const hasReference = !!(args.seed_reference || args.seed_reference_generation_id);
23900
+ if (!seed && (hasReference || args.seed_consent || voice === "seed-auto")) return errorResult("Os controles SeedAudio exigem seed-audio-1.0.");
23901
+ if (seed && (voice !== "seed-auto" || args.multi_speaker)) return errorResult("SeedAudio usa seed-auto e n\xE3o aceita di\xE1logo.");
23902
+ if (args.seed_reference && args.seed_reference_generation_id) return errorResult("Use apenas uma origem de refer\xEAncia.");
23903
+ if (hasReference && !args.seed_consent) return errorResult("Confirme a autoriza\xE7\xE3o da voz em seed_consent.");
23904
+ if (seed && args.text.trim().length + (args.style_instruction?.trim().length ?? 0) > 2700) return errorResult("SeedAudio: texto e dire\xE7\xE3o devem somar at\xE9 2700 caracteres.");
23893
23905
  const ms = args.multi_speaker;
23894
- const voices = [args.voice, ms?.speaker1.voice, ms?.speaker2.voice].filter((v) => !!v);
23895
- if (isCustomVoice(args.voice)) {
23906
+ const voices = [voice, ms?.speaker1.voice, ms?.speaker2.voice].filter((v) => !!v);
23907
+ if (isCustomVoice(voice)) {
23896
23908
  if (!TTS_MODELS_38.includes(args.model)) {
23897
23909
  return errorResult("Vozes pr\xF3prias s\xF3 funcionam nos modelos Gemini 3.8 (gemini-3.8-flash-lite-tts, gemini-3.8-flash-tts).");
23898
23910
  }
@@ -23900,7 +23912,7 @@ function registerAudioTools(server, client, cfg) {
23900
23912
  return errorResult("Vozes pr\xF3prias n\xE3o funcionam no modo di\xE1logo (multi_speaker). Use uma voz da biblioteca ou cl\xE1ssica.");
23901
23913
  }
23902
23914
  }
23903
- if (!TTS_MODELS_38.includes(args.model)) {
23915
+ if (!seed && !TTS_MODELS_38.includes(args.model)) {
23904
23916
  if (voices.some(isLibraryVoice)) {
23905
23917
  return errorResult(
23906
23918
  `Vozes da biblioteca s\xF3 funcionam nos modelos Gemini 3.8 (gemini-3.8-flash-lite-tts, gemini-3.8-flash-tts). O modelo ${args.model} s\xF3 aceita as 30 vozes cl\xE1ssicas (Kore, Puck, Charon...).`
@@ -23910,9 +23922,18 @@ function registerAudioTools(server, client, cfg) {
23910
23922
  return errorResult("O estilo por locutor (`style`) s\xF3 funciona nos modelos Gemini 3.8.");
23911
23923
  }
23912
23924
  }
23925
+ let seedReference;
23926
+ if (args.seed_reference) {
23927
+ const file = await (isRemoteRef(args.seed_reference) ? loadRemote(args.seed_reference, 3 * 1024 * 1024) : loadLocal(args.seed_reference, 3 * 1024 * 1024));
23928
+ if (!file.buf.length || file.buf.length > 3 * 1024 * 1024) return errorResult("A refer\xEAncia deve ter at\xE9 3 MiB.");
23929
+ seedReference = { audio: file.buf.toString("base64"), mime: file.mime };
23930
+ }
23913
23931
  const body2 = compact({
23932
+ seedReference,
23933
+ seedReferenceGenerationId: args.seed_reference_generation_id,
23934
+ seedConsent: args.seed_consent,
23914
23935
  text: args.text,
23915
- voice: args.voice,
23936
+ voice,
23916
23937
  model: args.model,
23917
23938
  styleInstruction: args.style_instruction,
23918
23939
  multiSpeaker: args.multi_speaker,
@@ -23922,12 +23943,13 @@ function registerAudioTools(server, client, cfg) {
23922
23943
  const parts = [`Custo: ${res.diamond_cost} \u{1F48E}`];
23923
23944
  if (res.diamonds_refunded) parts.push(`Estorno: ${res.diamonds_refunded} \u{1F48E}`);
23924
23945
  const projLine = res.project_code ? `
23925
- \u{1F4C1} Projeto: ${res.project_code}${res.project_name ? ` \u2014 ${res.project_name}` : ""}` : "";
23946
+ \u{1F4C1} Projeto: ${res.project_code}${res.project_name ? `: ${res.project_name}` : ""}` : "";
23926
23947
  const wa = weeklyAllowanceLine(res.weekly_allowance);
23927
23948
  const budget = budgetLine(res.budget);
23928
23949
  return textResult(
23929
- `\u2705 \xC1udio gerado (${res.model}, voz ${res.voice}, ${res.duration_sec.toFixed(1)}s).
23950
+ `\u2705 \xC1udio gerado (${res.model}, voz ${seed ? "por refer\xEAncia ou descri\xE7\xE3o" : res.voice}, ${res.duration_sec.toFixed(1)}s).
23930
23951
  ${res.url}
23952
+ Gera\xE7\xE3o: ${res.generation_id}
23931
23953
  ${parts.join(" \xB7 ")}${projLine}` + (budget ? `
23932
23954
  ${budget}` : "") + (wa ? `
23933
23955
  ${wa}` : "")
@@ -24773,7 +24795,7 @@ var KINDS = {
24773
24795
  data: { model: "gemini-3.8-flash-lite-tts", voice: "Algieba", speed: "normal" },
24774
24796
  inputs: ["text"],
24775
24797
  outputs: ["audio", "approved"],
24776
- dynamic: "style conforme o modelo"
24798
+ dynamic: "style conforme o modelo; SeedAudio 1.0 tem reference (audio), usa voice: seed-auto e seedConsent: true com refer\xEAncia; alternativa seedReferenceGenerationId de gera\xE7\xE3o pr\xF3pria"
24777
24799
  },
24778
24800
  music: {
24779
24801
  title: "Trilha",
@@ -27823,7 +27845,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
27823
27845
  import { createRequire } from "module";
27824
27846
  var require2 = createRequire(import.meta.url);
27825
27847
  function readVersion() {
27826
- if ("0.41.0") return "0.41.0";
27848
+ if ("0.42.0") return "0.42.0";
27827
27849
  try {
27828
27850
  const pkg = require2("../package.json");
27829
27851
  return pkg.version ?? "0.0.0-unknown";