jefrichat-mcp 0.49.3 → 0.49.4

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 (3) hide show
  1. package/dist/http.js +64 -19
  2. package/dist/index.js +64 -19
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -62438,6 +62438,37 @@ var psPathArg = (p) => p.startsWith("~/") || p.startsWith("~\\") ? `($HOME + ${p
62438
62438
  var isTextyFile = (mime, name) => /^text\/|json|xml|javascript|typescript|markdown|csv|ya?ml|x-sh|toml/i.test(mime) || /\.(md|txt|json|csv|ya?ml|log|ts|tsx|js|jsx|py|rb|go|rs|java|c|h|cpp|sh|sql|toml|env|cfg|ini)$/i.test(name);
62439
62439
  var DATAURL_INLINE_MAX = 256 * 1024;
62440
62440
  var dataUrlTooBig = (dataUrl) => dataUrl.length > DATAURL_INLINE_MAX * 1.4 ? `That dataUrl is ~${MB(dataUrl.length * 0.75)} MB decoded \u2014 far past the ${Math.round(DATAURL_INLINE_MAX / 1024)}KB inline limit. NEVER read a local file and base64 it through the model. Call this tool again with the file's PATH \u2014 you'll get a single-use upload command that sends it in seconds.` : null;
62441
+ var MINT_LINK_DEADLINE_MS = (() => {
62442
+ const n = Number(process.env.MCP_MINT_LINK_DEADLINE_MS);
62443
+ return Number.isInteger(n) && n > 0 ? n : 1e4;
62444
+ })();
62445
+ async function mintFileLink(hub, token, fileId) {
62446
+ const ac = new AbortController();
62447
+ const killer = setTimeout(() => ac.abort(), MINT_LINK_DEADLINE_MS);
62448
+ try {
62449
+ const lr = await fetch(`${hub}/api/files/${fileId}/link`, {
62450
+ method: "POST",
62451
+ headers: { authorization: `Bearer ${token}` },
62452
+ signal: ac.signal
62453
+ });
62454
+ if (!lr.ok) {
62455
+ try {
62456
+ await lr.body?.cancel();
62457
+ } catch {
62458
+ }
62459
+ return null;
62460
+ }
62461
+ const body = await lr.json();
62462
+ return body?.url ?? null;
62463
+ } catch {
62464
+ return null;
62465
+ } finally {
62466
+ ac.abort();
62467
+ clearTimeout(killer);
62468
+ }
62469
+ }
62470
+ var forwardTip = (link2, fileName) => `\u2022 To FORWARD this exact file: to a PERSON call jefri_send_file with to:"<username>", fileUrl:${JSON.stringify(link2)}, fileName:${JSON.stringify(fileName)}; to a GROUP call jefri_send_group_file with groupId:"<id>" and the same fileUrl + fileName. The connector fetches and delivers the ORIGINAL bytes (up to 25 MB).
62471
+ `;
62441
62472
  async function remoteUploadCommand(c, ctx, target, path3, caption, where) {
62442
62473
  const fname = crossBasename(path3);
62443
62474
  const fmime = mimeOf(fname);
@@ -62574,9 +62605,13 @@ function resolveLocalFile(rawPath) {
62574
62605
  return null;
62575
62606
  }
62576
62607
  var MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
62577
- var MAX_REMOTE_URL_BYTES = Number(process.env.MCP_MAX_REMOTE_URL_BYTES ?? 25 * 1024 * 1024);
62578
- var MAX_CONCURRENT_REMOTE_DOWNLOADS = Number(process.env.MCP_MAX_REMOTE_DL ?? 3);
62579
- var REMOTE_DOWNLOAD_DEADLINE_MS = Number(process.env.MCP_REMOTE_DL_DEADLINE_MS ?? 3e4);
62608
+ var posIntEnv = (v, dflt) => {
62609
+ const n = Number(v);
62610
+ return Number.isInteger(n) && n > 0 ? n : dflt;
62611
+ };
62612
+ var MAX_REMOTE_URL_BYTES = posIntEnv(process.env.MCP_MAX_REMOTE_URL_BYTES, 25 * 1024 * 1024);
62613
+ var MAX_CONCURRENT_REMOTE_DOWNLOADS = posIntEnv(process.env.MCP_MAX_REMOTE_DL, 3);
62614
+ var REMOTE_DOWNLOAD_DEADLINE_MS = posIntEnv(process.env.MCP_REMOTE_DL_DEADLINE_MS, 3e4);
62580
62615
  var activeRemoteDownloads = 0;
62581
62616
  var MB = (n) => (n / 1024 / 1024).toFixed(1);
62582
62617
  var WS_SAFE_FILE_BYTES = 45 * 1024 * 1024;
@@ -63481,7 +63516,9 @@ ${fp}`);
63481
63516
  return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
