beplus-mcp 0.40.0 → 0.41.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.
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) {
@@ -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));
@@ -21875,8 +21939,9 @@ async function loadRemote(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) {
@@ -23218,6 +23283,7 @@ function isRemote(ref) {
23218
23283
  return /^https?:\/\//i.test(ref);
23219
23284
  }
23220
23285
  function toLocalPath2(ref) {
23286
+ refuseLocalPath(ref);
23221
23287
  if (ref.startsWith("file://")) return fileURLToPath2(ref);
23222
23288
  if (ref === "~") return homedir3();
23223
23289
  if (ref.startsWith("~/")) return resolve2(homedir3(), ref.slice(2));
@@ -23243,7 +23309,7 @@ async function readAt(ref, offset, len) {
23243
23309
  const ctrl = new AbortController();
23244
23310
  const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
23245
23311
  try {
23246
- const res = await fetch(ref, {
23312
+ const res = await safeFetch(ref, {
23247
23313
  headers: { Range: `bytes=${offset}-${offset + len - 1}` },
23248
23314
  signal: ctrl.signal
23249
23315
  });
@@ -23286,7 +23352,7 @@ async function readHead(ref, bytes) {
23286
23352
  const ctrl = new AbortController();
23287
23353
  const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
23288
23354
  try {
23289
- const res = await fetch(ref, {
23355
+ const res = await safeFetch(ref, {
23290
23356
  headers: { Range: `bytes=0-${bytes - 1}` },
23291
23357
  signal: ctrl.signal
23292
23358
  });
@@ -23430,7 +23496,7 @@ function probeMp4(b) {
23430
23496
  }
23431
23497
  async function fileSizeOf(ref) {
23432
23498
  if (!isRemote(ref)) return (await stat3(toLocalPath2(ref))).size;
23433
- const res = await fetch(ref, { method: "HEAD" });
23499
+ const res = await safeFetch(ref, { method: "HEAD" });
23434
23500
  return Number(res.headers.get("content-length") ?? 0);
23435
23501
  }
23436
23502
  var ProbeError = class extends Error {
@@ -26203,10 +26269,10 @@ function withExtension(name, file) {
26203
26269
  return ext && extname3(clean).toLowerCase() !== ext.toLowerCase() ? `${clean}${ext}` : clean;
26204
26270
  }
26205
26271
  async function uploadMedia(cfg, source, name) {
26206
- const remote = isRemoteRef(source);
26207
- const loaded = remote ? await loadRemote(source) : await loadLocal(source);
26272
+ const remote2 = isRemoteRef(source);
26273
+ const loaded = remote2 ? await loadRemote(source) : await loadLocal(source);
26208
26274
  const file = { ...loaded, name: withExtension(name, loaded) };
26209
- const reused = remote && new URL(source).host === BEPLUS_STORAGE_HOST;
26275
+ const reused = remote2 && new URL(source).host === BEPLUS_STORAGE_HOST;
26210
26276
  const url = reused ? source : await uploadBytes(cfg, file);
26211
26277
  const kind = kindOfMime(file.mime);
26212
26278
  let meta = null;
@@ -27757,7 +27823,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
27757
27823
  import { createRequire } from "module";
27758
27824
  var require2 = createRequire(import.meta.url);
27759
27825
  function readVersion() {
27760
- if ("0.40.0") return "0.40.0";
27826
+ if ("0.41.0") return "0.41.0";
27761
27827
  try {
27762
27828
  const pkg = require2("../package.json");
27763
27829
  return pkg.version ?? "0.0.0-unknown";