beplus-mcp 0.13.0 → 0.16.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 +132 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -215,6 +215,10 @@ var TEAM_GATING_NOTE = " \u26A0\uFE0F DESTINO DA GERA\xC7\xC3O: toda imagem/v\xE
|
|
|
215
215
|
function isTeamGatingError(msg) {
|
|
216
216
|
return !!msg && /cliente e um projeto|selecione um cliente|sem cliente vinculado|antes de gerar/i.test(msg);
|
|
217
217
|
}
|
|
218
|
+
function isBudgetBlockedError(msg) {
|
|
219
|
+
return !!msg && /budget/i.test(msg);
|
|
220
|
+
}
|
|
221
|
+
var BUDGET_BLOCKED_GUIDANCE = "\u{1F4A1} O pote deste projeto/cliente acabou. Um admin pode recarregar em Admin \u2192 IA Lab \u2192 Projetos/Clientes. Alternativa: trocar o projeto ativo com `set_active_project`.";
|
|
218
222
|
async function buildPickProjectMessage(client) {
|
|
219
223
|
let clientsTxt = " (n\xE3o foi poss\xEDvel listar clientes)";
|
|
220
224
|
let projectsTxt = " (n\xE3o foi poss\xEDvel listar projetos)";
|
|
@@ -401,6 +405,16 @@ function weeklyAllowanceLine(wa) {
|
|
|
401
405
|
}
|
|
402
406
|
return line;
|
|
403
407
|
}
|
|
408
|
+
function budgetLine(b) {
|
|
409
|
+
if (!b || !b.enabled) return "";
|
|
410
|
+
const scope = b.scope === "client" ? "cliente" : "projeto";
|
|
411
|
+
const pct = b.percentUsed;
|
|
412
|
+
const exhausted = pct >= 100 || b.remainingDiamonds <= 0;
|
|
413
|
+
const icon = exhausted ? "\u{1F6D1}" : pct >= 80 ? "\u26A0\uFE0F" : "\u{1F9FE}";
|
|
414
|
+
let line = `${icon} Budget do ${scope}: ${b.remainingDiamonds} de ${b.quotaDiamonds}\u{1F48E} restantes (${pct}% usado)`;
|
|
415
|
+
if (exhausted) line += " \u2014 pe\xE7a a um admin para recarregar";
|
|
416
|
+
return line;
|
|
417
|
+
}
|
|
404
418
|
function textResult(s) {
|
|
405
419
|
return { content: [{ type: "text", text: s }] };
|
|
406
420
|
}
|
|
@@ -423,10 +437,16 @@ function costFooter(start, cfg) {
|
|
|
423
437
|
footer = `\u26A0 Custo alto (${start.diamond_cost} \u{1F48E})
|
|
424
438
|
${footer}`;
|
|
425
439
|
}
|
|
440
|
+
const budget = budgetLine(start.budget);
|
|
426
441
|
if (start.project_code) {
|
|
427
|
-
|
|
442
|
+
let proj = `\u{1F4C1} Projeto: ${start.project_code}${start.project_name ? ` \u2014 ${start.project_name}` : ""}`;
|
|
443
|
+
if (budget) proj = `${proj}
|
|
444
|
+
${budget}`;
|
|
428
445
|
footer = footer ? `${proj}
|
|
429
446
|
${footer}` : proj;
|
|
447
|
+
} else if (budget) {
|
|
448
|
+
footer = footer ? `${budget}
|
|
449
|
+
${footer}` : budget;
|
|
430
450
|
}
|
|
431
451
|
const wa = weeklyAllowanceLine(start.weekly_allowance);
|
|
432
452
|
if (wa) footer = footer ? `${footer}
|
|
@@ -466,8 +486,12 @@ var IMAGE_MODELS = [
|
|
|
466
486
|
"google/nano-banana-2",
|
|
467
487
|
"google/nano-banana-pro",
|
|
468
488
|
"openai/gpt-image-2",
|
|
489
|
+
"bytedance/seedream-5.0-lite",
|
|
490
|
+
"bytedance/seedream-5.0-pro",
|
|
469
491
|
"midjourney"
|
|
470
492
|
];
|
|
493
|
+
var SEEDREAM_LITE_SIZES = ["2K", "3K", "4K"];
|
|
494
|
+
var SEEDREAM_PRO_SIZES = ["1K", "2K"];
|
|
471
495
|
var VIDEO_MODELS = [
|
|
472
496
|
"bytedance/seedance-2.0",
|
|
473
497
|
"bytedance/seedance-2.0-fast",
|
|
@@ -561,8 +585,13 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
561
585
|
title: "Gerar imagem",
|
|
562
586
|
description: "Gera uma imagem a partir de um prompt usando o IA Lab da BePlus. R\xE1pido (~10-30s). Consome diamantes da conta vinculada. Apenas `prompt` \xE9 obrigat\xF3rio; passe `reference_images` (URLs p\xFAblicas OU caminhos de arquivo local \u2014 o MCP sobe pro R2 automaticamente) para image-to-image. Veja custos com estimate_cost / list_models." + TEAM_GATING_NOTE,
|
|
563
587
|
inputSchema: {
|
|
564
|
-
|
|
565
|
-
|
|
588
|
+
// Sem teto local: quem conhece o limite de cada provedor é o backend, e
|
|
589
|
+
// ele responde com quanto reduzir. Um `.max()` aqui rejeitava prompts
|
|
590
|
+
// que o modelo aceita (Seedream/nano-banana não têm limite publicado).
|
|
591
|
+
prompt: z.string().min(1).describe("Descri\xE7\xE3o da imagem desejada."),
|
|
592
|
+
model: z.enum(IMAGE_MODELS).default(DEFAULTS.imageModel).describe(
|
|
593
|
+
"nano-banana-2 = barato/r\xE1pido; nano-banana-pro = melhor qualidade; gpt-image-2 = OpenAI; seedream-5.0-lite = mais barato do cat\xE1logo, \xFAnico que chega a 4K, gera s\xE9ries e mant\xE9m o ESTILO da imagem de refer\xEAncia; seedream-5.0-pro = acabamento fotorrealista, s\xF3 at\xE9 2K \u2014 preserva o personagem da refer\xEAncia mas costuma reinterpretar o estilo (um vetor flat volta em 3D)."
|
|
594
|
+
),
|
|
566
595
|
aspect_ratio: z.enum(ASPECT_RATIOS).optional().describe("Propor\xE7\xE3o (nano-banana). Padr\xE3o 1:1."),
|
|
567
596
|
resolution: z.enum(IMAGE_RESOLUTIONS).optional().describe("Resolu\xE7\xE3o (nano-banana): 1K/2K/4K. Padr\xE3o 1K."),
|
|
568
597
|
reference_images: z.array(z.string().min(1)).max(16).optional().describe(
|
|
@@ -570,7 +599,12 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
570
599
|
),
|
|
571
600
|
google_search: z.boolean().optional().describe("nano-banana-2: aterra a imagem em busca web do Google (fatos atuais). Padr\xE3o false."),
|
|
572
601
|
image_search: z.boolean().optional().describe("nano-banana-2: usa busca de imagens do Google como refer\xEAncia visual. Padr\xE3o false."),
|
|
573
|
-
size: z.enum(["1024x1024", "1536x1024", "1024x1536", "auto"]).optional().describe(
|
|
602
|
+
size: z.enum(["1024x1024", "1536x1024", "1024x1536", "auto", "1K", "2K", "3K", "4K"]).optional().describe(
|
|
603
|
+
"Tamanho. gpt-image-2: 1024x1024/1536x1024/1024x1536/auto. Seedream Lite: 2K/3K/4K (padr\xE3o 2K). Seedream Pro: 1K/2K apenas \u2014 o Pro N\xC3O faz 3K/4K."
|
|
604
|
+
),
|
|
605
|
+
batch_images: z.number().int().min(2).max(15).optional().describe(
|
|
606
|
+
"Seedream 5.0 LITE apenas: gera uma s\xE9rie de imagens relacionadas numa chamada (2-15). Cada imagem \xE9 cobrada. O Seedream Pro N\xC3O suporta \u2014 passar aqui devolve erro."
|
|
607
|
+
),
|
|
574
608
|
quality: z.enum(["low", "medium", "high", "auto"]).optional().describe("Qualidade (gpt-image-2)."),
|
|
575
609
|
background: z.enum(["opaque", "transparent", "auto"]).optional().describe("Fundo (gpt-image-2). transparent s\xF3 com output_format png/webp. Padr\xE3o opaque."),
|
|
576
610
|
moderation: z.enum(["low", "auto"]).optional().describe("Modera\xE7\xE3o (gpt-image-2). Padr\xE3o low."),
|
|
@@ -599,6 +633,33 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
599
633
|
try {
|
|
600
634
|
const isGoogle = args.model.startsWith("google/");
|
|
601
635
|
const isMidjourney = args.model === "midjourney";
|
|
636
|
+
const isSeedreamLite = args.model === "bytedance/seedream-5.0-lite";
|
|
637
|
+
const isSeedreamPro = args.model === "bytedance/seedream-5.0-pro";
|
|
638
|
+
const isSeedream = isSeedreamLite || isSeedreamPro;
|
|
639
|
+
if (isSeedream) {
|
|
640
|
+
const allowed = isSeedreamPro ? SEEDREAM_PRO_SIZES : SEEDREAM_LITE_SIZES;
|
|
641
|
+
if (args.size && !allowed.includes(args.size)) {
|
|
642
|
+
return errorResult(
|
|
643
|
+
`${args.model} aceita size ${allowed.join("/")} (voc\xEA pediu ${args.size}). ` + (isSeedreamPro ? "O Pro n\xE3o faz 3K/4K \u2014 use o seedream-5.0-lite para resolu\xE7\xF5es maiores." : "O Lite n\xE3o faz 1K \u2014 use o seedream-5.0-pro se quiser a resolu\xE7\xE3o menor.")
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
if (args.batch_images && isSeedreamPro) {
|
|
647
|
+
return errorResult(
|
|
648
|
+
"O seedream-5.0-pro n\xE3o suporta gera\xE7\xE3o em s\xE9rie (batch_images). Use bytedance/seedream-5.0-lite para gerar v\xE1rias imagens relacionadas de uma vez."
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
if (args.output_format === "webp") {
|
|
652
|
+
return errorResult(`${args.model} aceita apenas png ou jpeg (webp n\xE3o \xE9 suportado).`);
|
|
653
|
+
}
|
|
654
|
+
const maxRefs = isSeedreamPro ? 10 : 14;
|
|
655
|
+
if (args.reference_images && args.reference_images.length > maxRefs) {
|
|
656
|
+
return errorResult(
|
|
657
|
+
`${args.model} aceita no m\xE1ximo ${maxRefs} imagens de refer\xEAncia (voc\xEA passou ${args.reference_images.length}).`
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
} else if (args.batch_images) {
|
|
661
|
+
return errorResult("batch_images s\xF3 existe no bytedance/seedream-5.0-lite.");
|
|
662
|
+
}
|
|
602
663
|
const reference_images = await resolveRefs(cfg, args.reference_images);
|
|
603
664
|
let style_reference;
|
|
604
665
|
if (isMidjourney && args.style_reference) {
|
|
@@ -612,7 +673,12 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
612
673
|
reference_images,
|
|
613
674
|
google_search: args.google_search,
|
|
614
675
|
image_search: args.image_search,
|
|
615
|
-
size: args.size,
|
|
676
|
+
size: args.size ?? (isSeedream ? "2K" : void 0),
|
|
677
|
+
// Série do Seedream Lite: o backend lê o par sequential_image_generation
|
|
678
|
+
// + max_images (nomes da API da BytePlus). `batch_images` é só o nome
|
|
679
|
+
// amigável exposto aqui.
|
|
680
|
+
sequential_image_generation: isSeedreamLite && args.batch_images ? "auto" : void 0,
|
|
681
|
+
max_images: isSeedreamLite && args.batch_images ? args.batch_images : void 0,
|
|
616
682
|
quality: isMidjourney ? args.mj_quality : args.quality,
|
|
617
683
|
background: args.background,
|
|
618
684
|
moderation: args.moderation,
|
|
@@ -664,6 +730,10 @@ ${footer}` }, ...blocks]
|
|
|
664
730
|
} catch (e) {
|
|
665
731
|
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
666
732
|
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
733
|
+
if (isBudgetBlockedError(msg)) {
|
|
734
|
+
return errorResult(`${apiErrorToUserMessage(e)}
|
|
735
|
+
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
736
|
+
}
|
|
667
737
|
return errorResult(apiErrorToUserMessage(e));
|
|
668
738
|
}
|
|
669
739
|
}
|
|
@@ -721,6 +791,10 @@ ${footer}` }, ...blocks] };
|
|
|
721
791
|
} catch (e) {
|
|
722
792
|
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
723
793
|
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
794
|
+
if (isBudgetBlockedError(msg)) {
|
|
795
|
+
return errorResult(`${apiErrorToUserMessage(e)}
|
|
796
|
+
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
797
|
+
}
|
|
724
798
|
return errorResult(apiErrorToUserMessage(e));
|
|
725
799
|
}
|
|
726
800
|
}
|
|
@@ -731,7 +805,9 @@ ${footer}` }, ...blocks] };
|
|
|
731
805
|
title: "Gerar v\xEDdeo",
|
|
732
806
|
description: "Gera um v\xEDdeo curto usando o IA Lab da BePlus. LENTO (geralmente 1-5+ min). Aguarda at\xE9 ~2 min; se n\xE3o terminar, retorna o generation_id e o status \u2014 ent\xE3o use check_generation. Consome diamantes (mais caro para dura\xE7\xF5es/resolu\xE7\xF5es maiores). Suporta image-to-video (`first_frame_image`), \xFAltimo frame (`last_frame_image`), refer\xEAncias de imagem/v\xEDdeo/\xE1udio, prompt negativo, \xE1udio gerado e mais \u2014 os campos aceitos variam por modelo (consulte list_models). `kling-v2.6-motion-control` usa `first_frame_image` + `motion_video`. Imagens com rosto de pessoa REAL s\xE3o bloqueadas pela modera\xE7\xE3o do provedor (use ilustra\xE7\xF5es/personagens)." + TEAM_GATING_NOTE,
|
|
733
807
|
inputSchema: {
|
|
734
|
-
|
|
808
|
+
// Sem teto local — ver nota em generate_image. O Seedance não publica
|
|
809
|
+
// limite; o Kling publica 2500 e o backend recusa dizendo quanto cortar.
|
|
810
|
+
prompt: z.string().min(1).describe("Descri\xE7\xE3o do v\xEDdeo desejado."),
|
|
735
811
|
model: z.enum(VIDEO_MODELS).default(DEFAULTS.videoModel).describe("seedance-2.0-fast = barato/r\xE1pido; seedance-2.0 = melhor; kling-v3-video = alternativa."),
|
|
736
812
|
aspect_ratio: z.enum(ASPECT_RATIOS).optional().describe("Propor\xE7\xE3o. Padr\xE3o 16:9."),
|
|
737
813
|
resolution: z.enum(VIDEO_RESOLUTIONS).optional().describe("480p/720p/1080p/4K (1080p/4K S\xD3 no seedance-2.0 standard; fast e mini aceitam apenas 480p/720p). Padr\xE3o 720p."),
|
|
@@ -813,6 +889,10 @@ ${footer}`);
|
|
|
813
889
|
} catch (e) {
|
|
814
890
|
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
815
891
|
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
892
|
+
if (isBudgetBlockedError(msg)) {
|
|
893
|
+
return errorResult(`${apiErrorToUserMessage(e)}
|
|
894
|
+
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
895
|
+
}
|
|
816
896
|
return errorResult(apiErrorToUserMessage(e));
|
|
817
897
|
}
|
|
818
898
|
}
|
|
@@ -920,14 +1000,29 @@ ${list}${flagsLine(status)}` }, ...blocks] };
|
|
|
920
1000
|
const model = g?.model ?? p?.model ?? "(modelo desconhecido)";
|
|
921
1001
|
const prompt = g?.prompt ?? p?.prompt ?? "(sem prompt)";
|
|
922
1002
|
const configs = g?.input_params ?? p?.configs ?? {};
|
|
923
|
-
const
|
|
1003
|
+
const meta = g?.metadata ?? {};
|
|
1004
|
+
const asUrls = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && !!x) : [];
|
|
1005
|
+
const asUrl = (v) => typeof v === "string" && v ? v : void 0;
|
|
1006
|
+
const pick = (fromDb, fromTag) => {
|
|
1007
|
+
const db = asUrls(fromDb);
|
|
1008
|
+
return db.length > 0 ? db : asUrls(fromTag);
|
|
1009
|
+
};
|
|
1010
|
+
const images = pick(meta.input_image_urls, p?.input_image_urls);
|
|
1011
|
+
const refVideos = pick(meta.reference_video_urls, p?.reference_video_urls);
|
|
1012
|
+
const refAudios = pick(meta.reference_audio_urls, p?.reference_audio_urls);
|
|
1013
|
+
const motionVideo = asUrl(meta.motion_video_url) ?? asUrl(p?.motion_video_url);
|
|
1014
|
+
const endImage = asUrl(meta.end_image_url) ?? asUrl(p?.end_image_url);
|
|
924
1015
|
const lines = [
|
|
925
1016
|
`\u{1F9EC} Dados da gera\xE7\xE3o (${r.source === "db" ? "registro da conta" : "metadata do arquivo"}):`,
|
|
926
1017
|
`\u2022 Modelo: ${model}`,
|
|
927
1018
|
`\u2022 Prompt: ${prompt}`,
|
|
928
1019
|
`\u2022 Configs: ${JSON.stringify(configs)}`
|
|
929
1020
|
];
|
|
930
|
-
if (
|
|
1021
|
+
if (images.length) lines.push(`\u2022 Imagens de entrada: ${images.join(", ")}`);
|
|
1022
|
+
if (endImage) lines.push(`\u2022 Frame final: ${endImage}`);
|
|
1023
|
+
if (refVideos.length) lines.push(`\u2022 V\xEDdeos de refer\xEAncia: ${refVideos.join(", ")}`);
|
|
1024
|
+
if (refAudios.length) lines.push(`\u2022 \xC1udios de refer\xEAncia: ${refAudios.join(", ")}`);
|
|
1025
|
+
if (motionVideo) lines.push(`\u2022 V\xEDdeo de motion control: ${motionVideo}`);
|
|
931
1026
|
if (p?.generation_id) lines.push(`\u2022 generation_id: ${p.generation_id}`);
|
|
932
1027
|
return textResult(lines.join("\n"));
|
|
933
1028
|
} catch (e) {
|
|
@@ -990,15 +1085,21 @@ function registerAudioTools(server, client, cfg) {
|
|
|
990
1085
|
const projLine = res.project_code ? `
|
|
991
1086
|
\u{1F4C1} Projeto: ${res.project_code}${res.project_name ? ` \u2014 ${res.project_name}` : ""}` : "";
|
|
992
1087
|
const wa = weeklyAllowanceLine(res.weekly_allowance);
|
|
1088
|
+
const budget = budgetLine(res.budget);
|
|
993
1089
|
return textResult(
|
|
994
1090
|
`\u2705 \xC1udio gerado (${res.model}, voz ${res.voice}, ${res.duration_sec.toFixed(1)}s).
|
|
995
1091
|
${res.url}
|
|
996
|
-
${parts.join(" \xB7 ")}${projLine}` + (
|
|
1092
|
+
${parts.join(" \xB7 ")}${projLine}` + (budget ? `
|
|
1093
|
+
${budget}` : "") + (wa ? `
|
|
997
1094
|
${wa}` : "")
|
|
998
1095
|
);
|
|
999
1096
|
} catch (e) {
|
|
1000
1097
|
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
1001
1098
|
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
1099
|
+
if (isBudgetBlockedError(msg)) {
|
|
1100
|
+
return errorResult(`${apiErrorToUserMessage(e)}
|
|
1101
|
+
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
1102
|
+
}
|
|
1002
1103
|
return errorResult(apiErrorToUserMessage(e));
|
|
1003
1104
|
}
|
|
1004
1105
|
}
|
|
@@ -1082,6 +1183,10 @@ ${footer}`);
|
|
|
1082
1183
|
} catch (e) {
|
|
1083
1184
|
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
1084
1185
|
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
1186
|
+
if (isBudgetBlockedError(msg)) {
|
|
1187
|
+
return errorResult(`${apiErrorToUserMessage(e)}
|
|
1188
|
+
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
1189
|
+
}
|
|
1085
1190
|
return errorResult(apiErrorToUserMessage(e));
|
|
1086
1191
|
}
|
|
1087
1192
|
}
|
|
@@ -1102,10 +1207,12 @@ function registerAccountTools(server, client, _cfg) {
|
|
|
1102
1207
|
try {
|
|
1103
1208
|
const me = await client.me();
|
|
1104
1209
|
const wa = weeklyAllowanceLine(me.weekly_allowance);
|
|
1210
|
+
const budget = budgetLine(me.budget);
|
|
1105
1211
|
return textResult(
|
|
1106
1212
|
`\u{1F464} ${me.name ?? "(sem nome)"} <${me.email ?? "sem email"}>
|
|
1107
1213
|
Saldo: ${me.diamonds} \u{1F48E} \xB7 auth: ${me.authType}` + (wa ? `
|
|
1108
|
-
${wa}` : "")
|
|
1214
|
+
${wa}` : "") + (budget ? `
|
|
1215
|
+
${budget}` : "")
|
|
1109
1216
|
);
|
|
1110
1217
|
} catch (e) {
|
|
1111
1218
|
return errorResult(apiErrorToUserMessage(e));
|
|
@@ -1123,8 +1230,12 @@ ${wa}` : "")
|
|
|
1123
1230
|
try {
|
|
1124
1231
|
const b = await client.balance();
|
|
1125
1232
|
const wa = weeklyAllowanceLine(b.weekly_allowance);
|
|
1126
|
-
|
|
1127
|
-
|
|
1233
|
+
const budget = budgetLine(b.budget);
|
|
1234
|
+
return textResult(
|
|
1235
|
+
`Saldo: ${b.diamonds} \u{1F48E}` + (wa ? `
|
|
1236
|
+
${wa}` : "") + (budget ? `
|
|
1237
|
+
${budget}` : "")
|
|
1238
|
+
);
|
|
1128
1239
|
} catch (e) {
|
|
1129
1240
|
return errorResult(apiErrorToUserMessage(e));
|
|
1130
1241
|
}
|
|
@@ -1242,7 +1353,8 @@ function projectLine(p, active) {
|
|
|
1242
1353
|
const marker = active ? "\u25CF " : "\u25CB ";
|
|
1243
1354
|
const counts = `${p.generation_count ?? 0} gera\xE7\xF5es`;
|
|
1244
1355
|
const owner = p.client_name ? ` \xB7 cliente: ${p.client_name}` : "";
|
|
1245
|
-
|
|
1356
|
+
const budget = p.budget?.enabled ? ` \xB7 budget ${p.budget.remainingDiamonds}/${p.budget.quotaDiamonds}\u{1F48E}` : "";
|
|
1357
|
+
return `${marker}${p.code} \u2014 ${p.name} (${counts}${owner})${budget}`;
|
|
1246
1358
|
}
|
|
1247
1359
|
function registerProjectTools(server, client, _cfg) {
|
|
1248
1360
|
server.registerTool(
|
|
@@ -1262,7 +1374,10 @@ function registerProjectTools(server, client, _cfg) {
|
|
|
1262
1374
|
}
|
|
1263
1375
|
const lines = projects.map((p) => projectLine(p, !!p.is_active_for_user));
|
|
1264
1376
|
const active = projects.find((p) => p.is_active_for_user);
|
|
1265
|
-
|
|
1377
|
+
let header = active ? `Projeto ativo: ${active.code} \u2014 ${active.name}` : "Nenhum projeto ativo (gera\xE7\xF5es ficam sem projeto).";
|
|
1378
|
+
const activeBudget = budgetLine(active?.budget);
|
|
1379
|
+
if (activeBudget) header += `
|
|
1380
|
+
${activeBudget}`;
|
|
1266
1381
|
return textResult(`${header}
|
|
1267
1382
|
|
|
1268
1383
|
${lines.join("\n")}`);
|
|
@@ -1313,7 +1428,9 @@ ${lines.join("\n")}`);
|
|
|
1313
1428
|
try {
|
|
1314
1429
|
const { project: active } = await client.setActiveProject(project ?? null);
|
|
1315
1430
|
if (!active) return textResult("\u2705 Projeto ativo removido \u2014 novas gera\xE7\xF5es ficar\xE3o sem projeto.");
|
|
1316
|
-
|
|
1431
|
+
const budget = budgetLine(active.budget);
|
|
1432
|
+
return textResult(`\u2705 Projeto ativo: ${active.code} \u2014 ${active.name}.` + (budget ? `
|
|
1433
|
+
${budget}` : ""));
|
|
1317
1434
|
} catch (e) {
|
|
1318
1435
|
return errorResult(apiErrorToUserMessage(e));
|
|
1319
1436
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beplus-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Conector MCP da equipe BePlus — gere imagens/vídeos/áudio e consulte calls, projetos e clientes pela sua conta BePlus (diamantes, limites e histórico aplicados pela plataforma).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|