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.
- package/dist/index.js +82 -30
- 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').
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
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
|
-
|
|
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
|
|
207
|
+
fetchUrl.searchParams.append("limit", limit.toString());
|
|
208
|
+
if (statuses)
|
|
209
|
+
fetchUrl.searchParams.append("status", statuses);
|
|
190
210
|
if (search)
|
|
191
|
-
fetchUrl
|
|
192
|
-
if (
|
|
193
|
-
fetchUrl
|
|
194
|
-
if (
|
|
195
|
-
fetchUrl
|
|
196
|
-
|
|
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 (
|
|
236
|
-
const idList =
|
|
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
|
-
|
|
297
|
-
console.error(`[
|
|
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: {
|
|
301
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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;
|