63482
63517
  }
63483
63518
  } else {
63484
- return fail("provide a local file `path`, a `dataUrl` + `fileName`, or a public `fileUrl`");
63519
+ return fail(
63520
+ "provide a local file `path`, a `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. If the file only exists INSIDE this chat (attached or generated here), the connector can't see it \u2014 ask the user to save it to their computer first, then call again with that path."
63521
+ );
63485
63522
  }
63486
63523
  const rawBytes = Buffer.from(url.split(",")[1] ?? "", "base64");
63487
63524
  if (rawBytes.length > MAX_UPLOAD_BYTES)
@@ -63671,7 +63708,7 @@ ${fp}`);
63671
63708
  "jefri_download_file",
63672
63709
  {
63673
63710
  title: "Read or save a file someone sent you",
63674
- description: "Get a file another agent/user sent you. For a DM give the sender's username `from`; for a GROUP file give `groupId` (from jefri_groups / jefri_inbox). Add `fileName` if there are several. TEXT files (code, markdown, JSON, CSV, logs \u2014 up to 256KB) come back with their FULL CONTENT inline, PDFs come back as EXTRACTED TEXT, and IMAGES (png/jpeg/gif/webp up to 4MB) come back as the ACTUAL PICTURE you can look at \u2014 so you can read, view and discuss all three directly, no terminal needed. Only video, very large images and other binaries return a download link. On the local connector, files are saved straight to disk. Use this when the user says 'read the file sara sent', 'download that file', 'what does the doc say', etc.",
63711
+ description: "Get a file another agent/user sent you. For a DM give the sender's username `from`; for a GROUP file give `groupId` (from jefri_groups / jefri_inbox). Add `fileName` if there are several. TEXT files (code, markdown, JSON, CSV, logs \u2014 up to 256KB) come back with their FULL CONTENT inline, PDFs come back as EXTRACTED TEXT plus a signed link to the ORIGINAL file (use the link with jefri_send_file's fileUrl to forward the real bytes \u2014 never reconstruct a file from text), and IMAGES (png/jpeg/gif/webp up to 4MB) come back as the ACTUAL PICTURE you can look at \u2014 so you can read, view and discuss all three directly, no terminal needed. Only video, very large images and other binaries return a download link. On the local connector, files are saved straight to disk. Use this when the user says 'read the file sara sent', 'download that file', 'what does the doc say', etc.",
63675
63712
  inputSchema: {
63676
63713
  from: external_exports.string().optional().describe("who sent you the file (their username) \u2014 for a DM"),
63677
63714
  groupId: external_exports.string().optional().describe("the group's id \u2014 for a file sent in a group"),
@@ -63762,12 +63799,24 @@ ${fp}`);
63762
63799
  const r = await fetch(`${hub}/api/files/${match.id}/text`, { headers: { authorization: `Bearer ${c.token}` }, signal: ac.signal });
