viewgate-mcp 1.0.47 → 1.0.49

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.
Files changed (2) hide show
  1. package/dist/index.js +77 -63
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -53,6 +53,7 @@ function createMcpServer(apiKey, personalKey) {
53
53
  step: 0,
54
54
  startedAt: Date.now(),
55
55
  lastActivityAt: Date.now(),
56
+ lockTokens: {},
56
57
  };
57
58
  function resetGuard() {
58
59
  guard.flow = "idle";
@@ -60,6 +61,8 @@ function createMcpServer(apiKey, personalKey) {
60
61
  guard.lastTool = undefined;
61
62
  guard.startedAt = Date.now();
62
63
  guard.lastActivityAt = Date.now();
64
+ guard.lockTokens = {};
65
+ guard.backlogVersion = undefined;
63
66
  }
64
67
  function getToolFlow(toolName) {
65
68
  switch (toolName) {
@@ -68,7 +71,6 @@ function createMcpServer(apiKey, personalKey) {
68
71
  case "mark_ui_component_generated":
69
72
  return "ui_components";
70
73
  case "get_annotations":
71
- case "mark_annotation_ready":
72
74
  case "mark_annotations_as_live":
73
75
  return "annotations";
74
76
  case "get_ui_improvements":
@@ -124,10 +126,13 @@ function createMcpServer(apiKey, personalKey) {
124
126
  break;
125
127
  default:
126
128
  if (guard.flow === "idle") {
127
- throw new Error("TOOL_CALL_BLOCKED: tool not allowed in idle");
129
+ throw new Error(`TOOL_CALL_BLOCKED: tool '${toolName}' not allowed in idle. Please start a workflow with 'get_annotations', 'get_ui_components', or 'get_ui_improvements'.`);
128
130
  }
129
131
  else {
130
- throw new Error("TOOL_CALL_BLOCKED: tool not allowed in active flow");
132
+ const nextTool = guard.flow === "annotations" ? "mark_annotations_as_live" :
133
+ guard.flow === "ui_components" ? "mark_ui_component_generated" :
134
+ guard.flow === "ui_improvements" ? "mark_annotations_as_live" : "the next workflow step";
135
+ throw new Error(`TOOL_CALL_BLOCKED: tool '${toolName}' not allowed while in '${guard.flow}' workflow. You must call '${nextTool}' to complete the current task before starting another.`);
131
136
  }
132
137
  }
133
138
  guard.startedAt = now;
@@ -160,16 +165,12 @@ function createMcpServer(apiKey, personalKey) {
160
165
  // Allow re-fetching at any time during the flow
161
166
  guard.step = 1;
162
167
  }
163
- else if (toolName === "mark_annotation_ready") {
168
+ else if (toolName === "mark_annotations_as_live") {
164
169
  if (guard.step !== 1)
165
170
  throw new Error("TOOL_CALL_BLOCKED: unexpected step");
166
171
  // Stop here and reset flow as per USER_REQUEST (MCP only reaches 'applied')
167
172
  resetGuard();
168
173
  }
169
- else if (toolName === "mark_annotations_as_live") {
170
- // Optional step if the model decides to use it, but no longer part of the required chain
171
- resetGuard();
172
- }
173
174
  else {
174
175
  throw new Error("TOOL_CALL_BLOCKED: tool not allowed in active flow");
175
176
  }
@@ -178,14 +179,11 @@ function createMcpServer(apiKey, personalKey) {
178
179
  if (toolName === "get_ui_improvements") {
179
180
  guard.step = 1;
180
181
  }
181
- else if (toolName === "mark_annotation_ready") {
182
+ else if (toolName === "mark_annotations_as_live") {
182
183
  if (guard.step !== 1)
183
184
  throw new Error("TOOL_CALL_BLOCKED: unexpected step");
184
185
  resetGuard();
185
186
  }
186
- else if (toolName === "mark_annotations_as_live") {
187
- resetGuard();
188
- }
189
187
  else {
190
188
  throw new Error("TOOL_CALL_BLOCKED: tool not allowed in active flow");
191
189
  }
@@ -290,8 +288,8 @@ function createMcpServer(apiKey, personalKey) {
290
288
  },
291
289
  },
292
290
  {
293
- name: "mark_annotation_ready",
294
- description: "Mark as ready/applied. Use internal IDs. IMPORTANT: appliedChanges must be in the project's preferredLanguage (e.g. SPANISH).",
291
+ name: "mark_annotations_as_live",
292
+ description: "Mark tickets (functional or UI/UX) as applied. Use this tool after fixing the code to submit your changes. IMPORTANT: appliedChanges must be in the project's preferredLanguage (e.g. SPANISH).",
295
293
  inputSchema: {
296
294
  type: "object",
297
295
  properties: {
@@ -310,21 +308,6 @@ function createMcpServer(apiKey, personalKey) {
310
308
  required: ["results"]
311
309
  },
312
310
  },
313
- {
314
- name: "mark_annotations_as_live",
315
- description: "Mark as live (ready_for_review). IDs required.",
316
- inputSchema: {
317
- type: "object",
318
- properties: {
319
- ids: {
320
- type: "array",
321
- items: { type: "string" },
322
- description: "Internal IDs."
323
- }
324
- },
325
- required: ["ids"]
326
- },
327
- },
328
311
  {
329
312
  name: "planning",
330
313
  description: "Planning tool for backlog tickets. Fetch tickets or submit analysis. IMPORTANT: aiAnalysis must be in the project's preferredLanguage (e.g. SPANISH).",
@@ -553,7 +536,13 @@ function createMcpServer(apiKey, personalKey) {
553
536
  if (!Array.isArray(rawAnnotations)) {
554
537
  return { content: [{ type: "text", text: "Error: Invalid format from backend" }], isError: true };
555
538
  }
556
- console.error(`[MCP] Received ${rawAnnotations.length} annotations from backend.`);
539
+ // Point: Lease Collection | Store lockTokens in the session guard
540
+ rawAnnotations.forEach((ann) => {
541
+ if (ann._id && ann.lockToken) {
542
+ guard.lockTokens[ann._id] = ann.lockToken;
543
+ }
544
+ });
545
+ console.error(`[MCP] Received ${rawAnnotations.length} annotations. Tokens stored: ${Object.keys(guard.lockTokens).length}`);
557
546
  // Local filtering fallback (for older backend versions or additional client-side safety)
558
547
  if (search) {
559
548
  const lowSearch = search.toLowerCase();
@@ -608,6 +597,12 @@ Endpoints: ${ann.backendEndpoints?.length ? ann.backendEndpoints.join(', ') : 'N
608
597
  Feedback previo: ${ann.corrections && Array.isArray(ann.corrections) && ann.corrections.length > 0 ? ann.corrections[ann.corrections.length - 1].text : 'Ninguno'}
609
598
  Tarea: ${ann.message}
610
599
  Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
600
+
601
+ ### FLUJO DE TRABAJO OBLIGATORIO ###
602
+ 1. Analiza el código y el problema.
603
+ 2. Aplica la solución técnica necesaria.
604
+ 3. AL TERMINAR, DEBES LLAMAR A 'mark_annotations_as_live' con el id '${ann._id}' y un resumen de tus cambios en 'appliedChanges' (en ESPAÑOL).
605
+ 4. El estado final del ticket será 'applied'. NO intentes usar otros estados o herramientas hasta completar este paso.
611
606
  (RECUERDA: Toda tu respuesta, comentarios y análisis DEBEN estar en ESPAÑOL)`
612
607
  };
613
608
  });
@@ -618,10 +613,10 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
618
613
  content: [{ type: "text", text: langHint + JSON.stringify({ preferredLanguage: rawData.preferredLanguage || 'en', annotations: annotationsWithTips }, null, 2) }],
619
614
  };
620
615
  }
621
- case "mark_annotation_ready": {
616
+ case "mark_annotations_as_live": {
622
617
  const args = argsAny;
623
618
  const results = args.results;
624
- console.error(`[mark_annotation_ready] Submitting results for ${results?.length} items`);
619
+ console.error(`[mark_annotations_as_live] Submitting results for ${results?.length} items as applied`);
625
620
  const response = await fetch(`${BACKEND_URL}/api/mcp/annotations/batch-ready`, {
626
621
  method: 'PATCH',
627
622
  headers: {
@@ -631,7 +626,11 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
631
626
  'x-personal-key': personalKey || ''
632
627
  },
633
628
  body: JSON.stringify({
634
- results: results.map((r) => ({ ...r, status: 'applied' }))
629
+ results: results.map((r) => ({
630
+ ...r,
631
+ status: 'applied',
632
+ lockToken: guard.lockTokens[r.id] // Attach the session-locked token
633
+ }))
635
634
  })
636
635
  });
637
636
  if (!response.ok)
@@ -641,27 +640,6 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
641
640
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
642
641
  };
643
642
  }
644
- case "mark_annotations_as_live": {
645
- const args = argsAny;
646
- const ids = args.ids;
647
- console.error(`[mark_annotations_as_live] Marking ${ids?.length} items as live`);
648
- const response = await fetch(`${BACKEND_URL}/api/mcp/annotations/mark-as-live`, {
649
- method: 'POST',
650
- headers: {
651
- 'Content-Type': 'application/json',
652
- 'x-api-key': apiKey,
653
- 'x-mcp-tool-name': toolName,
654
- 'x-personal-key': personalKey || ''
655
- },
656
- body: JSON.stringify({ ids })
657
- });
658
- if (!response.ok)
659
- throw new Error(`Backend responded with ${response.status}`);
660
- const data = (await response.json());
661
- return {
662
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
663
- };
664
- }
665
643
  case "planning": {
666
644
  const args = argsAny;
667
645
  const url = args.results ? `${BACKEND_URL}/api/mcp/annotations/batch-planning` : `${BACKEND_URL}/api/mcp/backlog`;
@@ -669,11 +647,18 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
669
647
  const response = await fetch(url, {
670
648
  method,
671
649
  headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'x-mcp-tool-name': toolName, ...(personalKey ? { 'x-personal-key': personalKey } : {}) },
672
- body: args.results ? JSON.stringify({ results: args.results, force: args.force }) : undefined
650
+ body: args.results ? JSON.stringify({
651
+ results: args.results,
652
+ force: args.force,
653
+ backlogVersion: guard.backlogVersion
654
+ }) : undefined
673
655
  });
674
656
  if (!response.ok)
675
657
  throw new Error(`Backend responded with ${response.status}`);
676
658
  const data = (await response.json());
659
+ // Point: Version Storing
660
+ if (data.backlogVersion)
661
+ guard.backlogVersion = data.backlogVersion;
677
662
  const langHint = data.preferredLanguage === 'es'
678
663
  ? "\n*** [INSTRUCTION: Project is in SPANISH. Provide all comments, appliedChanges, and aiAnalysis in SPANISH ONLY.] ***\n\n\n\n"
679
664
  : (data.preferredLanguage === 'en' ? "\n*** [INSTRUCTION: Provide all comments and analysis in English.] ***\n\n\n\n" : "");
@@ -681,6 +666,7 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
681
666
  }
682
667
  case "get_ui_improvements": {
683
668
  const args = argsAny;
669
+ const agentId = personalKey || 'mcp-agent-ui-' + (apiKey?.slice(-6) || 'generic');
684
670
  const fetchUrl = new URL(`${BACKEND_URL}/api/mcp/ui-improvements`);
685
671
  if (args.limit)
686
672
  fetchUrl.searchParams.append("limit", args.limit.toString());
@@ -690,6 +676,10 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
690
676
  fetchUrl.searchParams.append("key", args.key);
691
677
  if (args.ids)
692
678
  fetchUrl.searchParams.append("ids", args.ids);
679
+ // Add lock and agentId parameters to enable blocking flow
680
+ fetchUrl.searchParams.append("lock", "true");
681
+ fetchUrl.searchParams.append("agentId", agentId);
682
+ console.error(`[get_ui_improvements] Fetching with lock: ${fetchUrl.toString()}`);
693
683
  const response = await fetch(fetchUrl, {
694
684
  headers: {
695
685
  'x-api-key': apiKey,
@@ -697,15 +687,39 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
697
687
  ...(personalKey ? { 'x-personal-key': personalKey } : {})
698
688
  }
699
689
  });
700
- if (!response.ok) {
701
- const errorBody = await response.text();
702
- throw new Error(`Backend responded with ${response.status}: ${errorBody}`);
690
+ const rawData = (await response.json());
691
+ let rawImprovements = Array.isArray(rawData) ? rawData : (rawData?.data || rawData?.improvements || []);
692
+ if (!Array.isArray(rawImprovements)) {
693
+ return { content: [{ type: "text", text: "Error: Invalid format from backend" }], isError: true };
703
694
  }
704
- const data = (await response.json());
705
- const langHint = data.preferredLanguage === 'es'
706
- ? "\n*** [INSTRUCTION: Project is in SPANISH. Provide all CSS/Visual changes in SPANISH comments if requested.] ***\n\n\n\n"
707
- : "";
708
- return { content: [{ type: "text", text: langHint + JSON.stringify(data, null, 2) }] };
695
+ // Point: Lease Collection UI | Store lockTokens for UI tickets
696
+ rawImprovements.forEach((ann) => {
697
+ if (ann._id && ann.lockToken) {
698
+ guard.lockTokens[ann._id] = ann.lockToken;
699
+ }
700
+ });
701
+ const improvementsWithTips = rawImprovements.map((ann) => {
702
+ return {
703
+ ...ann,
704
+ _ia_fix_instruction: `### INSTRUCCIONES PARA LA IA (MEJORA UI - IMPORTANTE) ###
705
+ A continuación se presenta un ticket de MEJORA VISUAL/UX que requiere tu atención.
706
+ DEBES RESPONDER EN ESPAÑOL.
707
+ Proyecto: ${rawData.name || 'ViewGate'}
708
+ Tarea: ${ann.message}
709
+ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
710
+
711
+ ### FLUJO DE TRABAJO OBLIGATORIO ###
712
+ 1. Analiza el diseño y los estilos CSS actuales.
713
+ 2. Aplica los cambios visuales solicitados (STRICTLY LIMITED TO VISUAL/CSS CHANGES).
714
+ 3. AL TERMINAR, DEBES LLAMAR A 'mark_annotations_as_live' con el id '${ann._id}' y un resumen de tus cambios en 'appliedChanges' (en ESPAÑOL).
715
+ 4. El estado final del ticket será 'applied'. NO intentes usar otros estados o herramientas hasta completar este paso.
716
+ (RECUERDA: Toda tu respuesta, comentarios y análisis DEBEN estar en ESPAÑOL)`
717
+ };
718
+ });
719
+ const langHint = rawData.preferredLanguage === 'es'
720
+ ? "\n*** [INSTRUCTION: Project is in SPANISH. Provide all comments and changes in SPANISH ONLY.] ***\n\n"
721
+ : (rawData.preferredLanguage === 'en' ? "\n*** [INSTRUCTION: Provide all comments and analysis in English.] ***\n\n" : "");
722
+ return { content: [{ type: "text", text: langHint + JSON.stringify({ preferredLanguage: rawData.preferredLanguage || 'en', improvements: improvementsWithTips }, null, 2) }] };
709
723
  }
710
724
  case "sync_endpoints": {
711
725
  const args = argsAny;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viewgate-mcp",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "viewgate-mcp": "./dist/index.js"