@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 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":