63763
63800
  if (r.ok) {
63764
63801
  const body = await r.json();
63765
- if (body?.text)
63802
+ if (body?.text) {
63803
+ const pdfLink = await mintFileLink(hub, c.token, match.id);
63804
+ const webPdf = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
63766
63805
  return ok3(
63767
- `\u{1F4CE} ${fname2} from ${where} \u2014 extracted text (${body.chars ?? body.text.length} chars):
63806
+ `\u{1F4CE} ${fname2} from ${where} \u2014 THE ORIGINAL FILE:
63807
+ ` + (pdfLink ? `\u2022 Direct download (valid ~5 minutes): ${pdfLink}
63808
+ ` : "") + (pdfLink ? forwardTip(pdfLink, fname2) : "") + `\u2022 Web app (renders in the chat): ${webPdf}
63809
+ ` + // The closing line must only reference what EXISTS: with
63810
+ // no minted link, "via the link above" pointed at nothing.
63811
+ (pdfLink ? `Never reconstruct a file from extracted text \u2014 forward the original via the link above.
63812
+
63813
+ ` : `Never reconstruct a file from extracted text \u2014 the link service didn't answer just now; call jefri_download_file again for a fresh link, or use the web app above.
63814
+
63815
+ `) + `\u2014 Extracted text (${body.chars ?? body.text.length} chars):
63768
63816
 
63769
63817
  ` + body.text
63770
63818
  );
63819
+ }
63771
63820
  }
63772
63821
  } finally {
63773
63822
  clearTimeout(killer);
@@ -63828,17 +63877,11 @@ ${fp}`);
63828
63877
  const cmd = `curl -s --no-clobber --create-dirs -o ${shPathArg(`${DOWNLOAD_DIR_SH}/${out}`)} -H "Authorization: Bearer $JEFRI_TOKEN" ${shq(`${hub}/api/files/${match.id}`)}`;
63829
63878
  const web2 = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
63830
63879
  let directLink = "";
63831
- try {
63832
- const lr = await fetch(`${hub}/api/files/${match.id}/link`, {
63833
- method: "POST",
63834
- headers: { authorization: `Bearer ${c.token}` }
63835
- });
63836
- if (lr.ok) {
63837
- const body = await lr.json();
63838
- if (body?.url) directLink = `\u2022 Direct download (click it \u2014 valid ~5 minutes): ${body.url}
63839
- `;
63840
- }
63841
- } catch {
63880
+ {
63881
+ const linkUrl = await mintFileLink(hub, c.token, match.id);
63882
+ if (linkUrl)
63883
+ directLink = `\u2022 Direct download (click it \u2014 valid ~5 minutes): ${linkUrl}
63884
+ ` + forwardTip(linkUrl, fname2);
63842
63885
  }
63843
63886
  return ok3(
63844
63887
  `\u{1F4CE} ${match.fileName} (${fmime2}) \u2014 I can't show it inline${imageWhy ? `: ${imageWhy}` : " (not a viewable type)"}.
@@ -64250,7 +64293,9 @@ Or just upload ${fname} from the agent's page in the web app.`
64250
64293
  return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
64251
64294
  }
64252
64295
  } else {
64253
- return fail("provide a local file `path`, a `dataUrl` + `fileName`, or a public `fileUrl`");
64296
+ return fail(
64297
+ "provide a local file `path`, a `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. If the file only exists INSIDE this chat (attached or generated here), the connector can't see it \u2014 ask the user to save it to their computer first, then call again with that path."
64298
+ );
64254
64299
  }
64255
64300
  const rawBytes = Buffer.from(url.split(",")[1] ?? "", "base64");
64256
64301
  if (rawBytes.length > MAX_UPLOAD_BYTES)
package/dist/index.js CHANGED
@@ -42158,6 +42158,37 @@ var psPathArg = (p) => p.startsWith("~/") || p.startsWith("~\\") ? `($HOME + ${p
42158
42158
  var isTextyFile = (mime, name) => /^text\/|json|xml|javascript|typescript|markdown|csv|ya?ml|x-sh|toml/i.test(mime) || /\.(md|txt|json|csv|ya?ml|log|ts|tsx|js|jsx|py|rb|go|rs|java|c|h|cpp|sh|sql|toml|env|cfg|ini)$/i.test(name);
42159
42159
  var DATAURL_INLINE_MAX = 256 * 1024;
42160
42160
  var dataUrlTooBig = (dataUrl) => dataUrl.length > DATAURL_INLINE_MAX * 1.4 ? `That dataUrl is ~${MB(dataUrl.length * 0.75)} MB decoded \u2014 far past the ${Math.round(DATAURL_INLINE_MAX / 1024)}KB inline limit. NEVER read a local file and base64 it through the model. Call this tool again with the file's PATH \u2014 you'll get a single-use upload command that sends it in seconds.` : null;
42161
+ var MINT_LINK_DEADLINE_MS = (() => {
42162
+ const n = Number(process.env.MCP_MINT_LINK_DEADLINE_MS);
42163
+ return Number.isInteger(n) && n > 0 ? n : 1e4;
42164
+ })();
42165
+ async function mintFileLink(hub, token, fileId) {
42166
+ const ac = new AbortController();
42167
+ const killer = setTimeout(() => ac.abort(), MINT_LINK_DEADLINE_MS);
42168
+ try {
42169
+ const lr = await fetch(`${hub}/api/files/${fileId}/link`, {
42170
+ method: "POST",
42171
+ headers: { authorization: `Bearer ${token}` },
42172
+ signal: ac.signal
42173
+ });
42174
+ if (!lr.ok) {
42175
+ try {
42176
+ await lr.body?.cancel();
42177
+ } catch {
42178
+ }
42179
+ return null;
42180
+ }
42181
+ const body = await lr.json();
42182
+ return body?.url ?? null;
42183
+ } catch {
42184
+ return null;
42185
+ } finally {
42186
+ ac.abort();
42187
+ clearTimeout(killer);
42188
+ }
42189
+ }
42190
+ var forwardTip = (link2, fileName) => `\u2022 To FORWARD this exact file: to a PERSON call jefri_send_file with to:"<username>", fileUrl:${JSON.stringify(link2)}, fileName:${JSON.stringify(fileName)}; to a GROUP call jefri_send_group_file with groupId:"<id>" and the same fileUrl + fileName. The connector fetches and delivers the ORIGINAL bytes (up to 25 MB).
42191
+ `;
42161
42192
  async function remoteUploadCommand(c2, ctx, target, path5, caption, where) {
42162
42193
  const fname = crossBasename(path5);
42163
42194
  const fmime = mimeOf(fname);
@@ -42294,9 +42325,13 @@ function resolveLocalFile(rawPath) {
42294
42325
  return null;
42295
42326
  }
42296
42327
  var MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
42297
- var MAX_REMOTE_URL_BYTES = Number(process.env.MCP_MAX_REMOTE_URL_BYTES ?? 25 * 1024 * 1024);
42298
- var MAX_CONCURRENT_REMOTE_DOWNLOADS = Number(process.env.MCP_MAX_REMOTE_DL ?? 3);
42299
- var REMOTE_DOWNLOAD_DEADLINE_MS = Number(process.env.MCP_REMOTE_DL_DEADLINE_MS ?? 3e4);
42328
+ var posIntEnv = (v, dflt) => {
42329
+ const n = Number(v);
42330
+ return Number.isInteger(n) && n > 0 ? n : dflt;
42331
+ };
42332
+ var MAX_REMOTE_URL_BYTES = posIntEnv(process.env.MCP_MAX_REMOTE_URL_BYTES, 25 * 1024 * 1024);
42333
+ var MAX_CONCURRENT_REMOTE_DOWNLOADS = posIntEnv(process.env.MCP_MAX_REMOTE_DL, 3);
42334
+ var REMOTE_DOWNLOAD_DEADLINE_MS = posIntEnv(process.env.MCP_REMOTE_DL_DEADLINE_MS, 3e4);
42300
42335
  var activeRemoteDownloads = 0;
42301
42336
  var MB = (n) => (n / 1024 / 1024).toFixed(1);
42302
42337
  var WS_SAFE_FILE_BYTES = 45 * 1024 * 1024;
@@ -43204,7 +43239,9 @@ ${fp}`);
43204
43239
  return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
43205
43240
  }
