@saiteja1123/mcp-server 1.1.3 → 1.1.5
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/package.json +59 -54
- package/src/api-scan.mjs +362 -93
- package/src/cli.js +713 -322
- package/src/deep-scan/contracts.js +201 -0
- package/src/deep-scan/deterministic-scan.js +337 -0
- package/src/deep-scan/index.js +109 -0
- package/src/deep-scan/project-map.js +507 -0
- package/src/deep-scan/ralph-accept.js +510 -0
- package/src/deep-scan/ralph-compare.js +498 -0
- package/src/deep-scan/ralph-tasks.js +598 -0
- package/src/deep-scan/ralph-track.js +548 -0
- package/src/deep-scan/registry.js +159 -0
- package/src/deep-scan/runtime.js +275 -0
- package/src/deep-scan/sample-steppers.js +128 -0
- package/src/deep-scan/sourceSafe.js +73 -0
- package/src/deep-scan/status.js +70 -0
- package/src/deep-scan/store.js +57 -0
- package/src/deep-scan/test-plan.js +760 -0
- package/src/index.js +6 -6
- package/src/lock.mjs +55 -14
- package/src/middleware/governance.js +135 -0
- package/src/orchestrator/runScan.js +211 -0
- package/src/project-bindings.mjs +215 -0
- package/src/rule-engine/index.js +3 -2
- package/src/rule-engine/localScan.js +41 -12
- package/src/rule-engine/metadata.js +20 -0
- package/src/rule-engine/prompt.js +6 -5
- package/src/rule-engine/rules.js +71 -43
- package/src/rule-engine/score.js +5 -4
- package/src/security/pathGuard.js +170 -0
- package/src/selftest.js +2473 -0
- package/src/server.js +161 -145
- package/src/tools/deepScan.js +286 -0
- package/src/tools/localScan.js +85 -0
- package/src/tools/projects.js +124 -0
- package/src/tools/scanFile.js +131 -0
package/src/server.js
CHANGED
|
@@ -10,29 +10,44 @@ import {
|
|
|
10
10
|
} from './rule-engine/index.js';
|
|
11
11
|
import {
|
|
12
12
|
DEFAULT_INCLUDE, DEFAULT_EXCLUDE,
|
|
13
|
-
|
|
13
|
+
normalizeRootPath, ensureDirectory,
|
|
14
14
|
gatherRepoScan, readGitignoreChecks,
|
|
15
15
|
detectWorkspacePath, isHomePath,
|
|
16
16
|
} from './repo-scan.mjs';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { diagnosticLock } from './lock.mjs';
|
|
18
|
+
import { persistRepoScanLog } from './api-scan.mjs';
|
|
19
|
+
import { createBindingGuard, formatGuardError } from './security/pathGuard.js';
|
|
20
|
+
import { registerLocalScanTool } from './tools/localScan.js';
|
|
21
|
+
import { registerScanFileTool } from './tools/scanFile.js';
|
|
22
|
+
import { registerDeepScanTools } from './tools/deepScan.js';
|
|
23
|
+
import { registerProjectTools } from './tools/projects.js';
|
|
19
24
|
|
|
20
25
|
const require = createRequire(import.meta.url);
|
|
21
26
|
const mcpPkg = require('../package.json');
|
|
22
27
|
|
|
23
28
|
const INSTALL_TOKEN = process.env.VIBESECUR_INSTALL_TOKEN || null;
|
|
29
|
+
const AUTH_TOKEN = process.env.VIBESECUR_AUTH_TOKEN || process.env.VIBESECUR_TOKEN || null;
|
|
24
30
|
const BOUND_ROOT = process.env.VIBESECUR_BOUND_ROOT
|
|
25
31
|
? path.resolve(process.env.VIBESECUR_BOUND_ROOT)
|
|
26
32
|
: null;
|
|
33
|
+
const API_BASE = process.env.VIBESECUR_API_BASE || process.env.VIBESECUR_API_URL || '';
|
|
34
|
+
const UNIVERSAL_MODE = !!AUTH_TOKEN && !BOUND_ROOT;
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
process.
|
|
30
|
-
|
|
31
|
-
'[vibesecur] Run: vibesecur-mcp bind <folder> then vibesecur-mcp config <folder>\n' +
|
|
32
|
-
'[vibesecur] Scans will be restricted to process.cwd() as fallback.\n',
|
|
33
|
-
);
|
|
36
|
+
function debugLog(message, payload) {
|
|
37
|
+
if (process.env.VIBESECUR_DEBUG !== '1') return;
|
|
38
|
+
process.stderr.write(`[vibesecur-debug] ${message}: ${JSON.stringify(payload)}\n`);
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
const guardPath = createBindingGuard({
|
|
42
|
+
boundRoot: BOUND_ROOT,
|
|
43
|
+
installToken: INSTALL_TOKEN,
|
|
44
|
+
authToken: AUTH_TOKEN,
|
|
45
|
+
apiBase: API_BASE,
|
|
46
|
+
universalMode: UNIVERSAL_MODE,
|
|
47
|
+
normalizePath: normalizeRootPath,
|
|
48
|
+
debugLog,
|
|
49
|
+
});
|
|
50
|
+
|
|
36
51
|
const server = new McpServer({
|
|
37
52
|
name: 'vibesecur-mcp-server',
|
|
38
53
|
version: mcpPkg.version || '2.0.0',
|
|
@@ -40,52 +55,7 @@ const server = new McpServer({
|
|
|
40
55
|
|
|
41
56
|
const SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
const target = normalizeRootPath(requestedPath);
|
|
45
|
-
if (!BOUND_ROOT) {
|
|
46
|
-
const cwd = path.resolve(process.cwd());
|
|
47
|
-
if (!target.startsWith(cwd + path.sep) && target !== cwd) {
|
|
48
|
-
return {
|
|
49
|
-
ok: false,
|
|
50
|
-
httpStatus: 403,
|
|
51
|
-
code: 'NO_LOCK_OUT_OF_CWD',
|
|
52
|
-
message:
|
|
53
|
-
`No lock configured and "${target}" is outside process.cwd() "${cwd}". ` +
|
|
54
|
-
'Run vibesecur-mcp bind <folder> and add VIBESECUR_BOUND_ROOT to your MCP config.',
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return { ok: true, resolvedRoot: target };
|
|
58
|
-
}
|
|
59
|
-
if (!target.startsWith(BOUND_ROOT + path.sep) && target !== BOUND_ROOT) {
|
|
60
|
-
return {
|
|
61
|
-
ok: false,
|
|
62
|
-
httpStatus: 403,
|
|
63
|
-
code: 'OUT_OF_FOLDER',
|
|
64
|
-
message:
|
|
65
|
-
`Path "${target}" is outside the locked project folder "${BOUND_ROOT}". ` +
|
|
66
|
-
'Vibesecur MCP is bound to one folder per install. ' +
|
|
67
|
-
'To scan a different folder, run "vibesecur-mcp rebind <new-folder>".',
|
|
68
|
-
rebindHint: `vibesecur-mcp rebind ${target}`,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
const result = await validateScanPath(target, INSTALL_TOKEN);
|
|
72
|
-
if (!result.ok) return result;
|
|
73
|
-
return { ok: true, resolvedRoot: target, lock: result.lock };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function guardError(guard) {
|
|
77
|
-
const status = guard.httpStatus ? ` (${guard.httpStatus})` : '';
|
|
78
|
-
const text = JSON.stringify({
|
|
79
|
-
error: guard.code || 'SCAN_BLOCKED',
|
|
80
|
-
message: guard.message,
|
|
81
|
-
rebindHint: guard.rebindHint || null,
|
|
82
|
-
docs: 'https://vibesecur.com/docs/mcp-setup',
|
|
83
|
-
}, null, 2);
|
|
84
|
-
return {
|
|
85
|
-
content: [{ type: 'text', text: `Security Lock Error${status}:\n${text}` }],
|
|
86
|
-
isError: true,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
58
|
+
const guardError = formatGuardError;
|
|
89
59
|
|
|
90
60
|
function buildScanMeta(resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedLen, scannedLen) {
|
|
91
61
|
return {
|
|
@@ -96,7 +66,8 @@ function buildScanMeta(resolvedRoot, includeGlobs, excludeGlobs, maxFiles, match
|
|
|
96
66
|
matchedFiles: matchedLen,
|
|
97
67
|
scannedFiles: scannedLen,
|
|
98
68
|
cappedByMaxFiles: matchedLen > maxFiles,
|
|
99
|
-
boundRoot: BOUND_ROOT || 'unconfigured',
|
|
69
|
+
boundRoot: BOUND_ROOT || (UNIVERSAL_MODE ? 'account-wide' : 'unconfigured'),
|
|
70
|
+
mode: UNIVERSAL_MODE ? 'universal' : (BOUND_ROOT ? 'single-folder' : 'unconfigured'),
|
|
100
71
|
};
|
|
101
72
|
}
|
|
102
73
|
|
|
@@ -108,9 +79,40 @@ function humanRepoSummary(meta, agg) {
|
|
|
108
79
|
return parts.join(' ');
|
|
109
80
|
}
|
|
110
81
|
|
|
82
|
+
async function syncRepoScanToDashboard({ aggregate, findings, projectRoot, guard }) {
|
|
83
|
+
const installToken = guard.installToken || INSTALL_TOKEN;
|
|
84
|
+
const lockedRootHash = guard.lockedRootHash || guard.lock?.lockedRootHash || guard.lock?.rootHash;
|
|
85
|
+
if (!installToken || !lockedRootHash) {
|
|
86
|
+
return { ok: false, reason: 'missing install token or locked root hash' };
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const logRes = await persistRepoScanLog({
|
|
90
|
+
aggregate,
|
|
91
|
+
findings,
|
|
92
|
+
projectRoot,
|
|
93
|
+
installToken,
|
|
94
|
+
lockedRootHash,
|
|
95
|
+
});
|
|
96
|
+
if (logRes?.ok && logRes.json?.success) {
|
|
97
|
+
return { ok: true, scanId: logRes.json?.data?.scanId || null };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: logRes?.reason || logRes?.error || logRes?.json?.error || `status ${logRes?.status || 'unknown'}`,
|
|
102
|
+
};
|
|
103
|
+
} catch (err) {
|
|
104
|
+
process.stderr.write(`[vibesecur] scan log failed (non-fatal): ${err.message}\n`);
|
|
105
|
+
return { ok: false, reason: err.message };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
111
109
|
function flattenFindings(fileResults) {
|
|
112
110
|
return fileResults.flatMap((fr) =>
|
|
113
|
-
(fr.result.findings || []).map((f) => ({
|
|
111
|
+
(fr.result.findings || []).map((f) => ({
|
|
112
|
+
...f,
|
|
113
|
+
filePath: fr.filePath,
|
|
114
|
+
snippetPreview: f.snippetPreview || f.snippet || '',
|
|
115
|
+
})),
|
|
114
116
|
);
|
|
115
117
|
}
|
|
116
118
|
|
|
@@ -124,10 +126,11 @@ function pickTopFindings(fileResults, n) {
|
|
|
124
126
|
return flat.slice(0, n).map((f) => ({
|
|
125
127
|
filePath: f.filePath,
|
|
126
128
|
lineNumber: f.lineNumber,
|
|
129
|
+
endLineNumber: f.endLineNumber || f.lineNumber,
|
|
127
130
|
ruleId: f.ruleId,
|
|
128
131
|
ruleName: f.ruleName,
|
|
129
132
|
severity: f.severity,
|
|
130
|
-
snippetPreview: (f.
|
|
133
|
+
snippetPreview: (f.snippetPreview || '').slice(0, 120),
|
|
131
134
|
}));
|
|
132
135
|
}
|
|
133
136
|
|
|
@@ -144,9 +147,13 @@ server.registerTool('health', {
|
|
|
144
147
|
ok: true,
|
|
145
148
|
server: { name: 'vibesecur-mcp-server', version: mcpPkg.version },
|
|
146
149
|
lock: {
|
|
147
|
-
configured: !!(INSTALL_TOKEN && BOUND_ROOT),
|
|
150
|
+
configured: UNIVERSAL_MODE || !!(INSTALL_TOKEN && BOUND_ROOT),
|
|
151
|
+
mode: UNIVERSAL_MODE ? 'universal' : 'single-folder',
|
|
148
152
|
boundRoot: BOUND_ROOT || null,
|
|
153
|
+
accountWide: UNIVERSAL_MODE,
|
|
149
154
|
healthy: diag.healthy,
|
|
155
|
+
runtimeCompatible: diag.runtimeCompatible || false,
|
|
156
|
+
source: diag.source || null,
|
|
150
157
|
issues: diag.issues || [],
|
|
151
158
|
},
|
|
152
159
|
rules: {
|
|
@@ -165,9 +172,11 @@ server.registerTool('health', {
|
|
|
165
172
|
|
|
166
173
|
if (detail === 'full') {
|
|
167
174
|
payload.envHints = {
|
|
175
|
+
VIBESECUR_AUTH_TOKEN: AUTH_TOKEN ? '***set***' : 'NOT SET',
|
|
168
176
|
VIBESECUR_INSTALL_TOKEN: INSTALL_TOKEN ? '***set***' : 'NOT SET',
|
|
169
177
|
VIBESECUR_BOUND_ROOT: BOUND_ROOT || 'NOT SET',
|
|
170
|
-
VIBESECUR_API_BASE:
|
|
178
|
+
VIBESECUR_API_BASE: API_BASE || 'NOT SET',
|
|
179
|
+
universalMode: UNIVERSAL_MODE,
|
|
171
180
|
CURSOR_WORKSPACE_PATH: process.env.CURSOR_WORKSPACE_PATH ?? null,
|
|
172
181
|
WORKSPACE_PATH: process.env.WORKSPACE_PATH ?? null,
|
|
173
182
|
};
|
|
@@ -178,7 +187,9 @@ server.registerTool('health', {
|
|
|
178
187
|
ok: true,
|
|
179
188
|
version: mcpPkg.version,
|
|
180
189
|
lockConfigured: payload.lock.configured,
|
|
190
|
+
lockMode: payload.lock.mode,
|
|
181
191
|
lockHealthy: payload.lock.healthy,
|
|
192
|
+
runtimeCompatible: payload.lock.runtimeCompatible,
|
|
182
193
|
boundRoot: BOUND_ROOT || null,
|
|
183
194
|
totalRules: JS_RULES.length + PY_RULES.length,
|
|
184
195
|
processCwd: cwd,
|
|
@@ -218,92 +229,29 @@ server.registerTool('installDiagnostic', {
|
|
|
218
229
|
};
|
|
219
230
|
});
|
|
220
231
|
|
|
221
|
-
server
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
code: z.string().min(1).max(50000).describe('Source code to scan'),
|
|
226
|
-
lang: z.enum(['js', 'ts', 'py', 'json', 'auto']).default('auto'),
|
|
227
|
-
projectRoot: z.string().default('.').describe('Must be inside bound folder'),
|
|
228
|
-
},
|
|
229
|
-
}, async ({ code, lang = 'auto', projectRoot = '.' }) => {
|
|
230
|
-
const guard = await guardPath(projectRoot);
|
|
231
|
-
if (!guard.ok) return guardError(guard);
|
|
232
|
-
const remote = await postRemoteLocalScan({
|
|
233
|
-
code,
|
|
234
|
-
lang,
|
|
235
|
-
projectRoot: guard.resolvedRoot,
|
|
236
|
-
platform: 'mcp',
|
|
237
|
-
token: INSTALL_TOKEN,
|
|
238
|
-
});
|
|
239
|
-
if (!remote.skipped && remote.status === 402) {
|
|
240
|
-
return {
|
|
241
|
-
content: [{ type: 'text', text: JSON.stringify({ ...remote.json, upgradeUrl: 'https://vibesecur.com/#pricing' }, null, 2) }],
|
|
242
|
-
isError: true,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
if (!remote.skipped && remote.ok && remote.json?.success && remote.json?.data) {
|
|
246
|
-
const data = remote.json.data;
|
|
247
|
-
const humanSummary = `${data.verdict || ''} Score ${data.score} (${data.grade}) - ${(data.findings || []).length} finding(s).`;
|
|
248
|
-
const enriched = { ...data, humanSummary, engineVersion: mcpPkg.version, quota: remote.json.quota };
|
|
249
|
-
return { content: [{ type: 'text', text: JSON.stringify(enriched, null, 2) }], structuredContent: enriched };
|
|
250
|
-
}
|
|
251
|
-
const result = localScan(code, lang);
|
|
252
|
-
const humanSummary = `${result.verdict} Score ${result.score} (${result.grade}) - ${result.findings.length} finding(s).`;
|
|
253
|
-
const enriched = { ...result, humanSummary, engineVersion: mcpPkg.version, mode: 'offline' };
|
|
254
|
-
return { content: [{ type: 'text', text: JSON.stringify(enriched, null, 2) }], structuredContent: enriched };
|
|
232
|
+
registerProjectTools(server, {
|
|
233
|
+
authToken: AUTH_TOKEN,
|
|
234
|
+
apiBase: API_BASE,
|
|
235
|
+
normalizeRootPath,
|
|
255
236
|
});
|
|
256
237
|
|
|
257
|
-
server
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
projectRoot: guard.resolvedRoot,
|
|
275
|
-
platform: 'mcp',
|
|
276
|
-
token: INSTALL_TOKEN,
|
|
277
|
-
});
|
|
278
|
-
let result;
|
|
279
|
-
if (!remote.skipped && remote.ok && remote.json?.success && remote.json?.data) {
|
|
280
|
-
result = remote.json.data;
|
|
281
|
-
} else if (!remote.skipped && remote.status === 402) {
|
|
282
|
-
return { content: [{ type: 'text', text: JSON.stringify(remote.json, null, 2) }], isError: true };
|
|
283
|
-
} else {
|
|
284
|
-
result = localScan(code, useLang);
|
|
285
|
-
}
|
|
286
|
-
const findings = result.findings || [];
|
|
287
|
-
const bySev = findings.reduce((a, f) => {
|
|
288
|
-
a[f.severity] = (a[f.severity] || 0) + 1;
|
|
289
|
-
return a;
|
|
290
|
-
}, { critical: 0, high: 0, medium: 0, low: 0 });
|
|
291
|
-
const humanSummary = `File "${resolvedPath}": score ${result.score} (${result.grade}), ${findings.length} issue(s).`;
|
|
292
|
-
const body = {
|
|
293
|
-
humanSummary,
|
|
294
|
-
filePath: resolvedPath,
|
|
295
|
-
lang: useLang,
|
|
296
|
-
score: result.score,
|
|
297
|
-
grade: result.grade,
|
|
298
|
-
findings: findings.length,
|
|
299
|
-
bySeverity: bySev,
|
|
300
|
-
checklist: result.checklist,
|
|
301
|
-
result,
|
|
302
|
-
};
|
|
303
|
-
return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], structuredContent: body };
|
|
304
|
-
} catch (e) {
|
|
305
|
-
return { content: [{ type: 'text', text: `scanFile failed: ${e.message}` }], isError: true };
|
|
306
|
-
}
|
|
238
|
+
registerLocalScanTool(server, {
|
|
239
|
+
guardPath,
|
|
240
|
+
guardError,
|
|
241
|
+
installToken: INSTALL_TOKEN,
|
|
242
|
+
engineVersion: mcpPkg.version,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
registerScanFileTool(server, {
|
|
246
|
+
guardPath,
|
|
247
|
+
guardError,
|
|
248
|
+
installToken: INSTALL_TOKEN,
|
|
249
|
+
engineVersion: mcpPkg.version,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
registerDeepScanTools(server, {
|
|
253
|
+
guardPath,
|
|
254
|
+
guardError,
|
|
307
255
|
});
|
|
308
256
|
|
|
309
257
|
server.registerTool('scanRepo', {
|
|
@@ -323,7 +271,24 @@ server.registerTool('scanRepo', {
|
|
|
323
271
|
await ensureDirectory(resolvedRoot);
|
|
324
272
|
const { matchedFiles, limitedFiles, fileResults, aggregate, topRiskFiles } =
|
|
325
273
|
await gatherRepoScan(resolvedRoot, includeGlobs, excludeGlobs, maxFiles);
|
|
274
|
+
const allFindings = flattenFindings(fileResults).map((f) => ({
|
|
275
|
+
filePath: f.filePath,
|
|
276
|
+
lineNumber: f.lineNumber,
|
|
277
|
+
endLineNumber: f.endLineNumber || f.lineNumber,
|
|
278
|
+
ruleId: f.ruleId,
|
|
279
|
+
ruleName: f.ruleName,
|
|
280
|
+
severity: f.severity,
|
|
281
|
+
category: f.category,
|
|
282
|
+
snippetPreview: (f.snippetPreview || '').slice(0, 120),
|
|
283
|
+
fix: f.fix,
|
|
284
|
+
}));
|
|
326
285
|
const meta = buildScanMeta(resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedFiles.length, limitedFiles.length);
|
|
286
|
+
const logged = await syncRepoScanToDashboard({
|
|
287
|
+
aggregate,
|
|
288
|
+
findings: allFindings,
|
|
289
|
+
projectRoot: resolvedRoot,
|
|
290
|
+
guard,
|
|
291
|
+
});
|
|
327
292
|
const body = {
|
|
328
293
|
meta,
|
|
329
294
|
humanSummary: humanRepoSummary(meta, aggregate),
|
|
@@ -334,6 +299,8 @@ server.registerTool('scanRepo', {
|
|
|
334
299
|
summary: aggregate.summary,
|
|
335
300
|
checklist: aggregate.checklist,
|
|
336
301
|
topRiskFiles,
|
|
302
|
+
allFindings,
|
|
303
|
+
logged,
|
|
337
304
|
};
|
|
338
305
|
return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], structuredContent: { ...body, fileResults } };
|
|
339
306
|
} catch (e) {
|
|
@@ -350,8 +317,16 @@ server.registerTool('scanSummary', {
|
|
|
350
317
|
excludeGlobs: z.array(z.string()).default(DEFAULT_EXCLUDE),
|
|
351
318
|
maxFiles: z.number().int().min(1).max(5000).default(200),
|
|
352
319
|
topFindings: z.number().int().min(1).max(50).default(20),
|
|
320
|
+
maxFindings: z.number().int().min(20).max(500).default(200),
|
|
353
321
|
},
|
|
354
|
-
}, async ({
|
|
322
|
+
}, async ({
|
|
323
|
+
rootPath,
|
|
324
|
+
includeGlobs = DEFAULT_INCLUDE,
|
|
325
|
+
excludeGlobs = DEFAULT_EXCLUDE,
|
|
326
|
+
maxFiles = 200,
|
|
327
|
+
topFindings = 20,
|
|
328
|
+
maxFindings = 200,
|
|
329
|
+
}) => {
|
|
355
330
|
try {
|
|
356
331
|
const guard = await guardPath(rootPath);
|
|
357
332
|
if (!guard.ok) return guardError(guard);
|
|
@@ -361,12 +336,24 @@ server.registerTool('scanSummary', {
|
|
|
361
336
|
await gatherRepoScan(resolvedRoot, includeGlobs, excludeGlobs, maxFiles);
|
|
362
337
|
const meta = buildScanMeta(resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedFiles.length, limitedFiles.length);
|
|
363
338
|
const top = pickTopFindings(fileResults, topFindings);
|
|
339
|
+
const allFindings = flattenFindings(fileResults).slice(0, maxFindings).map((f) => ({
|
|
340
|
+
filePath: f.filePath,
|
|
341
|
+
lineNumber: f.lineNumber,
|
|
342
|
+
endLineNumber: f.endLineNumber || f.lineNumber,
|
|
343
|
+
ruleId: f.ruleId,
|
|
344
|
+
ruleName: f.ruleName,
|
|
345
|
+
severity: f.severity,
|
|
346
|
+
category: f.category,
|
|
347
|
+
snippetPreview: (f.snippetPreview || '').slice(0, 120),
|
|
348
|
+
fix: f.fix,
|
|
349
|
+
}));
|
|
364
350
|
const payload = {
|
|
365
351
|
meta,
|
|
366
352
|
humanSummary: humanRepoSummary(meta, aggregate),
|
|
367
353
|
summary: aggregate.summary,
|
|
368
354
|
checklist: { passed: aggregate.checklist.filter((c) => c.pass).length, total: aggregate.checklist.length },
|
|
369
355
|
topFindings: top,
|
|
356
|
+
allFindings,
|
|
370
357
|
};
|
|
371
358
|
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], structuredContent: payload };
|
|
372
359
|
} catch (e) {
|
|
@@ -396,7 +383,24 @@ server.registerTool('scanCurrentWorkspace', {
|
|
|
396
383
|
await ensureDirectory(guard.resolvedRoot);
|
|
397
384
|
const { matchedFiles, limitedFiles, fileResults, aggregate, topRiskFiles } =
|
|
398
385
|
await gatherRepoScan(guard.resolvedRoot, includeGlobs, excludeGlobs, maxFiles);
|
|
386
|
+
const allFindings = flattenFindings(fileResults).map((f) => ({
|
|
387
|
+
filePath: f.filePath,
|
|
388
|
+
lineNumber: f.lineNumber,
|
|
389
|
+
endLineNumber: f.endLineNumber || f.lineNumber,
|
|
390
|
+
ruleId: f.ruleId,
|
|
391
|
+
ruleName: f.ruleName,
|
|
392
|
+
severity: f.severity,
|
|
393
|
+
category: f.category,
|
|
394
|
+
snippetPreview: (f.snippetPreview || '').slice(0, 120),
|
|
395
|
+
fix: f.fix,
|
|
396
|
+
}));
|
|
399
397
|
const meta = buildScanMeta(guard.resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedFiles.length, limitedFiles.length);
|
|
398
|
+
const logged = await syncRepoScanToDashboard({
|
|
399
|
+
aggregate,
|
|
400
|
+
findings: allFindings,
|
|
401
|
+
projectRoot: guard.resolvedRoot,
|
|
402
|
+
guard,
|
|
403
|
+
});
|
|
400
404
|
const body = {
|
|
401
405
|
meta,
|
|
402
406
|
humanSummary: humanRepoSummary(meta, aggregate),
|
|
@@ -407,6 +411,8 @@ server.registerTool('scanCurrentWorkspace', {
|
|
|
407
411
|
summary: aggregate.summary,
|
|
408
412
|
checklist: aggregate.checklist,
|
|
409
413
|
topRiskFiles,
|
|
414
|
+
allFindings,
|
|
415
|
+
logged,
|
|
410
416
|
};
|
|
411
417
|
return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], structuredContent: { ...body, fileResults } };
|
|
412
418
|
} catch (e) {
|
|
@@ -476,6 +482,16 @@ server.registerTool('buildClaudePrompt', {
|
|
|
476
482
|
});
|
|
477
483
|
|
|
478
484
|
async function main() {
|
|
485
|
+
if (!INSTALL_TOKEN || !BOUND_ROOT) {
|
|
486
|
+
throw new Error(
|
|
487
|
+
'VIBESECUR_INSTALL_TOKEN and VIBESECUR_BOUND_ROOT are required. ' +
|
|
488
|
+
'Run "vibesecur-mcp bind <folder>" and update your MCP config env.',
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
const startupGuard = await guardPath(BOUND_ROOT);
|
|
492
|
+
if (!startupGuard.ok) {
|
|
493
|
+
throw new Error(`${startupGuard.code}: ${startupGuard.message}`);
|
|
494
|
+
}
|
|
479
495
|
const transport = new StdioServerTransport();
|
|
480
496
|
await server.connect(transport);
|
|
481
497
|
process.stderr.write(
|