beplus-mcp 0.12.0 → 0.14.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.
Files changed (2) hide show
  1. package/dist/index.js +108 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -189,7 +189,18 @@ var BeplusClient = class {
189
189
  return this.request("POST", "/analyze-media", { body, timeoutMs: 3e5 });
190
190
  }
191
191
  searchCalls(query) {
192
- return this.request("GET", "/calls", { query });
192
+ return this.request("GET", "/calls", {
193
+ query: {
194
+ query: query.query,
195
+ project: query.project,
196
+ client: query.client,
197
+ company: query.company,
198
+ visibility: query.visibility,
199
+ speaker: query.speaker,
200
+ mine: query.mine ? "true" : void 0,
201
+ limit: query.limit
202
+ }
203
+ });
193
204
  }
194
205
  getCall(id) {
195
206
  return this.request("GET", `/calls/${id}`);
@@ -204,6 +215,10 @@ var TEAM_GATING_NOTE = " \u26A0\uFE0F DESTINO DA GERA\xC7\xC3O: toda imagem/v\xE
204
215
  function isTeamGatingError(msg) {
205
216
  return !!msg && /cliente e um projeto|selecione um cliente|sem cliente vinculado|antes de gerar/i.test(msg);
206
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`.";
207
222
  async function buildPickProjectMessage(client) {
208
223
  let clientsTxt = " (n\xE3o foi poss\xEDvel listar clientes)";
209
224
  let projectsTxt = " (n\xE3o foi poss\xEDvel listar projetos)";
@@ -371,6 +386,35 @@ function logError(...args) {
371
386
  }
372
387
 
373
388
  // src/format.ts
389
+ function fmtResetDate(iso) {
390
+ const [, m, d] = iso.split("-");
391
+ return d && m ? `${d}/${m}` : iso;
392
+ }
393
+ function weeklyAllowanceLine(wa) {
394
+ if (!wa || !wa.enabled) return "";
395
+ if (wa.unlimited) return "\u267E\uFE0F Limite semanal: ilimitado (sua conta \xE9 isenta).";
396
+ const pct = wa.percentUsed;
397
+ const icon = pct >= 100 ? "\u{1F6D1}" : pct >= wa.warnThresholdPercent ? "\u26A0\uFE0F" : "\u{1F4CA}";
398
+ let line = `${icon} Limite semanal: ${pct}% usado \xB7 ${wa.remaining} de ${wa.quota} restantes`;
399
+ if (pct >= 100) {
400
+ line += `
401
+ \u{1F6D1} Limite semanal atingido \u2014 reseta em ${fmtResetDate(wa.periodEnd)}.`;
402
+ } else if (pct >= wa.warnThresholdPercent) {
403
+ line += `
404
+ \u26A0\uFE0F Seu limite semanal est\xE1 acabando \u2014 reseta em ${fmtResetDate(wa.periodEnd)}.`;
405
+ }
406
+ return line;
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
+ }
374
418
  function textResult(s) {
375
419
  return { content: [{ type: "text", text: s }] };
376
420
  }
@@ -393,11 +437,20 @@ function costFooter(start, cfg) {
393
437
  footer = `\u26A0 Custo alto (${start.diamond_cost} \u{1F48E})
394
438
  ${footer}`;
395
439
  }
440
+ const budget = budgetLine(start.budget);
396
441
  if (start.project_code) {
397
- const proj = `\u{1F4C1} Projeto: ${start.project_code}${start.project_name ? ` \u2014 ${start.project_name}` : ""}`;
442
+ let proj = `\u{1F4C1} Projeto: ${start.project_code}${start.project_name ? ` \u2014 ${start.project_name}` : ""}`;
443
+ if (budget) proj = `${proj}
444
+ ${budget}`;
398
445
  footer = footer ? `${proj}
399
446
  ${footer}` : proj;
447
+ } else if (budget) {
448
+ footer = footer ? `${budget}
449
+ ${footer}` : budget;
400
450
  }
451
+ const wa = weeklyAllowanceLine(start.weekly_allowance);
452
+ if (wa) footer = footer ? `${footer}
453
+ ${wa}` : wa;
401
454
  return footer;
402
455
  }
403
456
  function outputUrls(status) {
@@ -631,6 +684,10 @@ ${footer}` }, ...blocks]
631
684
  } catch (e) {
632
685
  const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
633
686
  if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
687
+ if (isBudgetBlockedError(msg)) {
688
+ return errorResult(`${apiErrorToUserMessage(e)}
689
+ ${BUDGET_BLOCKED_GUIDANCE}`);
690
+ }
634
691
  return errorResult(apiErrorToUserMessage(e));
635
692
  }
