beplus-mcp 0.21.1 → 0.23.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
@@ -88,7 +88,7 @@ Reinicie o cliente. Rode a tool **`whoami`** para confirmar o vínculo.
88
88
  | `check_generation` | Status de uma geração async por id. |
89
89
  | `cancel_generation` | Cancela uma geração em fila/processamento (estorna diamantes). |
90
90
  | `estimate_cost` | Custo em diamantes antes de gerar. |
91
- | `read_image_metadata` / `analyze_media` | Lê a proveniência embutida numa imagem / analisa mídia (imagem/vídeo/áudio/PDF) com prompt. |
91
+ | `read_image_metadata` / `analyze_media` | Lê a proveniência embutida numa imagem / analisa mídia (imagem/vídeo/áudio/PDF) com prompt, em Gemini, Claude ou GPT. O apelido do modelo vai em `model` e o catálogo por faixa de preço (econômico, médio, top) sai no `list_models`. Vídeo e áudio só nos `gemini-*`. |
92
92
 
93
93
  > **Referências de arquivo local (desde 0.8.1):** os campos de mídia — `reference_images`,
94
94
  > `first_frame_image`, `last_frame_image`, `reference_videos`, `reference_audios`, `motion_video`
package/dist/index.js CHANGED
@@ -21529,6 +21529,63 @@ function budgetLine(b) {
21529
21529
  if (exhausted) line += " \u2014 pe\xE7a a um admin para recarregar";
21530
21530
  return line;
21531
21531
  }
21532
+ var TIER_ORDER = ["economy", "mid", "top"];
21533
+ var TIER_LABEL = {
21534
+ economy: "\u{1F49A} Econ\xF4mico (r\xE1pido e barato)",
21535
+ mid: "\u{1F49B} M\xE9dio (equil\xEDbrio)",
21536
+ top: "\u2764\uFE0F Top (mais capaz, mais caro)"
21537
+ };
21538
+ var TIER_ALIAS = {
21539
+ economico: "economy",
21540
+ medio: "mid"
21541
+ };
21542
+ function normalizeTier(tier) {
21543
+ if (!tier) return "";
21544
+ const normalized = tier.normalize("NFD").replace(/[̀-ͯ]/g, "").trim().toLowerCase();
21545
+ return TIER_ALIAS[normalized] ?? normalized;
21546
+ }
21547
+ function usd(value) {
21548
+ return `$${value.toFixed(2)}`;
21549
+ }
21550
+ function acceptsLine(accepts) {
21551
+ const parts = [];
21552
+ if (accepts.images > 0) parts.push(`imagem (at\xE9 ${accepts.images})`);
21553
+ if (accepts.videos > 0) parts.push(`v\xEDdeo (at\xE9 ${accepts.videos})`);
21554
+ if (accepts.audios > 0) parts.push(`\xE1udio (at\xE9 ${accepts.audios})`);
21555
+ if (accepts.pdfs > 0) parts.push(`PDF (at\xE9 ${accepts.pdfs})`);
21556
+ return parts.length ? parts.join(", ") : "s\xF3 texto";
21557
+ }
21558
+ function analyzeModelLine(m) {
21559
+ const provider = m.provider ? ` \xB7 ${m.provider}` : "";
21560
+ return ` \u2022 ${m.model} \xB7 ${m.label}${provider}
21561
+ ${usd(m.inputCostPerMillion)} entrada / ${usd(m.outputCostPerMillion)} sa\xEDda por 1M tokens \xB7 aceita ${acceptsLine(m.accepts)}`;
21562
+ }
21563
+ function analyzeCatalogBlock(models) {
21564
+ if (!models?.length) return "";
21565
+ const groups = /* @__PURE__ */ new Map();
21566
+ for (const m of models) {
21567
+ const key = normalizeTier(typeof m.tier === "string" ? m.tier : void 0);
21568
+ const bucket = groups.get(key);
21569
+ if (bucket) bucket.push(m);
21570
+ else groups.set(key, [m]);
21571
+ }
21572
+ const known = TIER_ORDER.filter((t) => groups.has(t));
21573
+ const unknown2 = [...groups.keys()].filter((k) => !TIER_ORDER.includes(k));
21574
+ const untiered = groups.size === 1 && groups.has("");
21575
+ const sections = [...known, ...unknown2].map((key) => {
21576
+ const bucket = groups.get(key);
21577
+ const lines = bucket.map(analyzeModelLine).join("\n");
21578
+ if (untiered) return lines;
21579
+ const fallback = bucket[0]?.tierLabel?.trim();
21580
+ const heading = TIER_LABEL[key] ?? (fallback || (key ? `Faixa ${key}` : "Sem faixa definida"));
21581
+ return `${heading}:
21582
+ ${lines}`;
21583
+ });
21584
+ const videoAliases = models.filter((m) => m.accepts.videos > 0).map((m) => m.model);
21585
+ const videoHint = videoAliases.length && videoAliases.length < models.length ? `
21586
+ V\xEDdeo e \xE1udio s\xF3 em: ${videoAliases.join(", ")}.` : "";
21587
+ return "\n\nAn\xE1lise multimodal (tool analyze_media \xB7 prompt + m\xEDdia \u2192 texto):\n" + sections.join("\n") + "\nPasse o apelido em `model`." + videoHint;
21588
+ }
21532
21589
  function textResult(s) {
21533
21590
  return { content: [{ type: "text", text: s }] };
21534
21591
  }