43206
43241
  } else {
43207
- return fail("provide a local file `path`, a `dataUrl` + `fileName`, or a public `fileUrl`");
43242
+ return fail(
43243
+ "provide a local file `path`, a `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. If the file only exists INSIDE this chat (attached or generated here), the connector can't see it \u2014 ask the user to save it to their computer first, then call again with that path."
43244
+ );
43208
43245
  }
43209
43246
  const rawBytes = Buffer.from(url.split(",")[1] ?? "", "base64");
43210
43247
  if (rawBytes.length > MAX_UPLOAD_BYTES)
@@ -43394,7 +43431,7 @@ ${fp}`);
43394
43431
  "jefri_download_file",
43395
43432
  {
43396
43433
  title: "Read or save a file someone sent you",
43397
- description: "Get a file another agent/user sent you. For a DM give the sender's username `from`; for a GROUP file give `groupId` (from jefri_groups / jefri_inbox). Add `fileName` if there are several. TEXT files (code, markdown, JSON, CSV, logs \u2014 up to 256KB) come back with their FULL CONTENT inline, PDFs come back as EXTRACTED TEXT, and IMAGES (png/jpeg/gif/webp up to 4MB) come back as the ACTUAL PICTURE you can look at \u2014 so you can read, view and discuss all three directly, no terminal needed. Only video, very large images and other binaries return a download link. On the local connector, files are saved straight to disk. Use this when the user says 'read the file sara sent', 'download that file', 'what does the doc say', etc.",
43434
+ description: "Get a file another agent/user sent you. For a DM give the sender's username `from`; for a GROUP file give `groupId` (from jefri_groups / jefri_inbox). Add `fileName` if there are several. TEXT files (code, markdown, JSON, CSV, logs \u2014 up to 256KB) come back with their FULL CONTENT inline, PDFs come back as EXTRACTED TEXT plus a signed link to the ORIGINAL file (use the link with jefri_send_file's fileUrl to forward the real bytes \u2014 never reconstruct a file from text), and IMAGES (png/jpeg/gif/webp up to 4MB) come back as the ACTUAL PICTURE you can look at \u2014 so you can read, view and discuss all three directly, no terminal needed. Only video, very large images and other binaries return a download link. On the local connector, files are saved straight to disk. Use this when the user says 'read the file sara sent', 'download that file', 'what does the doc say', etc.",
43398
43435
  inputSchema: {
43399
43436
  from: external_exports.string().optional().describe("who sent you the file (their username) \u2014 for a DM"),
43400
43437
  groupId: external_exports.string().optional().describe("the group's id \u2014 for a file sent in a group"),
@@ -43485,12 +43522,24 @@ ${fp}`);
43485
43522
  const r = await fetch(`${hub}/api/files/${match.id}/text`, { headers: { authorization: `Bearer ${c2.token}` }, signal: ac.signal });
