viewgate-mcp 1.0.32 → 1.0.34

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 +82 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ import fetch from "node-fetch";
21
21
  import cors from "cors";
22
22
  import dotenv from "dotenv";
23
23
  import path from "path";
24
+ import os from "os";
24
25
  import { fileURLToPath } from "url";
25
26
  const __filename = fileURLToPath(import.meta.url);
26
27
  const __dirname = path.dirname(__filename);
@@ -29,6 +30,8 @@ const port = process.env.PORT || 3000;
29
30
  const BACKEND_URL = process.env.BACKEND_URL || "https://view-gate.vercel.app";
30
31
  console.error(`[MCP Config] BACKEND_URL: ${BACKEND_URL}`);
31
32
  console.error(`[MCP Config] API_KEY Prefix: ${process.env.API_KEY?.substring(0, 5)}...`);
33
+ const agentId = `${os.hostname()}-${process.pid}`;
34
+ console.error(`[MCP Identity] Agent ID: ${agentId}`);
32
35
  // Store active sessions for SSE: sessionId -> { server, transport }
33
36
  const sessions = new Map();
34
37
  /**
@@ -53,7 +56,7 @@ function createMcpServer(apiKey, personalKey) {
53
56
  type: "object",
54
57
  properties: {
55
58
  limit: { type: "number", description: "Maximum number of annotations to retrieve (default: 3, automatically increased if specific keys are provided)", default: 3 },
56
- status: { type: "string", description: "Comma-separated list (e.g. 'pending,bug_fixing'). Use 'all' for any state.", default: "pending,bug_fixing" },
59
+ status: { type: "string", description: "Comma-separated list (e.g. 'pending,bug_fixing'). Only 'pending' and 'bug_fixing' are allowed for MCP resolution flow.", default: "pending,bug_fixing" },
57
60
  search: { type: "string", description: "Search term to filter by message or file." },
58
61
  key: { type: "string", description: "Human key (e.g. VG-XXXX) or comma-separated keys to find specific annotations." },
59
62
  keys: { type: "array", items: { type: "string" }, description: "List of human keys (e.g. VG-XXXX) to find specific annotations." },
@@ -82,6 +85,21 @@ function createMcpServer(apiKey, personalKey) {
82
85
  required: ["results"]
83
86
  },
84
87
  },
88
+ {
89
+ name: "mark_annotations_as_live",
90
+ description: "Transitions annotations from 'applied' (local fix) to 'ready_for_review' (live on server). Call this tool ONLY after you have successfully deployed/uploaded your code changes.",
91
+ inputSchema: {
92
+ type: "object",
93
+ properties: {
94
+ ids: {
95
+ type: "array",
96
+ items: { type: "string" },
97
+ description: "List of internal database IDs to mark as live."
98
+ }
99
+ },
100
+ required: ["ids"]
101
+ },
102
+ },
85
103
  {
86
104
  name: "planning",
87
105
  description: "Planning tool for backlog tickets. Fetch tickets or submit analysis.",
@@ -171,29 +189,36 @@ function createMcpServer(apiKey, personalKey) {
171
189
  case "get_annotations": {
172
190
  const args = request.params.arguments;
173
191
  const limit = args.limit || 3;
174
- const statuses = args.status || 'pending,bug_fixing';
192
+ // Strictly enforce allowed statuses: pending and bug_fixing
193
+ const allowedStatuses = ['pending', 'bug_fixing'];
194
+ const requestedStatuses = (args.status || 'pending,bug_fixing').split(',').map(s => s.trim()).filter(s => allowedStatuses.includes(s));
195
+ const statuses = requestedStatuses.length > 0 ? requestedStatuses.join(',') : 'pending,bug_fixing';
175
196
  const search = args.search || '';
176
197
  // Normalize keys and ids
177
198
  let keysToFetch = [];
178
199
  if (args.key)
179
200
  keysToFetch = keysToFetch.concat(args.key.split(',').map(s => s.trim()));
180
- if (args.keys)
181
- keysToFetch = keysToFetch.concat(args.keys);
182
- const combinedKey = keysToFetch.filter(k => k).join(',');
183
- const idsToFetch = args.ids || '';
201
+ const key = args.key;
202
+ const keys = args.keys;
203
+ const ids = args.ids;
184
204
  const agentId = personalKey || 'mcp-agent-' + (apiKey?.slice(-6) || 'generic');
185
- let fetchUrl = `${BACKEND_URL}/api/mcp/annotations?lock=true&agentId=${encodeURIComponent(agentId)}`;
186
- if (statuses && statuses !== 'all')
187
- fetchUrl += `&status=${statuses}`;
205
+ const fetchUrl = new URL(`${BACKEND_URL}/api/mcp/annotations`);
188
206
  if (limit)
189
- fetchUrl += `&limit=${limit}`;
207
+ fetchUrl.searchParams.append("limit", limit.toString());
208
+ if (statuses)
209
+ fetchUrl.searchParams.append("status", statuses);
190
210
  if (search)
191
- fetchUrl += `&search=${encodeURIComponent(search)}`;
192
- if (combinedKey)
193
- fetchUrl += `&key=${encodeURIComponent(combinedKey)}`;
194
- if (idsToFetch)
195
- fetchUrl += `&ids=${encodeURIComponent(idsToFetch)}`;
196
- console.error(`[MCP] Fetching: ${fetchUrl}`);
211
+ fetchUrl.searchParams.append("search", search);
212
+ if (key)
213
+ fetchUrl.searchParams.append("key", key);
214
+ if (ids)
215
+ fetchUrl.searchParams.append("ids", ids);
216
+ if (keys && keys.length > 0)
217
+ fetchUrl.searchParams.append("keys", keys.join(","));
218
+ // Add lock and agentId parameters
219
+ fetchUrl.searchParams.append("lock", "true");
220
+ fetchUrl.searchParams.append("agentId", agentId);
221
+ console.error(`[get_annotations] Fetching: ${fetchUrl.toString()}`);
197
222
  const response = await fetch(fetchUrl, {
198
223
  headers: {
199
224
  'x-api-key': apiKey,
@@ -217,10 +242,11 @@ function createMcpServer(apiKey, personalKey) {
217
242
  (ann.reference?.source && ann.reference.source.toLowerCase().includes(lowSearch)) ||
218
243
  (ann.key && ann.key.toLowerCase().includes(lowSearch)));
219
244
  }
245
+ const combinedKey = key || (keys && keys.join(','));
220
246
  if (combinedKey) {
221
247
  // Normalize keyList for case-insensitive matching and handle VG- prefix
222
- const keyList = combinedKey.split(',').map(k => k.trim().toUpperCase());
223
- const baseKeys = keyList.map(k => k.startsWith('VG-') ? k.substring(3) : k);
248
+ const keyList = combinedKey.split(',').map((k) => k.trim().toUpperCase());
249
+ const baseKeys = keyList.map((k) => k.startsWith('VG-') ? k.substring(3) : k);
224
250
  rawAnnotations = rawAnnotations.filter((ann) => {
225
251
  if (!ann.key)
226
252
  return keyList.includes(ann._id);
@@ -232,8 +258,8 @@ function createMcpServer(apiKey, personalKey) {
232
258
  });
233
259
  console.error(`[MCP] Filtered with keys. Remaining: ${rawAnnotations.length}`);
234
260
  }
235
- if (idsToFetch) {
236
- const idList = idsToFetch.split(',').map(i => i.trim());
261
+ if (ids) {
262
+ const idList = ids.split(',').map((i) => i.trim());
237
263
  rawAnnotations = rawAnnotations.filter((ann) => idList.includes(ann._id));
238
264
  console.error(`[MCP] Filtered with IDs. Remaining: ${rawAnnotations.length}`);
239
265
  }
@@ -293,19 +319,45 @@ Instruction: ${ann.message}`
293
319
  }
294
320
  case "mark_annotation_ready": {
295
321
  const args = request.params.arguments;
296
- console.error(`[MCP] Marking ${args.results.length} annotations as ready.`);
297
- console.error(`[MCP] Batch ready update: ${BACKEND_URL}/api/mcp/annotations/batch-ready`);
322
+ const results = args.results;
323
+ console.error(`[mark_annotation_ready] Submitting results for ${results?.length} items`);
298
324
  const response = await fetch(`${BACKEND_URL}/api/mcp/annotations/batch-ready`, {
299
325
  method: 'PATCH',
300
- headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, ...(personalKey ? { 'x-personal-key': personalKey } : {}) },
301
- body: JSON.stringify({ results: args.results })
326
+ headers: {
327
+ 'Content-Type': 'application/json',
328
+ 'x-api-key': apiKey,
329
+ 'x-personal-key': personalKey || ''
330
+ },
331
+ body: JSON.stringify({
332
+ results: results.map((r) => ({ ...r, status: 'applied' }))
333
+ })
302
334
  });
303
- if (!response.ok) {
304
- const errorBody = await response.text();
305
- throw new Error(`Backend responded with ${response.status}: ${errorBody}`);
306
- }
307
- const result = await response.json();
308
- return { content: [{ type: "text", text: `Processed: ${JSON.stringify(result, null, 2)}` }] };
335
+ if (!response.ok)
336
+ throw new Error(`Backend responded with ${response.status}`);
337
+ const data = await response.json();
338
+ return {
339
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
340
+ };
341
+ }
342
+ case "mark_annotations_as_live": {
343
+ const args = request.params.arguments;
344
+ const ids = args.ids;
345
+ console.error(`[mark_annotations_as_live] Marking ${ids?.length} items as live`);
346
+ const response = await fetch(`${BACKEND_URL}/api/mcp/annotations/mark-as-live`, {
347
+ method: 'POST',
348
+ headers: {
349
+ 'Content-Type': 'application/json',
350
+ 'x-api-key': apiKey,
351
+ 'x-personal-key': personalKey || ''
352
+ },
353
+ body: JSON.stringify({ ids })
354
+ });
355
+ if (!response.ok)
356
+ throw new Error(`Backend responded with ${response.status}`);
357
+ const data = await response.json();
358
+ return {
359
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
360
+ };
309
361
  }
310
362
  case "planning": {
311
363
  const args = request.params.arguments;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viewgate-mcp",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "viewgate-mcp": "./dist/index.js"