beplus-mcp 0.40.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
@@ -12,6 +12,25 @@ Claude Code/Desktop/Cursor ──stdio──▶ beplus-mcp ──HTTPS (Beare
12
12
 
13
13
  Você se autentica com um **Personal Access Token (PAT)** gerado no painel da BePlus. O MCP só age em nome da SUA conta.
14
14
 
15
+ ## Jeito mais fácil: conector por URL (sem Node, sem token)
16
+
17
+ A API da BePlus serve as mesmas ferramentas por HTTP, com login pelo navegador (OAuth).
18
+
19
+ **Claude Code:**
20
+
21
+ ```bash
22
+ claude mcp add --transport http --scope user beplus https://api.beplus.academy/api/v1/mcp
23
+ claude mcp login beplus
24
+ ```
25
+
26
+ O navegador abre, você entra na BePlus e clica em **Autorizar acesso**. Reinicie o Claude Code.
27
+
28
+ **Claude Desktop e claude.ai:** Configurações > Conectores > Adicionar conector personalizado, cole
29
+ `https://api.beplus.academy/api/v1/mcp`, clique em Conectar e em Autorizar acesso.
30
+
31
+ No conector por URL as ferramentas rodam no servidor: arquivo de mídia vai por **URL pública**, não por caminho do
32
+ seu computador. Para subir arquivo local, use o pacote abaixo.
33
+
15
34
  ## 1. Gere um Personal Access Token
16
35
 
17
36
  No app da BePlus → **Configurações da conta → Tokens de acesso** → *Criar token*. Copie o token (`pat_…`) — ele só aparece uma vez.
@@ -84,7 +103,7 @@ Reinicie o cliente. Rode a tool **`whoami`** para confirmar o vínculo.
84
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). |
85
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. |
86
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. |
87
- | `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. |
88
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. |
89
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. |
90
109
  | `design_voice` | Cria uma voz própria a partir de uma descrição. Gasta 💎 e leva ~30 s. |
@@ -213,3 +232,13 @@ BEPLUS_API_TOKEN=pat_… npx @modelcontextprotocol/inspector node dist/index.js
213
232
  - **stdout** é o canal JSON-RPC — todo log vai pra **stderr** e o token nunca é logado.
214
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.
215
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
@@ -3303,8 +3303,8 @@ var require_utils = __commonJS({
3303
3303
  var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
3304
3304
  var HOST_DELIM_RE = /[@/?#:]/g;
3305
3305
  var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
3306
- function reescapeHostDelimiters(host, isIP) {
3307
- const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3306
+ function reescapeHostDelimiters(host, isIP2) {
3307
+ const re = isIP2 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3308
3308
  re.lastIndex = 0;
3309
3309
  return host.replace(re, (ch) => HOST_DELIMS[ch]);
3310
3310
  }
@@ -3786,7 +3786,7 @@ var require_fast_uri = __commonJS({
3786
3786
  fragment: void 0
3787
3787
  };
3788
3788
  let malformedAuthorityOrPort = false;
3789
- let isIP = false;
3789
+ let isIP2 = false;
3790
3790
  if (options.reference === "suffix") {
3791
3791
  if (options.scheme) {
3792
3792
  uri = options.scheme + ":" + uri;
@@ -3816,9 +3816,9 @@ var require_fast_uri = __commonJS({
3816
3816
  if (ipv4result === false) {
3817
3817
  const ipv6result = normalizeIPv6(parsed.host);
3818
3818
  parsed.host = ipv6result.host.toLowerCase();
3819
- isIP = ipv6result.isIPV6;
3819
+ isIP2 = ipv6result.isIPV6;
3820
3820
  } else {
3821
- isIP = true;
3821
+ isIP2 = true;
3822
3822
  }
3823
3823
  }
3824
3824
  if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) {
@@ -3835,7 +3835,7 @@ var require_fast_uri = __commonJS({
3835
3835
  }
3836
3836
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
3837
3837
  if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3838
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3838
+ if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
3839
3839
  try {
3840
3840
  parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
3841
3841
  } catch (e) {
@@ -3849,7 +3849,7 @@ var require_fast_uri = __commonJS({
3849
3849
  parsed.scheme = unescape(parsed.scheme);
3850
3850
  }
3851
3851
  if (parsed.host !== void 0) {
3852
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
3852
+ parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP2);
3853
3853
  }
3854
3854
  }
3855
3855
  if (parsed.path) {
@@ -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 });
@@ -21749,6 +21749,69 @@ async function pollUntilDone(client, generationId, budgetMs, jitterSeed = 0) {
21749
21749
 
21750
21750
  // src/media.ts
21751
21751
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
21752
+
21753
+ // src/remote.ts
21754
+ import { lookup } from "dns/promises";
21755
+ import { isIP } from "net";
21756
+ var remote = false;
21757
+ function refuseLocalPath(ref) {
21758
+ if (!remote) return;
21759
+ throw new ApiError(
21760
+ 0,
21761
+ `"${ref}" \xE9 um caminho de arquivo, e esta conex\xE3o roda no servidor da BePlus, que n\xE3o enxerga o seu computador. Mande uma URL p\xFAblica (http/https) ou suba o arquivo pela plataforma e use o link dele.`
21762
+ );
21763
+ }
21764
+ var MAX_REDIRECTS = 5;
21765
+ function v4Private(ip) {
21766
+ const [a, b] = ip.split(".").map(Number);
21767
+ return a === 0 || a === 10 || a === 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 100 && b >= 64 && b <= 127 || a >= 224;
21768
+ }
21769
+ function privateAddress(ip) {
21770
+ if (isIP(ip) === 4) return v4Private(ip);
21771
+ const v6 = ip.toLowerCase();
21772
+ const dotted = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(v6);
21773
+ if (dotted) return v4Private(dotted[1]);
21774
+ const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(v6);
21775
+ if (hex) {
21776
+ const hi = parseInt(hex[1], 16);
21777
+ const lo = parseInt(hex[2], 16);
21778
+ return v4Private(`${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`);
21779
+ }
21780
+ return v6 === "::" || v6 === "::1" || /^f[cd]/.test(v6) || /^fe[89ab]/.test(v6) || /^ff/.test(v6);
21781
+ }
21782
+ async function assertPublic(url) {
21783
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
21784
+ throw new ApiError(0, `S\xF3 baixo endere\xE7o http ou https: ${url.href}`);
21785
+ }
21786
+ const host = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
21787
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".internal") || host.endsWith(".local")) {
21788
+ throw new ApiError(0, `Endere\xE7o interno n\xE3o \xE9 aceito: ${url.host}`);
21789
+ }
21790
+ const addrs = isIP(host) ? [{ address: host }] : await lookup(host, { all: true }).catch(() => []);
21791
+ if (addrs.length === 0) throw new ApiError(0, `N\xE3o encontrei o endere\xE7o ${url.host}.`);
21792
+ if (addrs.some((a) => privateAddress(a.address))) {
21793
+ throw new ApiError(0, `Endere\xE7o de rede privada n\xE3o \xE9 aceito: ${url.host}`);
21794
+ }
21795
+ }
21796
+ async function safeFetch(input, init = {}) {
21797
+ if (!remote) return fetch(input, init);
21798
+ let url = new URL(input);
21799
+ for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
21800
+ await assertPublic(url);
21801
+ const res = await fetch(url, { ...init, redirect: "manual" });
21802
+ const location = res.headers.get("location");
21803
+ if (res.status >= 300 && res.status < 400 && location) {
21804
+ await res.body?.cancel().catch(() => {
21805
+ });
21806
+ url = new URL(location, url);
21807
+ continue;
21808
+ }
21809
+ return res;
21810
+ }
21811
+ throw new ApiError(0, `Redirecionamentos demais ao baixar ${input}.`);
21812
+ }
21813
+
21814
+ // src/media.ts
21752
21815
  import { basename, extname, isAbsolute, resolve } from "path";
21753
21816
  import { homedir as homedir2 } from "os";
21754
21817
  import { fileURLToPath } from "url";
@@ -21836,6 +21899,7 @@ function isRemoteRef(ref) {
21836
21899
  return /^https?:\/\//i.test(ref);
21837
21900
  }
21838
21901
  function toLocalPath(ref) {
21902
+ refuseLocalPath(ref);
21839
21903
  if (ref.startsWith("file://")) return fileURLToPath(ref);
21840
21904
  if (ref === "~") return homedir2();
21841
21905
  if (ref.startsWith("~/")) return resolve(homedir2(), ref.slice(2));
@@ -21855,7 +21919,7 @@ function tooLarge(name, bytes) {
21855
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.`
21856
21920
  );