636
693
  }
@@ -688,6 +745,10 @@ ${footer}` }, ...blocks] };
688
745
  } catch (e) {
689
746
  const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
690
747
  if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
748
+ if (isBudgetBlockedError(msg)) {
749
+ return errorResult(`${apiErrorToUserMessage(e)}
750
+ ${BUDGET_BLOCKED_GUIDANCE}`);
751
+ }
691
752
  return errorResult(apiErrorToUserMessage(e));
692
753
  }
693
754
  }
@@ -780,6 +841,10 @@ ${footer}`);
780
841
  } catch (e) {
781
842
  const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
782
843
  if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
844
+ if (isBudgetBlockedError(msg)) {
845
+ return errorResult(`${apiErrorToUserMessage(e)}
846
+ ${BUDGET_BLOCKED_GUIDANCE}`);
847
+ }
783
848
  return errorResult(apiErrorToUserMessage(e));
784
849
  }
785
850
  }
@@ -956,14 +1021,22 @@ function registerAudioTools(server, client, cfg) {
956
1021
  if (res.diamonds_refunded) parts.push(`Estorno: ${res.diamonds_refunded} \u{1F48E}`);
957
1022
  const projLine = res.project_code ? `
958
1023
  \u{1F4C1} Projeto: ${res.project_code}${res.project_name ? ` \u2014 ${res.project_name}` : ""}` : "";
1024
+ const wa = weeklyAllowanceLine(res.weekly_allowance);
1025
+ const budget = budgetLine(res.budget);
959
1026
  return textResult(
960
1027
  `\u2705 \xC1udio gerado (${res.model}, voz ${res.voice}, ${res.duration_sec.toFixed(1)}s).
961
1028
  ${res.url}
962
- ${parts.join(" \xB7 ")}${projLine}`
1029
+ ${parts.join(" \xB7 ")}${projLine}` + (budget ? `
1030
+ ${budget}` : "") + (wa ? `
1031
+ ${wa}` : "")
963
1032
  );
964
1033
  } catch (e) {
965
1034
  const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
966
1035
  if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
1036
+ if (isBudgetBlockedError(msg)) {
1037
+ return errorResult(`${apiErrorToUserMessage(e)}
1038
+ ${BUDGET_BLOCKED_GUIDANCE}`);
1039
+ }
967
1040
  return errorResult(apiErrorToUserMessage(e));
968
1041
  }
969
1042
  }
@@ -1047,6 +1120,10 @@ ${footer}`);
1047
1120
  } catch (e) {
1048
1121
  const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
1049
1122
  if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
1123
+ if (isBudgetBlockedError(msg)) {
1124
+ return errorResult(`${apiErrorToUserMessage(e)}
1125
+ ${BUDGET_BLOCKED_GUIDANCE}`);
1126
+ }
1050
1127
  return errorResult(apiErrorToUserMessage(e));
1051
1128
  }
1052
1129
  }
