@mgsoftwarebv/mcp-server-bridge 3.5.3 → 3.5.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.
- package/dist/index.js +235 -80
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -105954,7 +105954,7 @@ var TOOLS = [
|
|
|
105954
105954
|
},
|
|
105955
105955
|
{
|
|
105956
105956
|
name: "upload-ticket-attachment",
|
|
105957
|
-
description: "Attach a file (image or document) to a ticket. Provide exactly ONE source: `filePath` (absolute local path), `imageUrl` (
|
|
105957
|
+
description: "Attach a file (image or document) to a ticket. Provide exactly ONE source: `filePath` (absolute local path on the MCP runtime), `imageUrl` (URL to download), `uploadId` (from POST /mcp/attachment-upload \u2014 use for Hermes/Telegram gateway cache files over HTTP MCP), or `base64Data` (raw or data: URI, small files only). For Cursor pasted images: locate the newest workspace `assets/image-*.png` and pass its absolute path as `filePath` (stdio MCP). For Hermes cache paths like `/root/.hermes/image_cache/...` over HTTP MCP: POST bytes to `/mcp/attachment-upload` with the same API key, then pass the returned `uploadId`. Optionally set `HERMES_MEDIA_BASE_URL` so Hermes cache `filePath` values are fetched remotely. Allowed types: JPEG, PNG, GIF, WebP, PDF, DOC(X), XLS(X), PPT(X), TXT, CSV. Max 25 MB. Returns the attachment id and a 1-hour download URL.",
|
|
105958
105958
|
inputSchema: {
|
|
105959
105959
|
type: "object",
|
|
105960
105960
|
properties: {
|
|
@@ -105965,11 +105965,15 @@ var TOOLS = [
|
|
|
105965
105965
|
},
|
|
105966
105966
|
filePath: {
|
|
105967
105967
|
type: "string",
|
|
105968
|
-
description: "Absolute local path
|
|
105968
|
+
description: "Absolute local path on the MCP server. Works for Cursor workspace files with stdio MCP; Hermes gateway paths require uploadId or HERMES_MEDIA_BASE_URL."
|
|
105969
105969
|
},
|
|
105970
105970
|
imageUrl: {
|
|
105971
105971
|
type: "string",
|
|
105972
|
-
description: "Public URL to download and attach."
|
|
105972
|
+
description: "Public or gateway URL to download and attach."
|
|
105973
|
+
},
|
|
105974
|
+
uploadId: {
|
|
105975
|
+
type: "string",
|
|
105976
|
+
description: "Staged upload id from POST /mcp/attachment-upload. Preferred for original Telegram/Hermes media over HTTP MCP."
|
|
105973
105977
|
},
|
|
105974
105978
|
base64Data: {
|
|
105975
105979
|
type: "string",
|
|
@@ -120394,8 +120398,6 @@ var storage = new Proxy({}, {
|
|
|
120394
120398
|
return Reflect.get(_storage, prop, _storage);
|
|
120395
120399
|
}
|
|
120396
120400
|
});
|
|
120397
|
-
|
|
120398
|
-
// src/tools/ticket-attachments.ts
|
|
120399
120401
|
var ALLOWED_IMAGE_TYPES = [
|
|
120400
120402
|
"image/jpeg",
|
|
120401
120403
|
"image/png",
|
|
@@ -120434,14 +120436,213 @@ var EXT_MIME = {
|
|
|
120434
120436
|
ppt: "application/vnd.ms-powerpoint",
|
|
120435
120437
|
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
120436
120438
|
};
|
|
120437
|
-
|
|
120438
|
-
return { content: [{ type: "text", text: text3 }] };
|
|
120439
|
-
}
|
|
120439
|
+
var HERMES_CACHE_PATH = /(?:^|[\\/])\.hermes[\\/](.+)$/i;
|
|
120440
120440
|
function mimeFromName(name21) {
|
|
120441
120441
|
if (!name21) return null;
|
|
120442
120442
|
const ext = name21.split(/[?#]/)[0]?.split(".").pop()?.toLowerCase();
|
|
120443
120443
|
return ext && EXT_MIME[ext] ? EXT_MIME[ext] : null;
|
|
120444
120444
|
}
|
|
120445
|
+
function isHermesCachePath(filePath) {
|
|
120446
|
+
return HERMES_CACHE_PATH.test(filePath.replace(/\\/g, "/"));
|
|
120447
|
+
}
|
|
120448
|
+
function hermesCacheRelativePath(filePath) {
|
|
120449
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
120450
|
+
const match = HERMES_CACHE_PATH.exec(normalized);
|
|
120451
|
+
return match?.[1] ?? null;
|
|
120452
|
+
}
|
|
120453
|
+
function getHermesMediaBaseUrl() {
|
|
120454
|
+
const base = process.env.HERMES_MEDIA_BASE_URL?.trim() || process.env.HERMES_GATEWAY_MEDIA_URL?.trim();
|
|
120455
|
+
return base ? base.replace(/\/+$/, "") : null;
|
|
120456
|
+
}
|
|
120457
|
+
function resolveHermesCacheMediaUrl(filePath) {
|
|
120458
|
+
const base = getHermesMediaBaseUrl();
|
|
120459
|
+
const relative = hermesCacheRelativePath(filePath);
|
|
120460
|
+
if (!base || !relative) return null;
|
|
120461
|
+
return `${base}/${relative.replace(/^\/+/, "")}`;
|
|
120462
|
+
}
|
|
120463
|
+
function parseMcpStagingStorageKey(uploadId, teamId, userId) {
|
|
120464
|
+
const normalized = uploadId.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
120465
|
+
const prefix = `${teamId}/mcp-staging/${userId}/`;
|
|
120466
|
+
if (!normalized.startsWith(prefix)) return null;
|
|
120467
|
+
const remainder = normalized.slice(prefix.length);
|
|
120468
|
+
if (!remainder || remainder.includes("..")) return null;
|
|
120469
|
+
return normalized;
|
|
120470
|
+
}
|
|
120471
|
+
function formatFilePathEnoentError(filePath) {
|
|
120472
|
+
const hermesHint = isHermesCachePath(filePath) ? getHermesMediaBaseUrl() ? " Hermes cache paths are fetched via HERMES_MEDIA_BASE_URL when local read fails." : " For Hermes/Telegram cache files over HTTP MCP, POST the bytes to /mcp/attachment-upload and pass the returned uploadId, or set HERMES_MEDIA_BASE_URL so cache paths can be fetched remotely." : " filePath must exist on the MCP server filesystem (works for Cursor workspace paths with stdio MCP, not for gateway-local paths over HTTP MCP).";
|
|
120473
|
+
return `Failed to read the file at "${filePath}": path not found in the MCP runtime (ENOENT).${hermesHint} Alternatives: imageUrl (download URL), uploadId (from POST /mcp/attachment-upload), or base64Data for small files.`;
|
|
120474
|
+
}
|
|
120475
|
+
function hermesMediaFetchHeaders() {
|
|
120476
|
+
const token = process.env.HERMES_MEDIA_AUTH_TOKEN?.trim() || process.env.HERMES_MEDIA_BEARER_TOKEN?.trim();
|
|
120477
|
+
if (!token) return void 0;
|
|
120478
|
+
return { Authorization: `Bearer ${token}` };
|
|
120479
|
+
}
|
|
120480
|
+
async function fetchAttachmentUrl(url3, fallbackName, mimeOverride) {
|
|
120481
|
+
const res = await fetch(url3, { headers: hermesMediaFetchHeaders() });
|
|
120482
|
+
if (!res.ok) {
|
|
120483
|
+
return {
|
|
120484
|
+
ok: false,
|
|
120485
|
+
message: `Could not download from URL: HTTP ${res.status}.`
|
|
120486
|
+
};
|
|
120487
|
+
}
|
|
120488
|
+
const headerType = res.headers.get("content-type")?.split(";")[0]?.trim();
|
|
120489
|
+
const buffer2 = Buffer.from(await res.arrayBuffer());
|
|
120490
|
+
const fileName = fallbackName || url3.split("/").pop()?.split(/[?#]/)[0] || `attachment_${Date.now()}`;
|
|
120491
|
+
const mimeType = mimeOverride?.trim() || (headerType && headerType !== "application/octet-stream" ? headerType : null) || mimeFromName(url3) || mimeFromName(fileName) || "application/octet-stream";
|
|
120492
|
+
return { ok: true, buffer: buffer2, fileName, mimeType };
|
|
120493
|
+
}
|
|
120494
|
+
async function resolveFromFilePath(filePath, fileNameOverride, mimeOverride) {
|
|
120495
|
+
try {
|
|
120496
|
+
const buffer2 = await readFile(filePath);
|
|
120497
|
+
const fileName = fileNameOverride?.trim() || basename(filePath) || "attachment";
|
|
120498
|
+
const mimeType = mimeOverride?.trim() || mimeFromName(fileName) || "application/octet-stream";
|
|
120499
|
+
return { ok: true, buffer: buffer2, fileName, mimeType };
|
|
120500
|
+
} catch (error49) {
|
|
120501
|
+
const code = error49 && typeof error49 === "object" && "code" in error49 ? String(error49.code) : "";
|
|
120502
|
+
if (code !== "ENOENT") {
|
|
120503
|
+
return {
|
|
120504
|
+
ok: false,
|
|
120505
|
+
message: `Failed to read the file: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120506
|
+
};
|
|
120507
|
+
}
|
|
120508
|
+
const hermesUrl = resolveHermesCacheMediaUrl(filePath);
|
|
120509
|
+
if (hermesUrl) {
|
|
120510
|
+
const fetched = await fetchAttachmentUrl(
|
|
120511
|
+
hermesUrl,
|
|
120512
|
+
fileNameOverride?.trim() || basename(filePath) || void 0,
|
|
120513
|
+
mimeOverride
|
|
120514
|
+
);
|
|
120515
|
+
if (fetched.ok) {
|
|
120516
|
+
return {
|
|
120517
|
+
ok: true,
|
|
120518
|
+
buffer: fetched.buffer,
|
|
120519
|
+
fileName: fetched.fileName,
|
|
120520
|
+
mimeType: fetched.mimeType
|
|
120521
|
+
};
|
|
120522
|
+
}
|
|
120523
|
+
return {
|
|
120524
|
+
ok: false,
|
|
120525
|
+
message: `${formatFilePathEnoentError(filePath)} Hermes media fetch also failed: ${fetched.message}`
|
|
120526
|
+
};
|
|
120527
|
+
}
|
|
120528
|
+
return { ok: false, message: formatFilePathEnoentError(filePath) };
|
|
120529
|
+
}
|
|
120530
|
+
}
|
|
120531
|
+
async function resolveFromUploadId(uploadId, teamId, userId, fileNameOverride, mimeOverride) {
|
|
120532
|
+
const storageKey = parseMcpStagingStorageKey(uploadId, teamId, userId);
|
|
120533
|
+
if (!storageKey) {
|
|
120534
|
+
return {
|
|
120535
|
+
ok: false,
|
|
120536
|
+
message: "Invalid uploadId. Use the uploadId returned by POST /mcp/attachment-upload for your API key user."
|
|
120537
|
+
};
|
|
120538
|
+
}
|
|
120539
|
+
let downloaded;
|
|
120540
|
+
try {
|
|
120541
|
+
downloaded = await storage.download({ bucket: "vault", path: storageKey });
|
|
120542
|
+
} catch (error49) {
|
|
120543
|
+
return {
|
|
120544
|
+
ok: false,
|
|
120545
|
+
message: `Staged upload not found or expired (${uploadId}): ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120546
|
+
};
|
|
120547
|
+
}
|
|
120548
|
+
const buffer2 = Buffer.from(await downloaded.blob.arrayBuffer());
|
|
120549
|
+
const defaultName = storageKey.split("/").pop() || "attachment";
|
|
120550
|
+
const fileName = fileNameOverride?.trim() || defaultName;
|
|
120551
|
+
const mimeType = mimeOverride?.trim() || downloaded.contentType || mimeFromName(fileName) || "application/octet-stream";
|
|
120552
|
+
return {
|
|
120553
|
+
ok: true,
|
|
120554
|
+
buffer: buffer2,
|
|
120555
|
+
fileName,
|
|
120556
|
+
mimeType,
|
|
120557
|
+
stagingStorageKey: storageKey
|
|
120558
|
+
};
|
|
120559
|
+
}
|
|
120560
|
+
async function resolveAttachmentSource(input) {
|
|
120561
|
+
const sources = [
|
|
120562
|
+
input.filePath,
|
|
120563
|
+
input.imageUrl,
|
|
120564
|
+
input.base64Data,
|
|
120565
|
+
input.uploadId
|
|
120566
|
+
].filter((v2) => typeof v2 === "string" && v2.trim().length > 0);
|
|
120567
|
+
if (sources.length === 0) {
|
|
120568
|
+
return {
|
|
120569
|
+
ok: false,
|
|
120570
|
+
message: "Provide exactly one source: filePath, imageUrl, uploadId (from POST /mcp/attachment-upload), or base64Data."
|
|
120571
|
+
};
|
|
120572
|
+
}
|
|
120573
|
+
if (sources.length > 1) {
|
|
120574
|
+
return {
|
|
120575
|
+
ok: false,
|
|
120576
|
+
message: "Provide only one source (filePath, imageUrl, uploadId, or base64Data), not several."
|
|
120577
|
+
};
|
|
120578
|
+
}
|
|
120579
|
+
if (input.uploadId) {
|
|
120580
|
+
return resolveFromUploadId(
|
|
120581
|
+
input.uploadId.trim(),
|
|
120582
|
+
input.teamId,
|
|
120583
|
+
input.userId,
|
|
120584
|
+
input.fileName,
|
|
120585
|
+
input.mimeType
|
|
120586
|
+
);
|
|
120587
|
+
}
|
|
120588
|
+
if (input.filePath) {
|
|
120589
|
+
return resolveFromFilePath(input.filePath, input.fileName, input.mimeType);
|
|
120590
|
+
}
|
|
120591
|
+
if (input.imageUrl) {
|
|
120592
|
+
const fetched = await fetchAttachmentUrl(
|
|
120593
|
+
input.imageUrl,
|
|
120594
|
+
input.fileName,
|
|
120595
|
+
input.mimeType
|
|
120596
|
+
);
|
|
120597
|
+
if (!fetched.ok) return fetched;
|
|
120598
|
+
return {
|
|
120599
|
+
ok: true,
|
|
120600
|
+
buffer: fetched.buffer,
|
|
120601
|
+
fileName: fetched.fileName,
|
|
120602
|
+
mimeType: fetched.mimeType
|
|
120603
|
+
};
|
|
120604
|
+
}
|
|
120605
|
+
let b64 = input.base64Data;
|
|
120606
|
+
let mimeType = input.mimeType?.trim() ?? "";
|
|
120607
|
+
const dataUri = b64.match(/^data:([^;]+);base64,(.*)$/s);
|
|
120608
|
+
if (dataUri) {
|
|
120609
|
+
if (!mimeType) mimeType = dataUri[1] ?? "";
|
|
120610
|
+
b64 = dataUri[2] ?? "";
|
|
120611
|
+
} else if (b64.includes(",")) {
|
|
120612
|
+
b64 = b64.split(",")[1] || b64;
|
|
120613
|
+
}
|
|
120614
|
+
const buffer2 = Buffer.from(b64, "base64");
|
|
120615
|
+
const fileName = input.fileName?.trim() || `attachment_${Date.now()}`;
|
|
120616
|
+
if (!mimeType) {
|
|
120617
|
+
mimeType = mimeFromName(fileName) ?? "application/octet-stream";
|
|
120618
|
+
}
|
|
120619
|
+
return { ok: true, buffer: buffer2, fileName, mimeType };
|
|
120620
|
+
}
|
|
120621
|
+
function validateAttachmentBuffer(buffer2, mimeType) {
|
|
120622
|
+
if (buffer2.byteLength === 0) {
|
|
120623
|
+
return { ok: false, message: "The file is empty (0 bytes); nothing to upload." };
|
|
120624
|
+
}
|
|
120625
|
+
if (buffer2.byteLength > MAX_FILE_SIZE) {
|
|
120626
|
+
return {
|
|
120627
|
+
ok: false,
|
|
120628
|
+
message: `File too large (${(buffer2.byteLength / 1024 / 1024).toFixed(
|
|
120629
|
+
1
|
|
120630
|
+
)} MB). Max: 25 MB.`
|
|
120631
|
+
};
|
|
120632
|
+
}
|
|
120633
|
+
if (!ALLOWED_MIME_TYPES.has(mimeType)) {
|
|
120634
|
+
return {
|
|
120635
|
+
ok: false,
|
|
120636
|
+
message: `Unsupported file type: ${mimeType}. Allowed: JPEG, PNG, GIF, WebP, PDF, DOC(X), XLS(X), PPT(X), TXT, CSV.`
|
|
120637
|
+
};
|
|
120638
|
+
}
|
|
120639
|
+
return null;
|
|
120640
|
+
}
|
|
120641
|
+
|
|
120642
|
+
// src/tools/ticket-attachments.ts
|
|
120643
|
+
function textResponse5(text3) {
|
|
120644
|
+
return { content: [{ type: "text", text: text3 }] };
|
|
120645
|
+
}
|
|
120445
120646
|
async function findAttachment(attachmentId) {
|
|
120446
120647
|
const [ticketAtt] = await db.select({
|
|
120447
120648
|
ticketId: schema_exports.ticketAttachments.ticketId,
|
|
@@ -120510,82 +120711,30 @@ ${url3}`
|
|
|
120510
120711
|
};
|
|
120511
120712
|
}
|
|
120512
120713
|
async function handleUploadTicketAttachment(input) {
|
|
120513
|
-
const ctx = getAuthContext();
|
|
120514
|
-
|
|
120515
|
-
(
|
|
120516
|
-
);
|
|
120517
|
-
if (sources.length === 0) {
|
|
120518
|
-
return textResponse5(
|
|
120519
|
-
"Provide exactly one source: filePath (absolute local path), imageUrl, or base64Data."
|
|
120520
|
-
);
|
|
120521
|
-
}
|
|
120522
|
-
if (sources.length > 1) {
|
|
120523
|
-
return textResponse5(
|
|
120524
|
-
"Provide only one source (filePath, imageUrl, or base64Data), not several."
|
|
120525
|
-
);
|
|
120714
|
+
const ctx = getAuthContext() ?? authContext;
|
|
120715
|
+
if (!ctx) {
|
|
120716
|
+
return textResponse5("Error: Not authenticated.");
|
|
120526
120717
|
}
|
|
120527
120718
|
const access = await loadAccessibleTicket(input.teamId, input.ticketId);
|
|
120528
120719
|
if (!access.ok) return access.response;
|
|
120529
120720
|
const ticket = access.ticket;
|
|
120530
|
-
|
|
120531
|
-
|
|
120532
|
-
|
|
120533
|
-
|
|
120534
|
-
|
|
120535
|
-
|
|
120536
|
-
|
|
120537
|
-
|
|
120538
|
-
|
|
120539
|
-
|
|
120540
|
-
|
|
120541
|
-
|
|
120542
|
-
if (!res.ok) {
|
|
120543
|
-
return textResponse5(
|
|
120544
|
-
`Could not download from URL: HTTP ${res.status}.`
|
|
120545
|
-
);
|
|
120546
|
-
}
|
|
120547
|
-
const headerType = res.headers.get("content-type")?.split(";")[0]?.trim();
|
|
120548
|
-
buffer2 = Buffer.from(await res.arrayBuffer());
|
|
120549
|
-
if (!fileName) {
|
|
120550
|
-
fileName = input.imageUrl.split("/").pop()?.split(/[?#]/)[0] || `attachment_${Date.now()}`;
|
|
120551
|
-
}
|
|
120552
|
-
if (!mimeType) {
|
|
120553
|
-
mimeType = (headerType && headerType !== "application/octet-stream" ? headerType : null) ?? mimeFromName(input.imageUrl) ?? mimeFromName(fileName) ?? "application/octet-stream";
|
|
120554
|
-
}
|
|
120555
|
-
} else {
|
|
120556
|
-
let b64 = input.base64Data;
|
|
120557
|
-
const dataUri = b64.match(/^data:([^;]+);base64,(.*)$/s);
|
|
120558
|
-
if (dataUri) {
|
|
120559
|
-
if (!mimeType) mimeType = dataUri[1] ?? "";
|
|
120560
|
-
b64 = dataUri[2] ?? "";
|
|
120561
|
-
} else if (b64.includes(",")) {
|
|
120562
|
-
b64 = b64.split(",")[1] || b64;
|
|
120563
|
-
}
|
|
120564
|
-
buffer2 = Buffer.from(b64, "base64");
|
|
120565
|
-
if (!fileName) fileName = `attachment_${Date.now()}`;
|
|
120566
|
-
if (!mimeType) {
|
|
120567
|
-
mimeType = mimeFromName(fileName) ?? "application/octet-stream";
|
|
120568
|
-
}
|
|
120569
|
-
}
|
|
120570
|
-
} catch (error49) {
|
|
120571
|
-
return textResponse5(
|
|
120572
|
-
`Failed to read the file: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120573
|
-
);
|
|
120574
|
-
}
|
|
120575
|
-
if (buffer2.byteLength === 0) {
|
|
120576
|
-
return textResponse5("The file is empty (0 bytes); nothing to upload.");
|
|
120577
|
-
}
|
|
120578
|
-
if (buffer2.byteLength > MAX_FILE_SIZE) {
|
|
120579
|
-
return textResponse5(
|
|
120580
|
-
`File too large (${(buffer2.byteLength / 1024 / 1024).toFixed(
|
|
120581
|
-
1
|
|
120582
|
-
)} MB). Max: 25 MB.`
|
|
120583
|
-
);
|
|
120721
|
+
const resolved = await resolveAttachmentSource({
|
|
120722
|
+
filePath: input.filePath,
|
|
120723
|
+
imageUrl: input.imageUrl,
|
|
120724
|
+
base64Data: input.base64Data,
|
|
120725
|
+
uploadId: input.uploadId,
|
|
120726
|
+
fileName: input.fileName,
|
|
120727
|
+
mimeType: input.mimeType,
|
|
120728
|
+
teamId: ctx.teamId,
|
|
120729
|
+
userId: ctx.userId
|
|
120730
|
+
});
|
|
120731
|
+
if (!resolved.ok) {
|
|
120732
|
+
return textResponse5(resolved.message);
|
|
120584
120733
|
}
|
|
120585
|
-
|
|
120586
|
-
|
|
120587
|
-
|
|
120588
|
-
);
|
|
120734
|
+
const { buffer: buffer2, fileName, mimeType, stagingStorageKey } = resolved;
|
|
120735
|
+
const validationError = validateAttachmentBuffer(buffer2, mimeType);
|
|
120736
|
+
if (validationError) {
|
|
120737
|
+
return textResponse5(validationError.message);
|
|
120589
120738
|
}
|
|
120590
120739
|
const storageKey = `${ticket.teamId}/tickets/${ticket.id}/${Date.now()}_${fileName}`;
|
|
120591
120740
|
try {
|
|
@@ -120600,6 +120749,12 @@ async function handleUploadTicketAttachment(input) {
|
|
|
120600
120749
|
`Upload failed: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120601
120750
|
);
|
|
120602
120751
|
}
|
|
120752
|
+
if (stagingStorageKey) {
|
|
120753
|
+
try {
|
|
120754
|
+
await storage.remove({ bucket: "vault", paths: [stagingStorageKey] });
|
|
120755
|
+
} catch {
|
|
120756
|
+
}
|
|
120757
|
+
}
|
|
120603
120758
|
const [row] = await db.insert(schema_exports.ticketAttachments).values({
|
|
120604
120759
|
ticketId: ticket.id,
|
|
120605
120760
|
teamId: ticket.teamId,
|
|
@@ -122624,7 +122779,7 @@ ${tagErrors.map((e6) => ` \u2022 ${e6}`).join("\n")}
|
|
|
122624
122779
|
}
|
|
122625
122780
|
|
|
122626
122781
|
// src/server.ts
|
|
122627
|
-
var SERVER_VERSION = "3.5.
|
|
122782
|
+
var SERVER_VERSION = "3.5.4";
|
|
122628
122783
|
function createMcpServer() {
|
|
122629
122784
|
const server = new Server(
|
|
122630
122785
|
{
|