@mgsoftwarebv/mcp-server-bridge 3.3.1 → 3.3.2
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 +208 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import Stream, { Readable, Writable, Duplex } from 'stream';
|
|
|
6
6
|
import { performance } from 'perf_hooks';
|
|
7
7
|
import os, { platform, release, homedir } from 'os';
|
|
8
8
|
import fs, { ReadStream, lstatSync, fstatSync, readFileSync, promises } from 'fs';
|
|
9
|
-
import { join, dirname, sep as sep$1, normalize } from 'path';
|
|
9
|
+
import { basename, join, dirname, sep as sep$1, normalize } from 'path';
|
|
10
10
|
import fs2, { readFile } from 'fs/promises';
|
|
11
11
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
12
12
|
import { request as request$2 } from 'http';
|
|
@@ -106951,6 +106951,41 @@ var TOOLS = [
|
|
|
106951
106951
|
required: ["attachmentId"]
|
|
106952
106952
|
}
|
|
106953
106953
|
},
|
|
106954
|
+
{
|
|
106955
|
+
name: "upload-ticket-attachment",
|
|
106956
|
+
description: "Attach a file (image or document) to a ticket. Provide exactly ONE source: `filePath` (absolute local path), `imageUrl` (public URL to download), or `base64Data` (raw or data: URI). To push an image the user pasted into Cursor chat onto the ticket: when the message is sent, Cursor writes the pasted image into the workspace `assets/` folder as `image-<uuid>.png` \u2014 locate the newest `assets/image-*.png` and pass its absolute path as `filePath`. 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.",
|
|
106957
|
+
inputSchema: {
|
|
106958
|
+
type: "object",
|
|
106959
|
+
properties: {
|
|
106960
|
+
teamId: teamIdProp,
|
|
106961
|
+
ticketId: {
|
|
106962
|
+
type: "string",
|
|
106963
|
+
description: "Ticket ID (UUID) to attach the file to."
|
|
106964
|
+
},
|
|
106965
|
+
filePath: {
|
|
106966
|
+
type: "string",
|
|
106967
|
+
description: "Absolute local path to the file. For a pasted image, use the newest workspace assets/image-*.png."
|
|
106968
|
+
},
|
|
106969
|
+
imageUrl: {
|
|
106970
|
+
type: "string",
|
|
106971
|
+
description: "Public URL to download and attach."
|
|
106972
|
+
},
|
|
106973
|
+
base64Data: {
|
|
106974
|
+
type: "string",
|
|
106975
|
+
description: "Base64 file contents (raw or a data: URI)."
|
|
106976
|
+
},
|
|
106977
|
+
fileName: {
|
|
106978
|
+
type: "string",
|
|
106979
|
+
description: "Optional file name override (defaults to the source's basename)."
|
|
106980
|
+
},
|
|
106981
|
+
mimeType: {
|
|
106982
|
+
type: "string",
|
|
106983
|
+
description: "Optional MIME override; needed for base64Data without a data: prefix."
|
|
106984
|
+
}
|
|
106985
|
+
},
|
|
106986
|
+
required: ["ticketId"]
|
|
106987
|
+
}
|
|
106988
|
+
},
|
|
106954
106989
|
{
|
|
106955
106990
|
name: "get-customers",
|
|
106956
106991
|
description: "Get customers with optional search",
|
|
@@ -114629,6 +114664,7 @@ ${JSON.stringify(teams2)}`
|
|
|
114629
114664
|
|
|
114630
114665
|
// src/tools/ticket-attachments.ts
|
|
114631
114666
|
init_drizzle_orm();
|
|
114667
|
+
init_auth();
|
|
114632
114668
|
init_db2();
|
|
114633
114669
|
|
|
114634
114670
|
// ../../node_modules/@aws-sdk/middleware-expect-continue/dist-es/index.js
|
|
@@ -120089,6 +120125,52 @@ async function loadAccessibleTicket(requestedTeamId, ticketId) {
|
|
|
120089
120125
|
}
|
|
120090
120126
|
|
|
120091
120127
|
// src/tools/ticket-attachments.ts
|
|
120128
|
+
var ALLOWED_IMAGE_TYPES = [
|
|
120129
|
+
"image/jpeg",
|
|
120130
|
+
"image/png",
|
|
120131
|
+
"image/gif",
|
|
120132
|
+
"image/webp"
|
|
120133
|
+
];
|
|
120134
|
+
var ALLOWED_DOCUMENT_TYPES = [
|
|
120135
|
+
"application/pdf",
|
|
120136
|
+
"application/msword",
|
|
120137
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
120138
|
+
"application/vnd.ms-excel",
|
|
120139
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
120140
|
+
"application/vnd.ms-powerpoint",
|
|
120141
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
120142
|
+
"text/plain",
|
|
120143
|
+
"text/csv"
|
|
120144
|
+
];
|
|
120145
|
+
var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
120146
|
+
...ALLOWED_IMAGE_TYPES,
|
|
120147
|
+
...ALLOWED_DOCUMENT_TYPES
|
|
120148
|
+
]);
|
|
120149
|
+
var MAX_FILE_SIZE = 25 * 1024 * 1024;
|
|
120150
|
+
var EXT_MIME = {
|
|
120151
|
+
jpg: "image/jpeg",
|
|
120152
|
+
jpeg: "image/jpeg",
|
|
120153
|
+
png: "image/png",
|
|
120154
|
+
gif: "image/gif",
|
|
120155
|
+
webp: "image/webp",
|
|
120156
|
+
pdf: "application/pdf",
|
|
120157
|
+
doc: "application/msword",
|
|
120158
|
+
txt: "text/plain",
|
|
120159
|
+
csv: "text/csv",
|
|
120160
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
120161
|
+
xls: "application/vnd.ms-excel",
|
|
120162
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
120163
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
120164
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
120165
|
+
};
|
|
120166
|
+
function textResponse2(text3) {
|
|
120167
|
+
return { content: [{ type: "text", text: text3 }] };
|
|
120168
|
+
}
|
|
120169
|
+
function mimeFromName(name21) {
|
|
120170
|
+
if (!name21) return null;
|
|
120171
|
+
const ext = name21.split(/[?#]/)[0]?.split(".").pop()?.toLowerCase();
|
|
120172
|
+
return ext && EXT_MIME[ext] ? EXT_MIME[ext] : null;
|
|
120173
|
+
}
|
|
120092
120174
|
async function findAttachment(attachmentId) {
|
|
120093
120175
|
const [ticketAtt] = await db.select({
|
|
120094
120176
|
ticketId: schema_exports.ticketAttachments.ticketId,
|
|
@@ -120156,6 +120238,127 @@ ${url3}`
|
|
|
120156
120238
|
]
|
|
120157
120239
|
};
|
|
120158
120240
|
}
|
|
120241
|
+
async function handleUploadTicketAttachment(input) {
|
|
120242
|
+
const ctx = authContext;
|
|
120243
|
+
const sources = [input.filePath, input.imageUrl, input.base64Data].filter(
|
|
120244
|
+
(v2) => typeof v2 === "string" && v2.trim().length > 0
|
|
120245
|
+
);
|
|
120246
|
+
if (sources.length === 0) {
|
|
120247
|
+
return textResponse2(
|
|
120248
|
+
"Provide exactly one source: filePath (absolute local path), imageUrl, or base64Data."
|
|
120249
|
+
);
|
|
120250
|
+
}
|
|
120251
|
+
if (sources.length > 1) {
|
|
120252
|
+
return textResponse2(
|
|
120253
|
+
"Provide only one source (filePath, imageUrl, or base64Data), not several."
|
|
120254
|
+
);
|
|
120255
|
+
}
|
|
120256
|
+
const access = await loadAccessibleTicket(input.teamId, input.ticketId);
|
|
120257
|
+
if (!access.ok) return access.response;
|
|
120258
|
+
const ticket = access.ticket;
|
|
120259
|
+
let buffer2;
|
|
120260
|
+
let fileName = input.fileName?.trim() ?? "";
|
|
120261
|
+
let mimeType = input.mimeType?.trim() ?? "";
|
|
120262
|
+
try {
|
|
120263
|
+
if (input.filePath) {
|
|
120264
|
+
buffer2 = await readFile(input.filePath);
|
|
120265
|
+
if (!fileName) fileName = basename(input.filePath) || "attachment";
|
|
120266
|
+
if (!mimeType) {
|
|
120267
|
+
mimeType = mimeFromName(fileName) ?? "application/octet-stream";
|
|
120268
|
+
}
|
|
120269
|
+
} else if (input.imageUrl) {
|
|
120270
|
+
const res = await fetch(input.imageUrl);
|
|
120271
|
+
if (!res.ok) {
|
|
120272
|
+
return textResponse2(
|
|
120273
|
+
`Could not download from URL: HTTP ${res.status}.`
|
|
120274
|
+
);
|
|
120275
|
+
}
|
|
120276
|
+
const headerType = res.headers.get("content-type")?.split(";")[0]?.trim();
|
|
120277
|
+
buffer2 = Buffer.from(await res.arrayBuffer());
|
|
120278
|
+
if (!fileName) {
|
|
120279
|
+
fileName = input.imageUrl.split("/").pop()?.split(/[?#]/)[0] || `attachment_${Date.now()}`;
|
|
120280
|
+
}
|
|
120281
|
+
if (!mimeType) {
|
|
120282
|
+
mimeType = (headerType && headerType !== "application/octet-stream" ? headerType : null) ?? mimeFromName(input.imageUrl) ?? mimeFromName(fileName) ?? "application/octet-stream";
|
|
120283
|
+
}
|
|
120284
|
+
} else {
|
|
120285
|
+
let b64 = input.base64Data;
|
|
120286
|
+
const dataUri = b64.match(/^data:([^;]+);base64,(.*)$/s);
|
|
120287
|
+
if (dataUri) {
|
|
120288
|
+
if (!mimeType) mimeType = dataUri[1] ?? "";
|
|
120289
|
+
b64 = dataUri[2] ?? "";
|
|
120290
|
+
} else if (b64.includes(",")) {
|
|
120291
|
+
b64 = b64.split(",")[1] || b64;
|
|
120292
|
+
}
|
|
120293
|
+
buffer2 = Buffer.from(b64, "base64");
|
|
120294
|
+
if (!fileName) fileName = `attachment_${Date.now()}`;
|
|
120295
|
+
if (!mimeType) {
|
|
120296
|
+
mimeType = mimeFromName(fileName) ?? "application/octet-stream";
|
|
120297
|
+
}
|
|
120298
|
+
}
|
|
120299
|
+
} catch (error49) {
|
|
120300
|
+
return textResponse2(
|
|
120301
|
+
`Failed to read the file: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120302
|
+
);
|
|
120303
|
+
}
|
|
120304
|
+
if (buffer2.byteLength === 0) {
|
|
120305
|
+
return textResponse2("The file is empty (0 bytes); nothing to upload.");
|
|
120306
|
+
}
|
|
120307
|
+
if (buffer2.byteLength > MAX_FILE_SIZE) {
|
|
120308
|
+
return textResponse2(
|
|
120309
|
+
`File too large (${(buffer2.byteLength / 1024 / 1024).toFixed(
|
|
120310
|
+
1
|
|
120311
|
+
)} MB). Max: 25 MB.`
|
|
120312
|
+
);
|
|
120313
|
+
}
|
|
120314
|
+
if (!ALLOWED_MIME_TYPES.has(mimeType)) {
|
|
120315
|
+
return textResponse2(
|
|
120316
|
+
`Unsupported file type: ${mimeType}. Allowed: JPEG, PNG, GIF, WebP, PDF, DOC(X), XLS(X), PPT(X), TXT, CSV.`
|
|
120317
|
+
);
|
|
120318
|
+
}
|
|
120319
|
+
const storageKey = `${ticket.teamId}/tickets/${ticket.id}/${Date.now()}_${fileName}`;
|
|
120320
|
+
try {
|
|
120321
|
+
await storage.upload({
|
|
120322
|
+
bucket: "vault",
|
|
120323
|
+
path: storageKey,
|
|
120324
|
+
body: buffer2,
|
|
120325
|
+
options: { contentType: mimeType, upsert: true }
|
|
120326
|
+
});
|
|
120327
|
+
} catch (error49) {
|
|
120328
|
+
return textResponse2(
|
|
120329
|
+
`Upload failed: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120330
|
+
);
|
|
120331
|
+
}
|
|
120332
|
+
const [row] = await db.insert(schema_exports.ticketAttachments).values({
|
|
120333
|
+
ticketId: ticket.id,
|
|
120334
|
+
teamId: ticket.teamId,
|
|
120335
|
+
userId: ctx.userId,
|
|
120336
|
+
fileName,
|
|
120337
|
+
fileSize: buffer2.byteLength,
|
|
120338
|
+
mimeType,
|
|
120339
|
+
storageKey
|
|
120340
|
+
}).returning({ id: schema_exports.ticketAttachments.id });
|
|
120341
|
+
let url3 = "";
|
|
120342
|
+
try {
|
|
120343
|
+
const signed = await storage.createSignedUrl({
|
|
120344
|
+
bucket: "vault",
|
|
120345
|
+
path: storageKey,
|
|
120346
|
+
expiresIn: 3600
|
|
120347
|
+
});
|
|
120348
|
+
url3 = signed.url;
|
|
120349
|
+
} catch {
|
|
120350
|
+
}
|
|
120351
|
+
return textResponse2(
|
|
120352
|
+
`\u{1F4CE} **Attached to ${ticket.ticketNumber}**
|
|
120353
|
+
File: ${fileName}
|
|
120354
|
+
Type: ${mimeType}
|
|
120355
|
+
Size: ${Math.round(buffer2.byteLength / 1024)}KB
|
|
120356
|
+
Attachment id: ${row?.id}` + (url3 ? `
|
|
120357
|
+
|
|
120358
|
+
Download URL (valid 1 hour):
|
|
120359
|
+
${url3}` : "")
|
|
120360
|
+
);
|
|
120361
|
+
}
|
|
120159
120362
|
|
|
120160
120363
|
// src/tools/ticket-comments.ts
|
|
120161
120364
|
init_drizzle_orm();
|
|
@@ -120854,6 +121057,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request3) => {
|
|
|
120854
121057
|
return await handleGetTicketAttachment(
|
|
120855
121058
|
asToolArgs(toolArgs)
|
|
120856
121059
|
);
|
|
121060
|
+
case "upload-ticket-attachment":
|
|
121061
|
+
return await handleUploadTicketAttachment(
|
|
121062
|
+
asToolArgs(toolArgs)
|
|
121063
|
+
);
|
|
120857
121064
|
case "get-customers":
|
|
120858
121065
|
return await handleGetCustomers(asToolArgs(toolArgs));
|
|
120859
121066
|
case "create-customer":
|