@@ -21967,10 +22024,10 @@ ${BUDGET_BLOCKED_GUIDANCE}`);
21967
22024
  // limite; o Kling publica 2500 e o backend recusa dizendo quanto cortar.
21968
22025
  prompt: external_exports.string().min(1).describe("Descri\xE7\xE3o do v\xEDdeo desejado."),
21969
22026
  model: external_exports.enum(VIDEO_MODELS).default(DEFAULTS.videoModel).describe(
21970
- "seedance-2.0-fast = barato/r\xE1pido; seedance-2.5 = at\xE9 30s num take e 50 refs (m\xE1x 720p); seedance-2.0 = \xFAnico com 1080p/4K; kling-v3-video = alternativa."
22027
+ "seedance-2.0-fast = barato/r\xE1pido; seedance-2.5 = at\xE9 30s num take, 50 refs e at\xE9 1080p; seedance-2.0 = \xFAnico com 4K; kling-v3-video = alternativa."
21971
22028
  ),
21972
22029
  aspect_ratio: external_exports.enum(VIDEO_ASPECT_RATIOS).optional().describe("Propor\xE7\xE3o. Padr\xE3o 16:9. `adaptive` (herda dos inputs) s\xF3 no seedance-2.5."),
21973
- resolution: external_exports.enum(VIDEO_RESOLUTIONS).optional().describe("480p/720p/1080p/4K (1080p/4K S\xD3 no seedance-2.0 standard; 2.5, fast e mini aceitam apenas 480p/720p). Padr\xE3o 720p."),
22030
+ resolution: external_exports.enum(VIDEO_RESOLUTIONS).optional().describe("480p/720p/1080p/4K (4K S\xD3 no seedance-2.0 standard; o 2.5 vai at\xE9 1080p; fast e mini param em 720p). Padr\xE3o 720p."),
21974
22031
  duration: external_exports.number().int().min(3).max(60).optional().describe(
21975
22032
  "Dura\xE7\xE3o em segundos. Seedance 2.5: 4\u201330 (granular). Seedance 2.0/2.0-fast: 4\u201315. Kling: 5 ou 10. O backend valida por modelo \u2014 consulte list_models p/ os valores aceitos. Padr\xE3o 5."
21976
22033
  ),
@@ -21979,24 +22036,48 @@ ${BUDGET_BLOCKED_GUIDANCE}`);
21979
22036
  generate_audio: external_exports.boolean().optional().describe("Gerar trilha de \xE1udio junto com o v\xEDdeo (Seedance/Kling). Na web fica ligado por padr\xE3o."),
21980
22037
  first_frame_image: external_exports.string().min(1).optional().describe("Imagem inicial (image-to-video / primeiro frame): URL p\xFAblica OU caminho local (sobe pro R2)."),
21981
22038
  last_frame_image: external_exports.string().min(1).optional().describe("Imagem do \xFAltimo frame (Seedance/Kling): URL p\xFAblica OU caminho local (sobe pro R2)."),