@@ -1066,9 +1143,13 @@ function registerAccountTools(server, client, _cfg) {
1066
1143
  async () => {
1067
1144
  try {
1068
1145
  const me = await client.me();
1146
+ const wa = weeklyAllowanceLine(me.weekly_allowance);
1147
+ const budget = budgetLine(me.budget);
1069
1148
  return textResult(
1070
1149
  `\u{1F464} ${me.name ?? "(sem nome)"} <${me.email ?? "sem email"}>
1071
- Saldo: ${me.diamonds} \u{1F48E} \xB7 auth: ${me.authType}`
1150
+ Saldo: ${me.diamonds} \u{1F48E} \xB7 auth: ${me.authType}` + (wa ? `
1151
+ ${wa}` : "") + (budget ? `
1152
+ ${budget}` : "")
1072
1153
  );
1073
1154
  } catch (e) {
1074
1155
  return errorResult(apiErrorToUserMessage(e));
@@ -1079,13 +1160,19 @@ Saldo: ${me.diamonds} \u{1F48E} \xB7 auth: ${me.authType}`
1079
1160
  "get_balance",
1080
1161
  {
1081
1162
  title: "Saldo de diamantes",
1082
- description: "Mostra o saldo atual de diamantes da conta vinculada.",
1163
+ description: "Mostra o saldo atual de diamantes e, para membros da equipe, o cr\xE9dito semanal (% usado, quanto resta e quando reseta).",
1083
1164
  inputSchema: {}
1084
1165
  },
1085
1166
  async () => {
1086
1167
  try {
1087
1168
  const b = await client.balance();
1088
- return textResult(`Saldo: ${b.diamonds} \u{1F48E}`);
1169
+ const wa = weeklyAllowanceLine(b.weekly_allowance);
1170
+ const budget = budgetLine(b.budget);
1171
+ return textResult(
1172
+ `Saldo: ${b.diamonds} \u{1F48E}` + (wa ? `
1173
+ ${wa}` : "") + (budget ? `
1174
+ ${budget}` : "")
1175
+ );
1089
1176
  } catch (e) {
1090
1177
  return errorResult(apiErrorToUserMessage(e));
1091
1178
  }
@@ -1203,7 +1290,8 @@ function projectLine(p, active) {
1203
1290
  const marker = active ? "\u25CF " : "\u25CB ";
1204
1291
  const counts = `${p.generation_count ?? 0} gera\xE7\xF5es`;
1205
1292
  const owner = p.client_name ? ` \xB7 cliente: ${p.client_name}` : "";
1206
- return `${marker}${p.code} \u2014 ${p.name} (${counts}${owner})`;
1293
+ const budget = p.budget?.enabled ? ` \xB7 budget ${p.budget.remainingDiamonds}/${p.budget.quotaDiamonds}\u{1F48E}` : "";
1294
+ return `${marker}${p.code} \u2014 ${p.name} (${counts}${owner})${budget}`;
1207
1295
  }
1208
1296
  function registerProjectTools(server, client, _cfg) {
1209
1297
  server.registerTool(
@@ -1223,7 +1311,10 @@ function registerProjectTools(server, client, _cfg) {
1223
1311
  }
1224
1312
  const lines = projects.map((p) => projectLine(p, !!p.is_active_for_user));
1225
1313
  const active = projects.find((p) => p.is_active_for_user);
1226
- const header = active ? `Projeto ativo: ${active.code} \u2014 ${active.name}` : "Nenhum projeto ativo (gera\xE7\xF5es ficam sem projeto).";
1314
+ let header = active ? `Projeto ativo: ${active.code} \u2014 ${active.name}` : "Nenhum projeto ativo (gera\xE7\xF5es ficam sem projeto).";
1315
+ const activeBudget = budgetLine(active?.budget);
1316
+ if (activeBudget) header += `
1317
+ ${activeBudget}`;
1227
1318
  return textResult(`${header}
1228
1319
 
1229
1320
  ${lines.join("\n")}`);
@@ -1274,7 +1365,9 @@ ${lines.join("\n")}`);
1274
1365
  try {
1275
1366
  const { project: active } = await client.setActiveProject(project ?? null);
1276
1367
  if (!active) return textResult("\u2705 Projeto ativo removido \u2014 novas gera\xE7\xF5es ficar\xE3o sem projeto.");
1277
- return textResult(`\u2705 Projeto ativo: ${active.code} \u2014 ${active.name}.`);
1368
+ const budget = budgetLine(active.budget);
1369
+ return textResult(`\u2705 Projeto ativo: ${active.code} \u2014 ${active.name}.` + (budget ? `
1370
+ ${budget}` : ""));
1278
1371
  } catch (e) {
1279
1372
  return errorResult(apiErrorToUserMessage(e));
1280
1373
  }
@@ -1373,12 +1466,13 @@ function fmtDate(iso) {
1373
1466
  return iso ? iso.slice(0, 10) : "";
1374
1467
  }
1375
1468
  function callLine(c) {
1469
+ const company = c.company_name ? ` \xB7 ${c.company_name}` : "";
1376
1470
  const proj = c.project_name ? ` \xB7 ${c.client_name ? c.client_name + " / " : ""}${c.project_name}` : "";
1377
1471
  const who = c.speakers.length ? ` \xB7 ${c.speakers.join(", ")}` : c.participant_count ? ` \xB7 ${c.participant_count} falantes` : "";
1378
1472
  const vis = VIS_LABEL[c.visibility] ?? c.visibility;
1379
1473
  const flag = c.status !== "ready" ? ` [${c.status}]` : "";
1380
1474
  return `\u2022 ${c.title ?? "(sem t\xEDtulo)"}${flag}
1381
- ${fmtDate(c.date)} \xB7 ${fmtDuration(c.duration_sec)} \xB7 ${vis}${proj}${who}
1475
+ ${fmtDate(c.date)} \xB7 ${fmtDuration(c.duration_sec)} \xB7 ${vis}${company}${proj}${who}
1382
1476
  id: ${c.id}`;
1383
1477
  }
1384
1478
  function renderCall(c) {
@@ -1388,6 +1482,7 @@ function renderCall(c) {
1388
1482
  if (c.date) meta.push(fmtDate(c.date));
1389
1483
  meta.push(fmtDuration(c.duration_sec));
1390
1484
  meta.push(VIS_LABEL[c.visibility] ?? c.visibility);
1485
+ if (c.company_name) meta.push(c.company_name);
1391
1486
  if (c.project_name) meta.push(`${c.client_name ? c.client_name + " / " : ""}${c.project_name}`);
1392
1487
  if (c.status !== "ready") meta.push(`status: ${c.status}`);
1393
1488
  lines.push(meta.join(" \xB7 "));
@@ -1418,13 +1513,15 @@ function registerCallTools(server, client, _cfg) {
1418
1513
  "search_calls",
1419
1514
  {
1420
1515
  title: "Buscar calls",
1421
- description: "Busca nas calls (reuni\xF5es gravadas e transcritas) vis\xEDveis pra conta \u2014 suas, da equipe e compartilhadas. Todos os filtros s\xE3o opcionais e combin\xE1veis: `query` (texto no t\xEDtulo/projeto/cliente/falante), `project` (code/uuid/nome), `client` (code/nome), `visibility` (team|private|workspace|link), `speaker` (nome do falante). Retorna uma lista resumida \u2014 use get_call com o id pra ler a transcri\xE7\xE3o completa.",
1516
+ description: 'Busca nas calls (reuni\xF5es gravadas e transcritas) vis\xEDveis pra conta \u2014 suas, da equipe e compartilhadas. Todos os filtros s\xE3o opcionais e combin\xE1veis: `query` (texto no t\xEDtulo/projeto/cliente/falante), `project` (code/uuid/nome), `client` (code/nome), `company` (empresa/equipe do dono, ex.: "BePlus"), `visibility` (team|private|workspace|link), `speaker` (nome do falante), `mine` (s\xF3 as que EU gravei). Retorna uma lista resumida \u2014 use get_call com o id pra ler a transcri\xE7\xE3o completa.',
1422
1517
  inputSchema: {
1423
1518
  query: z8.string().max(200).optional().describe("Texto livre \u2014 casa t\xEDtulo, projeto, cliente ou falante."),
1424
1519
  project: z8.string().max(64).optional().describe('Projeto: code (ex.: "CONTEUDO-INS"), uuid ou parte do nome.'),
1425
1520
  client: z8.string().max(128).optional().describe("Cliente: code ou parte do nome."),
1521
+ company: z8.string().max(128).optional().describe('Empresa/equipe do dono da call (ex.: "BePlus"). Filtra por parte do nome.'),
1426
1522
  visibility: z8.enum(["team", "private", "workspace", "link"]).optional().describe("Privacidade: team (equipe), private (privada), workspace (projeto) ou link."),
1427
1523
  speaker: z8.string().max(128).optional().describe("Nome do falante."),
1524
+ mine: z8.boolean().optional().describe('true = s\xF3 as calls que EU gravei (sou o dono). Use pra "minhas grava\xE7\xF5es".'),
1428
1525
  limit: z8.number().int().min(1).max(50).optional().describe("M\xE1ximo de resultados (padr\xE3o 20).")
1429
1526
  }
1430
1527
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beplus-mcp",
3
- "version": "0.12.0",
3
+ "version": "0.14.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": {