21857
21921
  }
21858
- async function loadLocal(ref) {
21922
+ async function loadLocal(ref, maxBytes = MAX_UPLOAD_BYTES) {
21859
21923
  const abs = toLocalPath(ref);
21860
21924
  const name = basename(abs);
21861
21925
  let size;
@@ -21867,16 +21931,17 @@ async function loadLocal(ref) {
21867
21931
  `Arquivo local n\xE3o encontrado: "${ref}". Passe um caminho v\xE1lido ou uma URL p\xFAblica (http/https).`
21868
21932
  );
21869
21933
  }
21870
- 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.`);
21871
21935
  return { buf: await readFile2(abs), name, mime: mimeForName(abs), localPath: abs };
21872
21936
  }
21873
- async function loadRemote(url) {
21937
+ async function loadRemote(url, maxBytes = MAX_UPLOAD_BYTES) {
21874
21938
  const u = new URL(url);
21875
21939
  let name = decodeURIComponent(basename(u.pathname)) || "arquivo";
21876
21940
  let res;
21877
21941
  try {
21878
- res = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
21879
- } catch {
21942
+ res = await safeFetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
21943
+ } catch (e) {
21944
+ if (e instanceof ApiError) throw e;
21880
21945
  throw new ApiError(0, `N\xE3o consegui baixar ${url} (rede ou tempo esgotado).`);
21881
21946
  }
21882
21947
  if (!res.ok) {
@@ -21885,10 +21950,10 @@ async function loadRemote(url) {
21885
21950
  throw new ApiError(res.status, `N\xE3o consegui baixar ${url}: a origem respondeu HTTP ${res.status}.`);
21886
21951
  }
21887
21952
  const declared = Number(res.headers.get("content-length") ?? NaN);
21888
- if (Number.isFinite(declared) && declared > MAX_UPLOAD_BYTES) {
21953
+ if (Number.isFinite(declared) && declared > maxBytes) {
21889
21954
  await res.body?.cancel().catch(() => {
21890
21955
  });
21891
- 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.`);
21892
21957
  }
