@mgsoftwarebv/mcp-server-bridge 2.19.1 → 2.20.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 +138 -31
- package/dist/index.js.map +1 -1
- package/package.json +48 -48
package/dist/index.js
CHANGED
|
@@ -207,7 +207,7 @@ var TOOLS = [
|
|
|
207
207
|
priority: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
208
208
|
projectId: { type: "string" },
|
|
209
209
|
customerId: { type: "string" },
|
|
210
|
-
q: { type: "string", description: "Search query for title or description" },
|
|
210
|
+
q: { type: "string", description: "Search query for ticket number, title, or description" },
|
|
211
211
|
pageSize: { type: "number", default: 20, maximum: 100 }
|
|
212
212
|
},
|
|
213
213
|
required: []
|
|
@@ -647,7 +647,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
647
647
|
if (priority) query = query.eq("priority", priority);
|
|
648
648
|
if (projectId) query = query.eq("project_id", projectId);
|
|
649
649
|
if (customerId) query = query.eq("customer_id", customerId);
|
|
650
|
-
if (q) query = query.or(`title.ilike.%${q}%,description.ilike.%${q}%`);
|
|
650
|
+
if (q) query = query.or(`ticket_number.ilike.%${q}%,title.ilike.%${q}%,description.ilike.%${q}%`);
|
|
651
651
|
const { data, error } = await query.order("created_at", { ascending: false });
|
|
652
652
|
if (error) throw error;
|
|
653
653
|
return {
|
|
@@ -807,10 +807,48 @@ ${attachments && attachments.length > 0 ? `
|
|
|
807
807
|
case "create-ticket": {
|
|
808
808
|
const { title, description, status = "open", priority = "medium", type = "task", projectId, customerId } = args2;
|
|
809
809
|
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
810
|
-
|
|
811
|
-
|
|
810
|
+
let resolvedTeamId = authContext.teamId;
|
|
811
|
+
let resolvedCustomerId = customerId;
|
|
812
|
+
let projectAbbreviation = "";
|
|
813
|
+
if (projectId) {
|
|
814
|
+
const { data: project } = await supabase.from("projects").select("name, team_id, customer_id").eq("id", projectId).single();
|
|
815
|
+
if (project) {
|
|
816
|
+
if (project.team_id) {
|
|
817
|
+
resolvedTeamId = project.team_id;
|
|
818
|
+
}
|
|
819
|
+
if (!resolvedCustomerId && project.customer_id) {
|
|
820
|
+
resolvedCustomerId = project.customer_id;
|
|
821
|
+
}
|
|
822
|
+
if (project.name) {
|
|
823
|
+
const name2 = project.name.toUpperCase().replace(/[^A-Z0-9\s]/g, "");
|
|
824
|
+
const words = name2.split(/\s+/).filter(Boolean);
|
|
825
|
+
if (words.length >= 2) {
|
|
826
|
+
projectAbbreviation = words.slice(0, 2).map((w) => w.substring(0, 3)).join("").substring(0, 5);
|
|
827
|
+
} else if (words.length === 1 && words[0]) {
|
|
828
|
+
projectAbbreviation = words[0].substring(0, 5);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
let ticketNumber;
|
|
834
|
+
if (projectId && projectAbbreviation) {
|
|
835
|
+
const pattern = `${year}-${projectAbbreviation}-%`;
|
|
836
|
+
const { data: existingTickets } = await supabase.from("tickets").select("ticket_number").eq("project_id", projectId).like("ticket_number", pattern).order("ticket_number", { ascending: false }).limit(1);
|
|
837
|
+
let nextSequence = 1;
|
|
838
|
+
if (existingTickets && existingTickets.length > 0 && existingTickets[0]?.ticket_number) {
|
|
839
|
+
const parts = existingTickets[0].ticket_number.split("-");
|
|
840
|
+
if (parts.length === 3) {
|
|
841
|
+
const lastSeq = parseInt(parts[2], 10);
|
|
842
|
+
if (!isNaN(lastSeq)) nextSequence = lastSeq + 1;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
ticketNumber = `${year}-${projectAbbreviation}-${String(nextSequence).padStart(3, "0")}`;
|
|
846
|
+
} else {
|
|
847
|
+
const { count } = await supabase.from("tickets").select("*", { count: "exact", head: true }).eq("team_id", resolvedTeamId);
|
|
848
|
+
ticketNumber = `${year}-${String((count || 0) + 1).padStart(3, "0")}`;
|
|
849
|
+
}
|
|
812
850
|
const { data, error } = await supabase.from("tickets").insert({
|
|
813
|
-
team_id:
|
|
851
|
+
team_id: resolvedTeamId,
|
|
814
852
|
ticket_number: ticketNumber,
|
|
815
853
|
title,
|
|
816
854
|
description,
|
|
@@ -818,7 +856,7 @@ ${attachments && attachments.length > 0 ? `
|
|
|
818
856
|
priority,
|
|
819
857
|
type,
|
|
820
858
|
project_id: projectId || null,
|
|
821
|
-
customer_id:
|
|
859
|
+
customer_id: resolvedCustomerId || null,
|
|
822
860
|
requester_id: authContext.userId
|
|
823
861
|
}).select().single();
|
|
824
862
|
if (error) throw error;
|
|
@@ -1092,10 +1130,21 @@ ${cursorSessionId ? `\u{1F517} Cursor Session: ${cursorSessionId}
|
|
|
1092
1130
|
efficiency_score: currentEfficiency.toFixed(2),
|
|
1093
1131
|
actual_time_minutes: totalMinutesElapsed
|
|
1094
1132
|
}).eq("id", session.id);
|
|
1095
|
-
const { data:
|
|
1133
|
+
const { data: existingEntries, error: entryError } = await supabase.from("agenda_events").select("id, tracked_duration, title, description, start_time").eq("ai_session_id", session.id).eq("status", "draft").order("created_at", { ascending: false });
|
|
1096
1134
|
let trackerAction = "";
|
|
1097
1135
|
let trackerDetails = "";
|
|
1098
|
-
|
|
1136
|
+
const existingEntry = existingEntries && existingEntries.length > 0 ? existingEntries[0] : null;
|
|
1137
|
+
if (existingEntries && existingEntries.length > 1) {
|
|
1138
|
+
const totalExistingDuration = existingEntries.reduce((sum, entry) => sum + (entry.tracked_duration || 0), 0);
|
|
1139
|
+
const duplicateIds = existingEntries.slice(1).map((e) => e.id);
|
|
1140
|
+
await supabase.from("agenda_events").delete().in("id", duplicateIds);
|
|
1141
|
+
if (totalExistingDuration > (existingEntry?.tracked_duration || 0)) {
|
|
1142
|
+
await supabase.from("agenda_events").update({ tracked_duration: totalExistingDuration }).eq("id", existingEntry.id);
|
|
1143
|
+
existingEntry.tracked_duration = totalExistingDuration;
|
|
1144
|
+
}
|
|
1145
|
+
trackerAction = `Consolidated ${existingEntries.length} duplicate entries`;
|
|
1146
|
+
}
|
|
1147
|
+
if (existingEntry) {
|
|
1099
1148
|
const newDuration = (existingEntry.tracked_duration || 0) + roundedFollowUpMinutes * 60;
|
|
1100
1149
|
await supabase.from("agenda_events").update({
|
|
1101
1150
|
tracked_duration: newDuration,
|
|
@@ -1103,7 +1152,7 @@ ${cursorSessionId ? `\u{1F517} Cursor Session: ${cursorSessionId}
|
|
|
1103
1152
|
title: workDescription,
|
|
1104
1153
|
description: workDescription
|
|
1105
1154
|
}).eq("id", existingEntry.id);
|
|
1106
|
-
trackerAction = "Updated existing tracker";
|
|
1155
|
+
trackerAction = trackerAction || "Updated existing tracker";
|
|
1107
1156
|
trackerDetails = ` \u2022 Total tracked time: ${Math.round(newDuration / 60)} minutes (+${roundedFollowUpMinutes} min)
|
|
1108
1157
|
\u2022 Description: ${workDescription}
|
|
1109
1158
|
`;
|
|
@@ -1526,10 +1575,17 @@ ${workDescription}`;
|
|
|
1526
1575
|
const estimatedMinutes = session.ai_time_estimate_minutes || timeSpentMinutes;
|
|
1527
1576
|
const sessionStart = new Date(session.created_at);
|
|
1528
1577
|
const estimatedEnd = new Date(sessionStart.getTime() + estimatedMinutes * 6e4);
|
|
1529
|
-
const { data:
|
|
1578
|
+
const { data: existingAgendaEntries } = await supabase.from("agenda_events").select("id, tracked_duration").eq("ai_session_id", session.id).eq("status", "draft").order("created_at", { ascending: false });
|
|
1530
1579
|
let agendaEvent = null;
|
|
1531
1580
|
let agendaError = null;
|
|
1532
1581
|
let wasUpdated = false;
|
|
1582
|
+
let consolidatedCount = 0;
|
|
1583
|
+
const existingAgendaEntry = existingAgendaEntries && existingAgendaEntries.length > 0 ? existingAgendaEntries[0] : null;
|
|
1584
|
+
if (existingAgendaEntries && existingAgendaEntries.length > 1) {
|
|
1585
|
+
const duplicateIds = existingAgendaEntries.slice(1).map((e) => e.id);
|
|
1586
|
+
await supabase.from("agenda_events").delete().in("id", duplicateIds);
|
|
1587
|
+
consolidatedCount = existingAgendaEntries.length - 1;
|
|
1588
|
+
}
|
|
1533
1589
|
if (existingAgendaEntry) {
|
|
1534
1590
|
const { data: updated, error: updateError } = await supabase.from("agenda_events").update({
|
|
1535
1591
|
title: ticketInfo?.title || "Development Work",
|
|
@@ -1569,6 +1625,9 @@ ${workDescription}`;
|
|
|
1569
1625
|
if (agendaError) {
|
|
1570
1626
|
console.error(`\u26A0\uFE0F Failed to ${wasUpdated ? "update" : "create"} agenda event:`, agendaError);
|
|
1571
1627
|
}
|
|
1628
|
+
if (consolidatedCount > 0) {
|
|
1629
|
+
console.log(`\u{1F9F9} Cleaned up ${consolidatedCount} duplicate agenda entries for session ${aiSessionId}`);
|
|
1630
|
+
}
|
|
1572
1631
|
let responseText = `\u{1F389} **AI Session Completed Successfully!**
|
|
1573
1632
|
|
|
1574
1633
|
`;
|
|
@@ -1689,29 +1748,77 @@ ${efficiencyNotes}
|
|
|
1689
1748
|
}
|
|
1690
1749
|
const durationSeconds = Math.round(estimatedHours * 3600);
|
|
1691
1750
|
const now = /* @__PURE__ */ new Date();
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1751
|
+
let agendaEntry = null;
|
|
1752
|
+
let agendaError = null;
|
|
1753
|
+
let wasUpdated = false;
|
|
1754
|
+
let consolidatedCount = 0;
|
|
1755
|
+
if (aiSession?.id || ticket?.id) {
|
|
1756
|
+
let query = supabase.from("agenda_events").select("id, tracked_duration, project_id, ticket_id, ai_session_id").eq("status", "draft").eq("user_id", authContext.userId).order("created_at", { ascending: false });
|
|
1757
|
+
if (aiSession?.id) {
|
|
1758
|
+
query = query.eq("ai_session_id", aiSession.id);
|
|
1759
|
+
} else if (ticket?.id) {
|
|
1760
|
+
query = query.eq("ticket_id", ticket.id);
|
|
1761
|
+
}
|
|
1762
|
+
const { data: existingEntries } = await query;
|
|
1763
|
+
if (existingEntries && existingEntries.length > 0) {
|
|
1764
|
+
const existingEntry = existingEntries[0];
|
|
1765
|
+
if (existingEntries.length > 1) {
|
|
1766
|
+
const duplicateIds = existingEntries.slice(1).map((e) => e.id);
|
|
1767
|
+
await supabase.from("agenda_events").delete().in("id", duplicateIds);
|
|
1768
|
+
consolidatedCount = existingEntries.length - 1;
|
|
1769
|
+
}
|
|
1770
|
+
const newDuration = (existingEntry.tracked_duration || 0) + durationSeconds;
|
|
1771
|
+
const { data: updated, error: updateError } = await supabase.from("agenda_events").update({
|
|
1772
|
+
tracked_duration: newDuration,
|
|
1773
|
+
end_time: now.toISOString(),
|
|
1774
|
+
title: workDescription,
|
|
1775
|
+
description: chatContextSummary || workDescription,
|
|
1776
|
+
project_id: project?.id || existingEntry.project_id
|
|
1777
|
+
}).eq("id", existingEntry.id).select("id, tracked_duration, project_id, ticket_id, ai_session_id").single();
|
|
1778
|
+
agendaEntry = updated;
|
|
1779
|
+
agendaError = updateError;
|
|
1780
|
+
wasUpdated = true;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
if (!agendaEntry) {
|
|
1784
|
+
const startTime = new Date(now.getTime() - durationSeconds * 1e3);
|
|
1785
|
+
const { data: created, error: createError } = await supabase.from("agenda_events").insert({
|
|
1786
|
+
team_id: authContext.teamId,
|
|
1787
|
+
user_id: authContext.userId,
|
|
1788
|
+
project_id: project?.id || null,
|
|
1789
|
+
ticket_id: ticket?.id || null,
|
|
1790
|
+
ai_session_id: aiSession?.id || null,
|
|
1791
|
+
title: workDescription,
|
|
1792
|
+
description: chatContextSummary || workDescription,
|
|
1793
|
+
start_time: startTime.toISOString(),
|
|
1794
|
+
end_time: now.toISOString(),
|
|
1795
|
+
type: "work",
|
|
1796
|
+
status: "draft",
|
|
1797
|
+
all_day: false,
|
|
1798
|
+
is_tracked: true,
|
|
1799
|
+
tracked_duration: durationSeconds
|
|
1800
|
+
}).select("id, tracked_duration, project_id, ticket_id, ai_session_id").single();
|
|
1801
|
+
agendaEntry = created;
|
|
1802
|
+
agendaError = createError;
|
|
1803
|
+
}
|
|
1709
1804
|
if (agendaError || !agendaEntry) {
|
|
1710
|
-
throw new Error(`Failed to create time entry: ${agendaError?.message || "Unknown error"}`);
|
|
1805
|
+
throw new Error(`Failed to ${wasUpdated ? "update" : "create"} time entry: ${agendaError?.message || "Unknown error"}`);
|
|
1806
|
+
}
|
|
1807
|
+
let responseText = `\u23F1\uFE0F **Hours ${wasUpdated ? "Added to Existing Entry" : "Logged Successfully"}!**
|
|
1808
|
+
|
|
1809
|
+
`;
|
|
1810
|
+
if (wasUpdated) {
|
|
1811
|
+
responseText += `\u{1F504} **Updated existing draft entry** (avoiding duplicates)
|
|
1812
|
+
`;
|
|
1813
|
+
responseText += ` \u2022 New total: ${Math.round((agendaEntry.tracked_duration || 0) / 3600 * 10) / 10}h
|
|
1814
|
+
|
|
1815
|
+
`;
|
|
1711
1816
|
}
|
|
1712
|
-
|
|
1817
|
+
if (consolidatedCount > 0) {
|
|
1818
|
+
responseText += `\u{1F9F9} **Cleaned up ${consolidatedCount} duplicate entries**
|
|
1713
1819
|
|
|
1714
1820
|
`;
|
|
1821
|
+
}
|
|
1715
1822
|
responseText += `\u{1F4CB} **Entry Details:**
|
|
1716
1823
|
`;
|
|
1717
1824
|
if (project) {
|
|
@@ -1731,7 +1838,7 @@ ${efficiencyNotes}
|
|
|
1731
1838
|
}
|
|
1732
1839
|
responseText += ` \u2022 Description: ${workDescription}
|
|
1733
1840
|
`;
|
|
1734
|
-
responseText += ` \u2022 Estimated Hours: ${estimatedHours}h (${Math.floor(estimatedHours)}h ${Math.round(estimatedHours % 1 * 60)}m)
|
|
1841
|
+
responseText += ` \u2022 ${wasUpdated ? "Added" : "Estimated"} Hours: ${estimatedHours}h (${Math.floor(estimatedHours)}h ${Math.round(estimatedHours % 1 * 60)}m)
|
|
1735
1842
|
`;
|
|
1736
1843
|
responseText += ` \u2022 Status: DRAFT (not billed yet)
|
|
1737
1844
|
`;
|
|
@@ -1745,7 +1852,7 @@ ${efficiencyNotes}
|
|
|
1745
1852
|
|
|
1746
1853
|
`;
|
|
1747
1854
|
}
|
|
1748
|
-
responseText += `\u2705 Time entry created and ready for review in the agenda!`;
|
|
1855
|
+
responseText += `\u2705 Time entry ${wasUpdated ? "updated" : "created"} and ready for review in the agenda!`;
|
|
1749
1856
|
return {
|
|
1750
1857
|
content: [{
|
|
1751
1858
|
type: "text",
|