@mgsoftwarebv/mcp-server-bridge 3.3.5 → 3.3.7
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 +5 -5
- package/dist/index.js +97 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,11 +98,11 @@ Use **get-teams** to list the providers you can act on and copy a `teamId`.
|
|
|
98
98
|
- **get-teams** — list the provider teams (workspaces) you can act on; use a returned id as `teamId` on other tools
|
|
99
99
|
|
|
100
100
|
### Tickets, Customers & Projects
|
|
101
|
-
- **get-tickets** — list tickets with filters (status, priority, project, customer, text query)
|
|
102
|
-
- **get-ticket-by-id** — fetch one ticket with comment text, a full attachment listing (ids), and inline images (base64)
|
|
103
|
-
- **create-ticket** — create a new ticket
|
|
104
|
-
- **update-ticket** — update fields (title, description, status, priority, type, project, customer, assignee, estimated hours); `assigneeId: null` unassigns
|
|
105
|
-
- **add-ticket-comment** — add a comment (plain text or TipTap JSON); `isInternal` for internal-only notes
|
|
101
|
+
- **get-tickets** — list tickets with filters (status, priority, project, customer, text query); each result includes the ticket ID (UUID) and ticket number
|
|
102
|
+
- **get-ticket-by-id** — fetch one ticket by UUID or ticket number, with comment text, a full attachment listing (ids), and inline images (base64)
|
|
103
|
+
- **create-ticket** — create a new ticket (returns UUID and ticket number)
|
|
104
|
+
- **update-ticket** — update fields (title, description, status, priority, type, project, customer, assignee, estimated hours); accepts UUID or ticket number; `assigneeId: null` unassigns
|
|
105
|
+
- **add-ticket-comment** — add a comment (plain text or TipTap JSON); accepts UUID or ticket number; `isInternal` for internal-only notes
|
|
106
106
|
- **get-ticket-comments** — read all comments as plain text (author, internal flag, timestamp)
|
|
107
107
|
- **get-ticket-attachment** — get a 1-hour signed download URL for any ticket/comment attachment (any file type)
|
|
108
108
|
- **get-customers** — list/search customers
|
package/dist/index.js
CHANGED
|
@@ -105508,6 +105508,10 @@ var teamIdProp = {
|
|
|
105508
105508
|
type: "string",
|
|
105509
105509
|
description: "Provider team ID. Optional when you belong to a single provider; required when you belong to several. Call get-teams to list the providers you can act on."
|
|
105510
105510
|
};
|
|
105511
|
+
var ticketIdentifierProp = {
|
|
105512
|
+
type: "string",
|
|
105513
|
+
description: "Ticket ID (UUID) or ticket number (e.g. 2026-REFRO-116). Pass teamId when using a ticket number across multiple providers."
|
|
105514
|
+
};
|
|
105511
105515
|
var TOOLS = [
|
|
105512
105516
|
{
|
|
105513
105517
|
name: "get-teams",
|
|
@@ -105520,7 +105524,7 @@ var TOOLS = [
|
|
|
105520
105524
|
},
|
|
105521
105525
|
{
|
|
105522
105526
|
name: "get-tickets",
|
|
105523
|
-
description: "Get tickets with optional filtering by status, priority, project, customer, tags, or search query. Each ticket includes its tags.",
|
|
105527
|
+
description: "Get tickets with optional filtering by status, priority, project, customer, tags, or search query. Each ticket includes its ID (UUID), ticket number, and tags.",
|
|
105524
105528
|
inputSchema: {
|
|
105525
105529
|
type: "object",
|
|
105526
105530
|
properties: {
|
|
@@ -105567,12 +105571,12 @@ var TOOLS = [
|
|
|
105567
105571
|
},
|
|
105568
105572
|
{
|
|
105569
105573
|
name: "get-ticket-by-id",
|
|
105570
|
-
description: "Get a specific ticket by its ID, including tags, comment text and a full attachment listing (with ids). Images from ticket and comment attachments are downloaded and returned inline as base64. For non-image attachments, call get-ticket-attachment with the listed id to get a download URL.",
|
|
105574
|
+
description: "Get a specific ticket by its ID or ticket number, including tags, comment text and a full attachment listing (with ids). Images from ticket and comment attachments are downloaded and returned inline as base64. For non-image attachments, call get-ticket-attachment with the listed id to get a download URL.",
|
|
105571
105575
|
inputSchema: {
|
|
105572
105576
|
type: "object",
|
|
105573
105577
|
properties: {
|
|
105574
105578
|
teamId: teamIdProp,
|
|
105575
|
-
id:
|
|
105579
|
+
id: ticketIdentifierProp
|
|
105576
105580
|
},
|
|
105577
105581
|
required: ["id"]
|
|
105578
105582
|
}
|
|
@@ -105642,7 +105646,7 @@ var TOOLS = [
|
|
|
105642
105646
|
type: "object",
|
|
105643
105647
|
properties: {
|
|
105644
105648
|
teamId: teamIdProp,
|
|
105645
|
-
id:
|
|
105649
|
+
id: ticketIdentifierProp,
|
|
105646
105650
|
title: { type: "string" },
|
|
105647
105651
|
description: {
|
|
105648
105652
|
type: "string",
|
|
@@ -105844,7 +105848,7 @@ var TOOLS = [
|
|
|
105844
105848
|
type: "object",
|
|
105845
105849
|
properties: {
|
|
105846
105850
|
teamId: teamIdProp,
|
|
105847
|
-
ticketId:
|
|
105851
|
+
ticketId: ticketIdentifierProp,
|
|
105848
105852
|
content: { type: "string", description: "Comment body" },
|
|
105849
105853
|
isInternal: { type: "boolean", default: false }
|
|
105850
105854
|
},
|
|
@@ -105858,7 +105862,7 @@ var TOOLS = [
|
|
|
105858
105862
|
type: "object",
|
|
105859
105863
|
properties: {
|
|
105860
105864
|
teamId: teamIdProp,
|
|
105861
|
-
ticketId:
|
|
105865
|
+
ticketId: ticketIdentifierProp
|
|
105862
105866
|
},
|
|
105863
105867
|
required: ["ticketId"]
|
|
105864
105868
|
}
|
|
@@ -105886,8 +105890,8 @@ var TOOLS = [
|
|
|
105886
105890
|
properties: {
|
|
105887
105891
|
teamId: teamIdProp,
|
|
105888
105892
|
ticketId: {
|
|
105889
|
-
|
|
105890
|
-
description: "Ticket ID (UUID) to attach the file to."
|
|
105893
|
+
...ticketIdentifierProp,
|
|
105894
|
+
description: "Ticket ID (UUID) or ticket number to attach the file to."
|
|
105891
105895
|
},
|
|
105892
105896
|
filePath: {
|
|
105893
105897
|
type: "string",
|
|
@@ -106688,17 +106692,87 @@ async function resolveTeamScope(requestedTeamId) {
|
|
|
106688
106692
|
}
|
|
106689
106693
|
|
|
106690
106694
|
// src/tools/ticket-access.ts
|
|
106691
|
-
|
|
106695
|
+
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
106696
|
+
function isUuid(value) {
|
|
106697
|
+
return UUID_REGEX.test(value);
|
|
106698
|
+
}
|
|
106699
|
+
function notFoundResponse(identifier) {
|
|
106692
106700
|
return {
|
|
106693
106701
|
content: [
|
|
106694
106702
|
{
|
|
106695
106703
|
type: "text",
|
|
106696
|
-
text: `Ticket not found or no access: ${
|
|
106704
|
+
text: `Ticket not found or no access: ${identifier}. Call get-tickets to find the correct ticket.`
|
|
106697
106705
|
}
|
|
106698
106706
|
]
|
|
106699
106707
|
};
|
|
106700
106708
|
}
|
|
106709
|
+
function multipleMatchesResponse(identifier, matches) {
|
|
106710
|
+
const list = matches.map((t8) => `- **${t8.ticketNumber}** (ID: ${t8.id}): ${t8.title}`).join("\n");
|
|
106711
|
+
return {
|
|
106712
|
+
content: [
|
|
106713
|
+
{
|
|
106714
|
+
type: "text",
|
|
106715
|
+
text: `Multiple tickets match "${identifier}":
|
|
106716
|
+
${list}
|
|
106717
|
+
|
|
106718
|
+
Pass a more specific ticket number or include \`teamId\` to disambiguate.`
|
|
106719
|
+
}
|
|
106720
|
+
]
|
|
106721
|
+
};
|
|
106722
|
+
}
|
|
106723
|
+
var ticketLookupFields = {
|
|
106724
|
+
id: schema_exports.tickets.id,
|
|
106725
|
+
ticketNumber: schema_exports.tickets.ticketNumber,
|
|
106726
|
+
title: schema_exports.tickets.title
|
|
106727
|
+
};
|
|
106728
|
+
async function resolveTicketIdentifier(requestedTeamId, identifier) {
|
|
106729
|
+
if (isUuid(identifier)) {
|
|
106730
|
+
return { ok: true, id: identifier };
|
|
106731
|
+
}
|
|
106732
|
+
const scope = await resolveTeamScope(requestedTeamId);
|
|
106733
|
+
if (!scope.ok) return scope;
|
|
106734
|
+
const accessPredicate = buildTicketAccessPredicate(
|
|
106735
|
+
scope.teamIds,
|
|
106736
|
+
scope.projectIds,
|
|
106737
|
+
scope.customerIds
|
|
106738
|
+
);
|
|
106739
|
+
const baseFilters = [
|
|
106740
|
+
accessPredicate,
|
|
106741
|
+
eq(schema_exports.tickets.isDeleted, false)
|
|
106742
|
+
];
|
|
106743
|
+
const exactMatches = await db.select(ticketLookupFields).from(schema_exports.tickets).where(
|
|
106744
|
+
and(
|
|
106745
|
+
...baseFilters,
|
|
106746
|
+
sql`lower(${schema_exports.tickets.ticketNumber}) = lower(${identifier})`
|
|
106747
|
+
)
|
|
106748
|
+
).limit(5);
|
|
106749
|
+
let matches = exactMatches;
|
|
106750
|
+
if (matches.length === 0) {
|
|
106751
|
+
matches = await db.select(ticketLookupFields).from(schema_exports.tickets).where(
|
|
106752
|
+
and(...baseFilters, ilike(schema_exports.tickets.ticketNumber, `%${identifier}%`))
|
|
106753
|
+
).limit(5);
|
|
106754
|
+
}
|
|
106755
|
+
if (matches.length === 0) {
|
|
106756
|
+
return {
|
|
106757
|
+
ok: false,
|
|
106758
|
+
response: {
|
|
106759
|
+
content: [
|
|
106760
|
+
{
|
|
106761
|
+
type: "text",
|
|
106762
|
+
text: `No ticket found with number "${identifier}". Call get-tickets to search by title or keyword.`
|
|
106763
|
+
}
|
|
106764
|
+
]
|
|
106765
|
+
}
|
|
106766
|
+
};
|
|
106767
|
+
}
|
|
106768
|
+
if (matches.length > 1) {
|
|
106769
|
+
return { ok: false, response: multipleMatchesResponse(identifier, matches) };
|
|
106770
|
+
}
|
|
106771
|
+
return { ok: true, id: matches[0].id };
|
|
106772
|
+
}
|
|
106701
106773
|
async function loadAccessibleTicket(requestedTeamId, ticketId) {
|
|
106774
|
+
const resolved = await resolveTicketIdentifier(requestedTeamId, ticketId);
|
|
106775
|
+
if (!resolved.ok) return resolved;
|
|
106702
106776
|
const scope = await resolveTeamScope(requestedTeamId);
|
|
106703
106777
|
if (!scope.ok) return scope;
|
|
106704
106778
|
const [ticket] = await db.select({
|
|
@@ -106712,7 +106786,7 @@ async function loadAccessibleTicket(requestedTeamId, ticketId) {
|
|
|
106712
106786
|
priority: schema_exports.tickets.priority,
|
|
106713
106787
|
type: schema_exports.tickets.type,
|
|
106714
106788
|
assigneeId: schema_exports.tickets.assigneeId
|
|
106715
|
-
}).from(schema_exports.tickets).where(eq(schema_exports.tickets.id,
|
|
106789
|
+
}).from(schema_exports.tickets).where(eq(schema_exports.tickets.id, resolved.id)).limit(1);
|
|
106716
106790
|
if (!ticket) return { ok: false, response: notFoundResponse(ticketId) };
|
|
106717
106791
|
const hasAccess = scope.teamIds.includes(ticket.teamId) || !!ticket.projectId && scope.projectIds.includes(ticket.projectId) || !!ticket.customerId && scope.customerIds.includes(ticket.customerId);
|
|
106718
106792
|
if (!hasAccess) return { ok: false, response: notFoundResponse(ticketId) };
|
|
@@ -119809,6 +119883,10 @@ function tiptapToPlainText(content) {
|
|
|
119809
119883
|
if (doc?.type !== "doc" || !Array.isArray(doc.content)) return content;
|
|
119810
119884
|
return doc.content.map(renderNode).join("").trim();
|
|
119811
119885
|
}
|
|
119886
|
+
function descriptionPlainText(content) {
|
|
119887
|
+
const text3 = tiptapToPlainText(content).trim();
|
|
119888
|
+
return text3 || null;
|
|
119889
|
+
}
|
|
119812
119890
|
|
|
119813
119891
|
// src/tools/ticket-comments.ts
|
|
119814
119892
|
async function handleAddTicketComment(input) {
|
|
@@ -120462,6 +120540,7 @@ async function handleGetTickets(input) {
|
|
|
120462
120540
|
${rows.map((t8) => {
|
|
120463
120541
|
const ticketTags2 = tagsByTicket.get(t8.id) ?? [];
|
|
120464
120542
|
return `**${t8.ticketNumber}**: ${t8.title}
|
|
120543
|
+
ID: ${t8.id}
|
|
120465
120544
|
Status: ${t8.status} | Priority: ${t8.priority}
|
|
120466
120545
|
${ticketTags2.length > 0 ? `Tags: ${formatTagList(ticketTags2)}
|
|
120467
120546
|
` : ""}${t8.projectName ? `Project: ${t8.projectName}
|
|
@@ -120475,11 +120554,13 @@ ${ticketTags2.length > 0 ? `Tags: ${formatTagList(ticketTags2)}
|
|
|
120475
120554
|
}
|
|
120476
120555
|
async function handleGetTicketById(input) {
|
|
120477
120556
|
const { id } = input;
|
|
120557
|
+
const resolved = await resolveTicketIdentifier(input.teamId, id);
|
|
120558
|
+
if (!resolved.ok) return resolved.response;
|
|
120478
120559
|
const scope = await resolveTeamScope(input.teamId);
|
|
120479
120560
|
if (!scope.ok) return scope.response;
|
|
120480
120561
|
const { teamIds, projectIds, customerIds } = scope;
|
|
120481
120562
|
const ticketRow = await db.query.tickets.findFirst({
|
|
120482
|
-
where: eq(schema_exports.tickets.id, id),
|
|
120563
|
+
where: eq(schema_exports.tickets.id, resolved.id),
|
|
120483
120564
|
with: {
|
|
120484
120565
|
project: { columns: { id: true, name: true } },
|
|
120485
120566
|
customer: { columns: { id: true, name: true } },
|
|
@@ -120587,6 +120668,7 @@ ${text3.split("\n").map((l4) => ` ${l4}`).join("\n")}`;
|
|
|
120587
120668
|
const tagsLine = ticketTags2.length > 0 ? `Tags: ${formatTagList(ticketTags2)}
|
|
120588
120669
|
` : `Tags: (none)
|
|
120589
120670
|
`;
|
|
120671
|
+
const descriptionText = descriptionPlainText(ticketRow.description);
|
|
120590
120672
|
const content = [
|
|
120591
120673
|
{
|
|
120592
120674
|
type: "text",
|
|
@@ -120599,7 +120681,7 @@ Status: ${ticketRow.status}
|
|
|
120599
120681
|
Priority: ${ticketRow.priority}
|
|
120600
120682
|
Type: ${ticketRow.type}
|
|
120601
120683
|
${ticketRow.dueDate ? `Deadline: ${new Date(ticketRow.dueDate).toLocaleDateString()}
|
|
120602
|
-
` : ""}${
|
|
120684
|
+
` : ""}${descriptionText ? `Description: ${descriptionText}
|
|
120603
120685
|
` : ""}${ticketRow.project?.name ? `Project: ${ticketRow.project.name}
|
|
120604
120686
|
` : ""}${ticketRow.customer?.name ? `Customer: ${ticketRow.customer.name}
|
|
120605
120687
|
` : ""}` + tagsLine + assigneeLine + requesterLine + `Created: ${new Date(ticketRow.createdAt).toLocaleDateString()}
|
|
@@ -120743,7 +120825,7 @@ async function handleCreateTicket(input) {
|
|
|
120743
120825
|
teamId: resolvedTeamId,
|
|
120744
120826
|
ticketNumber,
|
|
120745
120827
|
title,
|
|
120746
|
-
description: description
|
|
120828
|
+
description: ensureTipTapFormat(description),
|
|
120747
120829
|
status,
|
|
120748
120830
|
priority,
|
|
120749
120831
|
type,
|
|
@@ -120786,7 +120868,7 @@ async function handleCreateTicket(input) {
|
|
|
120786
120868
|
text: `\u2705 **Ticket Created Successfully!**
|
|
120787
120869
|
|
|
120788
120870
|
Ticket Number: **${created.ticketNumber}**
|
|
120789
|
-
|
|
120871
|
+
Ticket id: ${created.id}
|
|
120790
120872
|
Title: ${title}
|
|
120791
120873
|
Status: ${status}
|
|
120792
120874
|
Priority: ${priority}
|