21982
- reference_images: external_exports.array(external_exports.string().min(1)).max(9).optional().describe("Imagens de refer\xEAncia (personagem/estilo) \u2014 omni/Seedance. URL p\xFAblica OU caminho local. At\xE9 9."),
21983
- reference_videos: external_exports.array(external_exports.string().min(1)).max(3).optional().describe("V\xEDdeos de refer\xEAncia (Seedance reference_videos / Kling reference_video, m\xE1x 3): URL OU caminho local."),
21984
- reference_audios: external_exports.array(external_exports.string().min(1)).max(3).optional().describe("\xC1udios de refer\xEAncia (Seedance, m\xE1x 3): URL p\xFAblica OU caminho local (sobe pro R2)."),
22039
+ // Os tetos aqui são os do MODELO MAIS PERMISSIVO (seedance-2.5:
22040
+ // 30 imagens / 10 vídeos / 10 áudios). Quem for além do que o modelo
22041
+ // escolhido aceita leva a mensagem do backend, que conhece o limite
22042
+ // por modelo — cortar em 9/3/3 aqui só escondia metade do 2.5.
22043
+ reference_images: external_exports.array(external_exports.string().min(1)).max(30).optional().describe("Imagens de refer\xEAncia (personagem/estilo) \u2014 omni/Seedance. URL p\xFAblica OU caminho local. At\xE9 30 no 2.5, 9 no 2.0."),
22044
+ reference_videos: external_exports.array(external_exports.string().min(1)).max(10).optional().describe("V\xEDdeos de refer\xEAncia (Seedance reference_videos / Kling reference_video): URL OU caminho local. At\xE9 10 no 2.5 (cada um de 1,8s a 30s), 3 no 2.0."),
22045
+ reference_audios: external_exports.array(external_exports.string().min(1)).max(10).optional().describe("\xC1udios de refer\xEAncia: URL p\xFAblica OU caminho local. At\xE9 10 no 2.5 (onde o \xE1udio pode vir sozinho), 3 no 2.0."),
21985
22046
  motion_video: external_exports.string().min(1).optional().describe("V\xEDdeo de movimento/m\xE1scara din\xE2mica (kling-v2.6-motion-control): URL OU caminho local."),
21986
22047
  character_orientation: external_exports.string().optional().describe("Orienta\xE7\xE3o do personagem (alguns modelos Kling via Replicate)."),
21987
22048
  keep_original_sound: external_exports.boolean().optional().describe("Manter o som original do v\xEDdeo de refer\xEAncia (alguns modelos Kling)."),
21988
22049
  reference_image: external_exports.string().min(1).optional().describe("[Obsoleto \u2014 use first_frame_image] Imagem de refer\xEAncia: URL p\xFAblica OU caminho local."),
21989
22050
  seed: external_exports.number().int().optional(),
22051
+ output_format: external_exports.enum(["mp4", "mov"]).optional().describe("Formato do arquivo (s\xF3 seedance-2.5). Padr\xE3o mp4; `mov` \xE9 H.264 4:4:4 + PCM, para p\xF3s-produ\xE7\xE3o \u2014 nem todo player abre."),
22052
+ video_task_type: external_exports.enum(["auto", "reference", "edit", "extend"]).optional().describe(
22053
+ "O que fazer com os v\xEDdeos de refer\xEAncia (s\xF3 seedance-2.5): `reference` gera um v\xEDdeo novo; `edit` edita o original (exige 1 v\xEDdeo de 4-30s, a dura\xE7\xE3o vira a dele); `extend` estende o original. Sem isto o modelo adivinha e um par\xE2metro incompat\xEDvel s\xF3 falha DEPOIS da fila."
22054
+ ),
21990
22055
  project: external_exports.string().max(64).optional().describe('Projeto (uuid OU code curto, ex.: "VRAO-26"). S\xF3 omita se o usu\xE1rio J\xC1 confirmou o projeto ativo nesta sess\xE3o.')
21991
22056
  }
21992
22057
  },
