@mgsoftwarebv/mcp-server-bridge 2.20.0 → 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 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
- const { count } = await supabase.from("tickets").select("*", { count: "exact", head: true }).eq("team_id", authContext.teamId);
811
- const ticketNumber = `${year}-${String((count || 0) + 1).padStart(3, "0")}`;
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: authContext.teamId,
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: customerId || null,
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: existingEntry, error: entryError } = await supabase.from("agenda_events").select("id, tracked_duration, title, description, start_time").eq("ai_session_id", session.id).eq("status", "draft").single();
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
- if (existingEntry && !entryError) {
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: existingAgendaEntry } = await supabase.from("agenda_events").select("id, tracked_duration").eq("ai_session_id", session.id).eq("status", "draft").single();
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
- const startTime = new Date(now.getTime() - durationSeconds * 1e3);
1693
- const { data: agendaEntry, error: agendaError } = await supabase.from("agenda_events").insert({
1694
- team_id: authContext.teamId,
1695
- user_id: authContext.userId,
1696
- project_id: project?.id || null,
1697
- ticket_id: ticket?.id || null,
1698
- ai_session_id: aiSession?.id || null,
1699
- title: workDescription,
1700
- description: chatContextSummary || workDescription,
1701
- start_time: startTime.toISOString(),
1702
- end_time: now.toISOString(),
1703
- type: "work",
1704
- status: "draft",
1705
- all_day: false,
1706
- is_tracked: true,
1707
- tracked_duration: durationSeconds
1708
- }).select("id, tracked_duration, project_id, ticket_id, ai_session_id").single();
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
- let responseText = `\u23F1\uFE0F **Hours Logged Successfully!**
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",