21893
21958
  const chunks = [];
21894
21959
  let total = 0;
@@ -21899,10 +21964,10 @@ async function loadRemote(url) {
21899
21964
  const { done, value } = await reader.read();
21900
21965
  if (done) break;
21901
21966
  total += value.byteLength;
21902
- if (total > MAX_UPLOAD_BYTES) {
21967
+ if (total > maxBytes) {
21903
21968
  await reader.cancel().catch(() => {
21904
21969
  });
21905
- 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.`);
21906
21971
  }
21907
21972
  chunks.push(Buffer.from(value.buffer, value.byteOffset, value.byteLength));
21908
21973
  }
@@ -22217,6 +22282,7 @@ var MUSIC_TASK_TYPES = ["create", "extend", "cover", "upload_extend", "upload_co
22217
22282
  var MUSIC_TOOLS = ["wav", "midi", "stems-basic", "stems-full", "bpm", "lyrics-timeline"];
22218
22283
  var VOCAL_GENDERS = ["f", "m"];
22219
22284
  var TTS_MODELS = [
22285
+ "seed-audio-1.0",
22220
22286
  "gemini-3.8-flash-lite-tts",
22221
22287
  "gemini-3.8-flash-tts",
22222
22288
  "gemini-3.1-flash-tts-preview",
@@ -23218,6 +23284,7 @@ function isRemote(ref) {
23218
23284
  return /^https?:\/\//i.test(ref);
23219
23285
  }
23220
23286
  function toLocalPath2(ref) {
23287
+ refuseLocalPath(ref);
23221
23288
  if (ref.startsWith("file://")) return fileURLToPath2(ref);
23222
23289
  if (ref === "~") return homedir3();
23223
23290
  if (ref.startsWith("~/")) return resolve2(homedir3(), ref.slice(2));
@@ -23243,7 +23310,7 @@ async function readAt(ref, offset, len) {
23243
23310
  const ctrl = new AbortController();
23244
23311
  const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
23245
23312
  try {
23246
- const res = await fetch(ref, {
23313
+ const res = await safeFetch(ref, {
23247
23314
  headers: { Range: `bytes=${offset}-${offset + len - 1}` },
23248
23315
  signal: ctrl.signal
23249
23316
  });
@@ -23286,7 +23353,7 @@ async function readHead(ref, bytes) {
23286
23353
  const ctrl = new AbortController();
23287
23354
  const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
23288
23355
  try {
23289
- const res = await fetch(ref, {
23356
+ const res = await safeFetch(ref, {
23290
23357
  headers: { Range: `bytes=0-${bytes - 1}` },
23291
23358
  signal: ctrl.signal
23292
23359
  });
@@ -23430,7 +23497,7 @@ function probeMp4(b) {
23430
23497
  }
23431
23498
  async function fileSizeOf(ref) {
23432
23499
  if (!isRemote(ref)) return (await stat3(toLocalPath2(ref))).size;
23433
- const res = await fetch(ref, { method: "HEAD" });
23500
+ const res = await safeFetch(ref, { method: "HEAD" });
23434
23501
  return Number(res.headers.get("content-length") ?? 0);
23435
23502
  }
23436
23503
  var ProbeError = class extends Error {
@@ -23790,7 +23857,7 @@ var VOICE_HELP_TOP = `${VOICE_HELP} Ou o id de uma voz pr\xF3pria (voice_xxx, de
23790
23857
  var voiceSchema = external_exports.string().refine((v) => VOICES.includes(v) || LIBRARY_VOICE_ID.test(v), {
23791
23858
  message: "Voz inv\xE1lida: use uma das 30 vozes cl\xE1ssicas (ex.: Kore) ou um id da biblioteca (ex.: pt-br-advisor-10)."
23792
23859
  });
23793
- 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), {
23794
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)."
23795
23862
  });
23796
23863
  function isCustomVoice(v) {
@@ -23809,10 +23876,13 @@ function registerAudioTools(server, client, cfg) {
23809
23876
  "generate_audio",
23810
23877
  {
23811
23878
  title: "Gerar \xE1udio (TTS)",
23812
- 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,
23813
23880
  inputSchema: {
23814
23881
  text: external_exports.string().min(1).max(1e4).describe("Texto a ser falado."),
23815
- 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."),
23816
23886
  model: external_exports.enum(TTS_MODELS).default(DEFAULTS.ttsModel).describe("Modelo TTS."),
23817
23887
  style_instruction: external_exports.string().max(2e3).optional().describe("Instru\xE7\xE3o de estilo/tom aplicada \xE0 fala."),
23818
23888
  multi_speaker: external_exports.object({
@@ -23824,9 +23894,17 @@ function registerAudioTools(server, client, cfg) {
23824
23894
  },
23825
23895
  async (args) => {
23826
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.");
23827
23905
  const ms = args.multi_speaker;
23828
- const voices = [args.voice, ms?.speaker1.voice, ms?.speaker2.voice].filter((v) => !!v);
23829
- if (isCustomVoice(args.voice)) {
23906
+ const voices = [voice, ms?.speaker1.voice, ms?.speaker2.voice].filter((v) => !!v);
23907
+ if (isCustomVoice(voice)) {
23830
23908
  if (!TTS_MODELS_38.includes(args.model)) {
23831
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).");
23832
23910
  }
@@ -23834,7 +23912,7 @@ function registerAudioTools(server, client, cfg) {
23834
23912
  return errorResult("Vozes pr\xF3prias n\xE3o funcionam no modo di\xE1logo (multi_speaker). Use uma voz da biblioteca ou cl\xE1ssica.");
23835
23913
  }
23836
23914
  }
23837
- if (!TTS_MODELS_38.includes(args.model)) {
23915
+ if (!seed && !TTS_MODELS_38.includes(args.model)) {
23838
23916
  if (voices.some(isLibraryVoice)) {
23839
23917
  return errorResult(
23840
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...).`
@@ -23844,9 +23922,18 @@ function registerAudioTools(server, client, cfg) {
23844
23922
  return errorResult("O estilo por locutor (`style`) s\xF3 funciona nos modelos Gemini 3.8.");
23845
23923
  }
23846
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
+ }
23847
23931
  const body2 = compact({
23932
+ seedReference,
23933
+ seedReferenceGenerationId: args.seed_reference_generation_id,
23934
+ seedConsent: args.seed_consent,
23848
23935
  text: args.text,
23849
- voice: args.voice,
23936
+ voice,
23850
23937
  model: args.model,
23851
23938
  styleInstruction: args.style_instruction,
23852
23939
  multiSpeaker: args.multi_speaker,
@@ -23856,12 +23943,13 @@ function registerAudioTools(server, client, cfg) {
23856
23943
  const parts = [`Custo: ${res.diamond_cost} \u{1F48E}`];
23857
23944
  if (res.diamonds_refunded) parts.push(`Estorno: ${res.diamonds_refunded} \u{1F48E}`);
23858
23945
  const projLine = res.project_code ? `
23859
- \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}` : ""}` : "";
23860
23947
  const wa = weeklyAllowanceLine(res.weekly_allowance);
23861
23948
  const budget = budgetLine(res.budget);
23862
23949
  return textResult(
23863
- `\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).
23864
23951
  ${res.url}
23952
+ Gera\xE7\xE3o: ${res.generation_id}
23865
23953
  ${parts.join(" \xB7 ")}${projLine}` + (budget ? `
23866
23954
  ${budget}` : "") + (wa ? `
23867
23955
  ${wa}` : "")
@@ -24707,7 +24795,7 @@ var KINDS = {
24707
24795
  data: { model: "gemini-3.8-flash-lite-tts", voice: "Algieba", speed: "normal" },
24708
24796
  inputs: ["text"],
24709
24797
  outputs: ["audio", "approved"],
24710
- 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"
24711
24799
  },
24712
24800
  music: {
24713
24801
  title: "Trilha",
@@ -26203,10 +26291,10 @@ function withExtension(name, file) {
26203
26291
  return ext && extname3(clean).toLowerCase() !== ext.toLowerCase() ? `${clean}${ext}` : clean;
26204
26292
  }
26205
26293
  async function uploadMedia(cfg, source, name) {
26206
- const remote = isRemoteRef(source);
26207
- const loaded = remote ? await loadRemote(source) : await loadLocal(source);
26294
+ const remote2 = isRemoteRef(source);
26295
+ const loaded = remote2 ? await loadRemote(source) : await loadLocal(source);
26208
26296
  const file = { ...loaded, name: withExtension(name, loaded) };
26209
- const reused = remote && new URL(source).host === BEPLUS_STORAGE_HOST;
26297
+ const reused = remote2 && new URL(source).host === BEPLUS_STORAGE_HOST;
26210
26298
  const url = reused ? source : await uploadBytes(cfg, file);
26211
26299
  const kind = kindOfMime(file.mime);
26212
26300
  let meta = null;
@@ -27757,7 +27845,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
27757
27845
  import { createRequire } from "module";
27758
27846
  var require2 = createRequire(import.meta.url);
27759
27847
  function readVersion() {
27760
- if ("0.40.0") return "0.40.0";
27848
+ if ("0.42.0") return "0.42.0";
27761
27849
  try {
27762
27850
  const pkg = require2("../package.json");
27763
27851
  return pkg.version ?? "0.0.0-unknown";