21993
22058
  async (args) => {
21994
22059
  try {
21995
- if ((args.model === "bytedance/seedance-2.0-mini" || args.model === "bytedance/seedance-2.0-fast" || args.model === "bytedance/seedance-2.5") && args.resolution && args.resolution !== "480p" && args.resolution !== "720p") {
22060
+ const MAX_RESOLUTION = {
22061
+ "bytedance/seedance-2.0-mini": ["480p", "720p"],
22062
+ "bytedance/seedance-2.0-fast": ["480p", "720p"],
22063
+ "bytedance/seedance-2.5": ["480p", "720p", "1080p"]
22064
+ };
22065
+ const allowedResolutions = args.model ? MAX_RESOLUTION[args.model] : void 0;
22066
+ if (allowedResolutions && args.resolution && !allowedResolutions.includes(args.resolution)) {
21996
22067
  return errorResult(
21997
- `${args.model} s\xF3 suporta 480p ou 720p (voc\xEA pediu ${args.resolution}). Use seedance-2.0 (standard) para 1080p/4K.`
22068
+ `${args.model} s\xF3 suporta ${allowedResolutions.join(" ou ")} (voc\xEA pediu ${args.resolution}). Use seedance-2.0 (standard) para 4K.`
21998
22069
  );
21999
22070
  }
22071
+ const isSeedance25 = args.model === "bytedance/seedance-2.5";
22072
+ if (!isSeedance25 && args.output_format && args.output_format !== "mp4") {
22073
+ return errorResult(`output_format ${args.output_format} s\xF3 existe no seedance-2.5 (voc\xEA pediu ${args.model}).`);
22074
+ }
22075
+ if (!isSeedance25 && args.video_task_type && args.video_task_type !== "auto") {
22076
+ return errorResult(`Editar e estender v\xEDdeo s\xF3 existe no seedance-2.5 (voc\xEA pediu ${args.model}).`);
22077
+ }
22078
+ if ((args.video_task_type === "edit" || args.video_task_type === "extend") && !(args.reference_videos && args.reference_videos.length > 0)) {
22079
+ return errorResult(`video_task_type "${args.video_task_type}" exige o v\xEDdeo original em reference_videos.`);
22080
+ }
22000
22081
  if (args.model?.startsWith("bytedance/seedance-") && args.aspect_ratio) {
22001
22082
  const SEEDANCE_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "21:9", "adaptive"];
22002
22083
  if (!SEEDANCE_RATIOS.includes(args.aspect_ratio)) {
@@ -22041,7 +22122,9 @@ ${BUDGET_BLOCKED_GUIDANCE}`);
22041
22122
  motion_video: motionVideo,
22042
22123
  character_orientation: args.character_orientation,
22043
22124
  keep_original_sound: args.keep_original_sound,
22044
- seed: args.seed
22125
+ seed: args.seed,
22126
+ output_format: args.output_format,
22127
+ omni_reference_task_type: args.video_task_type
22045
22128
  });
22046
22129
  const start = await client.startGeneration(
22047
22130
  compact({ model: args.model, input, project: args.project ?? cfg.activeProject ?? void 0 })
@@ -22878,7 +22961,7 @@ ${budget}` : "")
22878
22961
  "list_models",
22879
22962
  {
22880
22963
  title: "Listar modelos",
22881
- description: "Lista os modelos dispon\xEDveis com pre\xE7o-base e limites por usu\xE1rio. Use quando estiver em d\xFAvida sobre qual modelo/params usar ou quanto custa. \xC9 a fonte de verdade em runtime.",
22964
+ description: "Lista os modelos dispon\xEDveis com pre\xE7o-base e limites por usu\xE1rio, e o cat\xE1logo de an\xE1lise multimodal (Gemini, Claude e GPT) agrupado por faixa de pre\xE7o. Use quando estiver em d\xFAvida sobre qual modelo/params usar ou quanto custa. \xC9 a fonte de verdade em runtime.",
22882
22965
  inputSchema: {
22883
22966
  kind: external_exports.enum(["image", "video", "audio", "all"]).default("all").optional().describe("Filtrar por tipo.")
22884
22967
  }
@@ -22890,13 +22973,7 @@ ${budget}` : "")
22890
22973
  const price = typeof x.pricing === "number" ? `$${x.pricing}` : JSON.stringify(x.pricing);
22891
22974
  return `\u2022 ${x.model} \u2014 ${price} (base USD)`;
22892
22975
  });
22893
- const analyzeLines = (m.analyze ?? []).map(
22894
- (a) => `\u2022 ${a.model} (${a.label}) \u2014 $${a.inputCostPerMillion}/$${a.outputCostPerMillion} por 1M tok in/out \xB7 aceita img=${a.accepts.images} vid=${a.accepts.videos} \xE1udio=${a.accepts.audios} pdf=${a.accepts.pdfs}`
22895
- );
22896
- const analyzeBlock = analyzeLines.length ? `
22897
-
22898
- Modelos de an\xE1lise multimodal (tool analyze_media \u2014 prompt + m\xEDdia \u2192 texto):
22899
- ${analyzeLines.join("\n")}` : "";
22976
+ const analyzeBlock = analyzeCatalogBlock(m.analyze);
22900
22977
  const txt = `Modelos de gera\xE7\xE3o:
22901
22978
  ${lines.join("\n") || "(nenhum)"}` + analyzeBlock + `
22902
22979
 
@@ -22983,14 +23060,18 @@ function registerAnalyzeTools(server, client, cfg) {
22983
23060
  server.registerTool(
22984
23061
  "analyze_media",
22985
23062
  {
22986
- title: "Analisar m\xEDdia (Gemini)",
22987
- description: "Envia um prompt + m\xEDdia (imagem, v\xEDdeo, \xE1udio ou PDF) para o Gemini e devolve uma resposta em texto. Use para descrever/analisar/transcrever/resumir/extrair informa\xE7\xE3o de m\xEDdia, ou perguntar algo sobre ela. A m\xEDdia entra como `media_urls` (URLs p\xFAblicas \u2014 tudo que o Google aceita: imagens, v\xEDdeos, \xE1udios, PDFs). V\xEDdeo/\xE1udio grandes funcionam (v\xE3o pela Files API do Google). Consome diamantes (reembolsados se falhar). Veja custo/limites com list_models.",
23063
+ title: "Analisar m\xEDdia (Gemini, Claude e GPT)",
23064
+ description: "Envia um prompt + m\xEDdia (imagem, v\xEDdeo, \xE1udio ou PDF) para um modelo de texto e devolve a resposta. Use para descrever/analisar/transcrever/resumir/extrair informa\xE7\xE3o de m\xEDdia, ou perguntar algo sobre ela. Tamb\xE9m funciona s\xF3 com texto, sem m\xEDdia. Tr\xEAs faixas de pre\xE7o: econ\xF4mica, m\xE9dia e top. A m\xEDdia entra como `media_urls` (URLs p\xFAblicas ou caminhos locais). V\xEDdeo e \xE1udio s\xF3 nos modelos Gemini, que leem arquivos grandes pela Files API do Google. Consome diamantes (reembolsados se falhar). O cat\xE1logo vivo, com pre\xE7o por 1M de tokens e m\xEDdia aceita por modelo, sai no list_models.",
22988
23065
  inputSchema: {
22989
23066
  prompt: external_exports.string().min(1).max(5e4).describe('O que voc\xEA quer do modelo sobre a m\xEDdia (ex.: "descreva este v\xEDdeo", "transcreva este \xE1udio").'),
22990
23067
  media_urls: external_exports.array(external_exports.string().min(1)).max(50).optional().describe(
22991
23068
  "M\xEDdia (imagem/v\xEDdeo/\xE1udio/PDF): URLs p\xFAblicas OU caminhos de arquivo local (o MCP sobe pro R2 automaticamente). Pode misturar tipos. Opcional (prompt s\xF3-texto tamb\xE9m funciona)."
22992
23069
  ),
22993
- model: external_exports.enum(["gemini-flash", "gemini-pro"]).default("gemini-flash").optional().describe("gemini-flash = r\xE1pido/barato (Gemini 3.5 Flash); gemini-pro = mais capaz (Gemini 3.1 Pro)."),
23070
+ // String livre, não z.enum: quem valida o apelido é o backend, então um
23071
+ // modelo novo no catálogo passa a valer sem republicar o pacote.
23072
+ model: external_exports.string().min(1).default("gemini-flash").optional().describe(
23073
+ "Apelido do modelo. Econ\xF4micos: gemini-lite, gpt-mini, claude-haiku. M\xE9dios: gemini-flash (padr\xE3o), gpt, claude-sonnet. Top: gemini-pro, claude-opus, gpt-pro, claude-fable. V\xEDdeo e \xE1udio s\xF3 nos gemini-*; PDF nos gemini-* e claude-*; os gpt-* leem s\xF3 imagem. Lista autoritativa, com pre\xE7os: list_models."
23074
+ ),
22994
23075
  project: external_exports.string().optional().describe(
22995
23076
  'Projeto (uuid ou code curto, ex.: "ICONS") de cujo budget cobrar. Omitido \u2192 usa o projeto ativo da conta.'
22996
23077
  )
@@ -23294,7 +23375,7 @@ ${calls.map(callLine).join("\n\n")}`);
23294
23375
  import { createRequire } from "module";
23295
23376
  var require2 = createRequire(import.meta.url);
23296
23377
  function readVersion() {
23297
- if ("0.21.1") return "0.21.1";
23378
+ if ("0.23.0") return "0.23.0";
23298
23379
  try {
23299
23380
  const pkg = require2("../package.json");
23300
23381
  return pkg.version ?? "0.0.0-unknown";