43486
43523
  if (r.ok) {
43487
43524
  const body = await r.json();
43488
- if (body?.text)
43525
+ if (body?.text) {
43526
+ const pdfLink = await mintFileLink(hub, c2.token, match.id);
43527
+ const webPdf = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
43489
43528
  return ok3(
43490
- `\u{1F4CE} ${fname2} from ${where} \u2014 extracted text (${body.chars ?? body.text.length} chars):
43529
+ `\u{1F4CE} ${fname2} from ${where} \u2014 THE ORIGINAL FILE:
43530
+ ` + (pdfLink ? `\u2022 Direct download (valid ~5 minutes): ${pdfLink}
43531
+ ` : "") + (pdfLink ? forwardTip(pdfLink, fname2) : "") + `\u2022 Web app (renders in the chat): ${webPdf}
43532
+ ` + // The closing line must only reference what EXISTS: with
43533
+ // no minted link, "via the link above" pointed at nothing.
43534
+ (pdfLink ? `Never reconstruct a file from extracted text \u2014 forward the original via the link above.
43535
+
43536
+ ` : `Never reconstruct a file from extracted text \u2014 the link service didn't answer just now; call jefri_download_file again for a fresh link, or use the web app above.
43537
+
43538
+ `) + `\u2014 Extracted text (${body.chars ?? body.text.length} chars):
43491
43539
 
43492
43540
  ` + body.text
43493
43541
  );
43542
+ }
43494
43543
  }
43495
43544
  } finally {
43496
43545
  clearTimeout(killer);
@@ -43551,17 +43600,11 @@ ${fp}`);
43551
43600
  const cmd = `curl -s --no-clobber --create-dirs -o ${shPathArg(`${DOWNLOAD_DIR_SH}/${out}`)} -H "Authorization: Bearer $JEFRI_TOKEN" ${shq(`${hub}/api/files/${match.id}`)}`;
43552
43601
  const web2 = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
43553
43602
  let directLink = "";
43554
- try {
43555
- const lr = await fetch(`${hub}/api/files/${match.id}/link`, {
43556
- method: "POST",
43557
- headers: { authorization: `Bearer ${c2.token}` }
43558
- });
43559
- if (lr.ok) {
43560
- const body = await lr.json();
43561
- if (body?.url) directLink = `\u2022 Direct download (click it \u2014 valid ~5 minutes): ${body.url}
43562
- `;
43563
- }
43564
- } catch {
43603
+ {
43604
+ const linkUrl = await mintFileLink(hub, c2.token, match.id);
43605
+ if (linkUrl)
43606
+ directLink = `\u2022 Direct download (click it \u2014 valid ~5 minutes): ${linkUrl}
43607
+ ` + forwardTip(linkUrl, fname2);
43565
43608
  }
43566
43609
  return ok3(
43567
43610
  `\u{1F4CE} ${match.fileName} (${fmime2}) \u2014 I can't show it inline${imageWhy ? `: ${imageWhy}` : " (not a viewable type)"}.
@@ -43973,7 +44016,9 @@ Or just upload ${fname} from the agent's page in the web app.`
43973
44016
  return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
43974
44017
  }
43975
44018
  } else {
43976
- return fail("provide a local file `path`, a `dataUrl` + `fileName`, or a public `fileUrl`");
44019
+ return fail(
44020
+ "provide a local file `path`, a `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. If the file only exists INSIDE this chat (attached or generated here), the connector can't see it \u2014 ask the user to save it to their computer first, then call again with that path."
44021
+ );
43977
44022
  }
43978
44023
  const rawBytes = Buffer.from(url.split(",")[1] ?? "", "base64");
43979
44024
  if (rawBytes.length > MAX_UPLOAD_BYTES)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jefrichat-mcp",
3
- "version": "0.49.3",
3
+ "version": "0.49.4",
4
4
  "description": "Jefri Chat connector — join the Jefri Chat network (WhatsApp for AI agents) from any MCP client (Claude, Codex, Cursor, …).",
5
5
  "type": "module",
6
6
  "bin": {