@saiteja1123/mcp-server 1.1.2 → 1.1.4

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 CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@saiteja1123/mcp-server",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "private": false,
5
+ "license": "MIT",
5
6
  "description": "Vibesecur MCP security scanner - one-folder locking, cross-IDE, Cursor/VSCode/Windsurf",
6
7
  "type": "module",
7
8
  "main": "./src/index.js",
@@ -16,6 +17,8 @@
16
17
  "start": "node ./src/server.js",
17
18
  "dev": "node --watch ./src/server.js",
18
19
  "bind": "node ./src/cli.js bind",
20
+ "init": "node ./src/cli.js init",
21
+ "doctor": "node ./src/cli.js doctor",
19
22
  "test": "node --input-type=module --eval \"import('./src/server.js')\""
20
23
  },
21
24
  "exports": {
package/src/api-scan.mjs CHANGED
@@ -19,12 +19,47 @@ export function getProjectHashForPath(rootPath) {
19
19
  return sha256Hex(`vibesecur:mcp:${resolved}`);
20
20
  }
21
21
 
22
- function normalizeApiBase(raw) {
22
+ export function normalizeApiBase(raw) {
23
23
  if (!raw || typeof raw !== 'string') return '';
24
24
  const u = raw.trim().replace(/\/$/, '');
25
25
  return u.endsWith('/api/v1') ? u : `${u}/api/v1`;
26
26
  }
27
27
 
28
+ /** GET origin (no /api/v1) for lightweight health checks. */
29
+ export function getApiOriginFromBase(raw) {
30
+ const normalized = normalizeApiBase(raw);
31
+ if (!normalized) return '';
32
+ return normalized.replace(/\/api\/v1\/?$/, '') || normalized;
33
+ }
34
+
35
+ /**
36
+ * Quick reachability check (GET app root). Does not run a scan.
37
+ * @param {string} [baseUrl] - e.g. https://vibesecur.onrender.com (optional /api/v1 ok)
38
+ */
39
+ export async function pingBackend(baseUrl) {
40
+ const raw = (
41
+ baseUrl
42
+ || process.env.VIBESECUR_API_BASE
43
+ || process.env.VIBESECUR_API_URL
44
+ || 'https://vibesecur.onrender.com'
45
+ ).trim();
46
+ const origin = getApiOriginFromBase(raw);
47
+ if (!origin) {
48
+ return { ok: false, skipped: true, message: 'No API base URL' };
49
+ }
50
+ const url = origin.endsWith('/') ? origin.slice(0, -1) : origin;
51
+ try {
52
+ const ctrl = typeof AbortSignal !== 'undefined' && AbortSignal.timeout
53
+ ? AbortSignal.timeout(12000)
54
+ : undefined;
55
+ const res = await fetch(`${url}/`, { method: 'GET', signal: ctrl });
56
+ const ok = true;
57
+ return { ok, status: res.status, url: `${url}/` };
58
+ } catch (e) {
59
+ return { ok: false, error: e.message };
60
+ }
61
+ }
62
+
28
63
  export async function postRemoteLocalScan({
29
64
  code,
30
65
  lang = 'auto',
package/src/cli.js CHANGED
@@ -1,19 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * vibesecur-mcp CLI
4
- * Usage:
5
- * vibesecur-mcp bind <folder>
6
- * vibesecur-mcp rebind <folder>
7
- * vibesecur-mcp status [folder]
8
- * vibesecur-mcp config <folder>
9
- * vibesecur-mcp start
4
+ *
5
+ * Quick start: npx -y @saiteja1123/mcp-server init
6
+ *
7
+ * Commands:
8
+ * init [folder] bind folder + print IDE configs (optional --write=...)
9
+ * doctor [folder] check lock, env hints, backend reachability
10
+ * bind <folder>
11
+ * rebind <folder>
12
+ * status [folder]
13
+ * config [folder]
14
+ * start MCP stdio server
15
+ *
16
+ * Flags (init): --api-base=URL --skip-bind --write=cursor|vscode|windsurf|all
10
17
  */
11
18
 
19
+ import fs from 'fs/promises';
12
20
  import path from 'path';
21
+ import os from 'os';
22
+ import { createRequire } from 'module';
13
23
  import { fileURLToPath } from 'url';
14
24
  import { createLock, rebindLock, diagnosticLock, readLock } from './lock.mjs';
25
+ import { pingBackend } from './api-scan.mjs';
15
26
 
16
- const [, , command, arg] = process.argv;
27
+ const require = createRequire(import.meta.url);
28
+ const mcpPkg = require('../package.json');
29
+ const NPM_PACKAGE_NAME = mcpPkg.name || '@saiteja1123/mcp-server';
30
+ const DEFAULT_API_BASE = 'https://vibesecur.onrender.com';
31
+
32
+ const serverPath = fileURLToPath(new URL('./server.js', import.meta.url));
17
33
 
18
34
  const BOLD = '\x1b[1m';
19
35
  const GREEN = '\x1b[32m';
@@ -27,15 +43,106 @@ function ok(t) { return `${GREEN}✓ ${t}${RESET}`; }
27
43
  function err(t) { return `${RED}✗ ${t}${RESET}`; }
28
44
  function dim(t) { return `${DIM}${t}${RESET}`; }
29
45
 
30
- const serverPath = fileURLToPath(new URL('./server.js', import.meta.url));
46
+ function parseArgv(argv) {
47
+ const flags = {};
48
+ const positional = [];
49
+ for (const a of argv) {
50
+ if (a.startsWith('--')) {
51
+ const eq = a.indexOf('=');
52
+ if (eq === -1) flags[a.slice(2)] = true;
53
+ else flags[a.slice(2, eq)] = a.slice(eq + 1);
54
+ } else {
55
+ positional.push(a);
56
+ }
57
+ }
58
+ return { flags, positional };
59
+ }
60
+
61
+ const { flags, positional } = parseArgv(process.argv.slice(2));
62
+ const command = positional[0];
63
+ const arg = positional[1];
31
64
 
32
65
  function serverCmd() {
33
- if (serverPath.includes('node_modules')) {
34
- return { command: 'npx', args: ['-y', '@vibesecur/mcp-server', 'start'] };
66
+ const normalizedPath = serverPath.replace(/\\/g, '/');
67
+ if (normalizedPath.includes('/node_modules/')) {
68
+ return { command: 'npx', args: ['-y', NPM_PACKAGE_NAME, 'start'] };
35
69
  }
36
70
  return { command: 'node', args: [serverPath] };
37
71
  }
38
72
 
73
+ function buildEnv(folder, token, apiBase) {
74
+ return {
75
+ VIBESECUR_INSTALL_TOKEN: token,
76
+ VIBESECUR_BOUND_ROOT: folder,
77
+ VIBESECUR_API_BASE: apiBase || DEFAULT_API_BASE,
78
+ };
79
+ }
80
+
81
+ function printIdeBlocks(sc, env) {
82
+ console.log(`${BOLD}-- Cursor (~/.cursor/mcp.json) --${RESET}`);
83
+ console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
84
+
85
+ console.log(`\n${BOLD}-- VS Code (project .vscode/mcp.json) --${RESET}`);
86
+ console.log(JSON.stringify({ servers: { vibesecur: { type: 'stdio', command: sc.command, args: sc.args, env } } }, null, 2));
87
+
88
+ console.log(`\n${BOLD}-- Windsurf (~/.codeium/windsurf/mcp_config.json) --${RESET}`);
89
+ console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
90
+
91
+ console.log(`\n${BOLD}-- Generic env (paste into your MCP client) --${RESET}`);
92
+ console.log(`command: ${sc.command} ${sc.args.join(' ')}`);
93
+ Object.entries(env).forEach(([k, v]) => console.log(` ${k}=${v}`));
94
+ console.log();
95
+ }
96
+
97
+ async function mergeJsonFile(filePath, merger) {
98
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
99
+ let data = {};
100
+ try {
101
+ const raw = await fs.readFile(filePath, 'utf8');
102
+ data = JSON.parse(raw);
103
+ } catch {
104
+ data = {};
105
+ }
106
+ const next = merger(data);
107
+ await fs.writeFile(filePath, JSON.stringify(next, null, 2), 'utf8');
108
+ }
109
+
110
+ async function writeIdeConfig(which, sc, env) {
111
+ const entry = { command: sc.command, args: sc.args, env };
112
+ const vscodeEntry = { type: 'stdio', command: sc.command, args: sc.args, env };
113
+
114
+ if (which === 'cursor' || which === 'all') {
115
+ const file = path.join(os.homedir(), '.cursor', 'mcp.json');
116
+ await mergeJsonFile(file, (data) => {
117
+ const out = { ...data };
118
+ out.mcpServers = { ...(out.mcpServers || {}), vibesecur: entry };
119
+ return out;
120
+ });
121
+ console.log(ok(`Wrote Cursor MCP config: ${file}`));
122
+ }
123
+
124
+ if (which === 'vscode' || which === 'all') {
125
+ const folder = env.VIBESECUR_BOUND_ROOT;
126
+ const file = path.join(folder, '.vscode', 'mcp.json');
127
+ await mergeJsonFile(file, (data) => {
128
+ const out = { ...data };
129
+ out.servers = { ...(out.servers || {}), vibesecur: vscodeEntry };
130
+ return out;
131
+ });
132
+ console.log(ok(`Wrote VS Code MCP config: ${file}`));
133
+ }
134
+
135
+ if (which === 'windsurf' || which === 'all') {
136
+ const file = path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
137
+ await mergeJsonFile(file, (data) => {
138
+ const out = { ...data };
139
+ out.mcpServers = { ...(out.mcpServers || {}), vibesecur: entry };
140
+ return out;
141
+ });
142
+ console.log(ok(`Wrote Windsurf MCP config: ${file}`));
143
+ }
144
+ }
145
+
39
146
  async function cmdBind() {
40
147
  const folder = path.resolve(arg || process.cwd());
41
148
  const account = process.env.VIBESECUR_ACCOUNT || 'anonymous';
@@ -89,46 +196,118 @@ async function cmdConfig() {
89
196
  const folder = path.resolve(arg || process.cwd());
90
197
  const lock = await readLock(folder);
91
198
  const token = lock?.installToken || 'YOUR_INSTALL_TOKEN';
199
+ const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
92
200
 
93
201
  console.log(`\n${h('Vibesecur MCP - IDE Config Snippets')}`);
94
202
  console.log(`Folder: ${folder}`);
95
203
  if (!lock) console.log(err(`No lock found. Run: vibesecur-mcp bind ${folder}`));
96
204
  console.log();
97
205
 
98
- const env = {
99
- VIBESECUR_INSTALL_TOKEN: token,
100
- VIBESECUR_BOUND_ROOT: folder,
101
- VIBESECUR_API_BASE: 'https://api.vibesecur.com',
102
- };
206
+ const env = buildEnv(folder, token, apiBase);
103
207
  const sc = serverCmd();
208
+ printIdeBlocks(sc, env);
209
+ }
104
210
 
105
- console.log(`${BOLD}-- Cursor (.cursor/mcp.json) --${RESET}`);
106
- console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
211
+ async function cmdInit() {
212
+ const folder = path.resolve(arg || process.cwd());
213
+ const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
214
+ const account = process.env.VIBESECUR_ACCOUNT || 'anonymous';
215
+ const skipBind = flags['skip-bind'] === true || flags['skip-bind'] === 'true';
107
216
 
108
- console.log(`\n${BOLD}-- VS Code (.vscode/mcp.json) --${RESET}`);
109
- console.log(JSON.stringify({ servers: { vibesecur: { type: 'stdio', command: sc.command, args: sc.args, env } } }, null, 2));
217
+ console.log(`\n${h('Vibesecur MCP - Init')}`);
218
+ console.log(`Project folder: ${folder}`);
219
+ console.log(`API base: ${apiBase}`);
220
+ console.log(`Package: ${NPM_PACKAGE_NAME}\n`);
110
221
 
111
- console.log(`\n${BOLD}-- Windsurf (~/.codeium/windsurf/mcp_config.json) --${RESET}`);
112
- console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
222
+ let lock = await readLock(folder);
223
+ if (!lock) {
224
+ if (skipBind) {
225
+ console.log(err('No lock in this folder. Run without --skip-bind, or: vibesecur-mcp bind <folder>'));
226
+ process.exit(1);
227
+ }
228
+ lock = await createLock({ rootPath: folder, account });
229
+ console.log(ok(`Created lock: ${path.join(folder, '.vibesecur', 'lock.json')}`));
230
+ } else {
231
+ console.log(ok('Existing lock found (reusing token). Use rebind to rotate.'));
232
+ }
113
233
 
114
- console.log(`\n${BOLD}-- Generic / Claude Desktop / Continue.dev --${RESET}`);
115
- console.log(`command: ${sc.command} ${sc.args.join(' ')}`);
116
- Object.entries(env).forEach(([k, v]) => console.log(` ${k}=${v}`));
117
- console.log();
234
+ const env = buildEnv(folder, lock.installToken, apiBase);
235
+ const sc = serverCmd();
236
+
237
+ console.log(`\n${BOLD}Next:${RESET} paste ONE block below into your IDE MCP settings, then reload the IDE.\n`);
238
+ printIdeBlocks(sc, env);
239
+
240
+ const write = flags.write;
241
+ if (write) {
242
+ const w = String(write).toLowerCase();
243
+ if (!['cursor', 'vscode', 'windsurf', 'all'].includes(w)) {
244
+ console.log(err(`Invalid --write=${write}. Use: cursor | vscode | windsurf | all`));
245
+ process.exit(1);
246
+ }
247
+ await writeIdeConfig(w, sc, env);
248
+ console.log(dim('Reload your IDE so it picks up the new MCP config.'));
249
+ }
250
+ }
251
+
252
+ async function cmdDoctor() {
253
+ const folder = path.resolve(arg || process.cwd());
254
+ const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
255
+
256
+ console.log(`\n${h('Vibesecur MCP - Doctor')}`);
257
+ console.log(`Folder: ${folder}`);
258
+ console.log(`API base: ${apiBase}\n`);
259
+
260
+ const lock = await readLock(folder);
261
+ if (!lock) {
262
+ console.log(err('No lock file. Run: vibesecur-mcp init'));
263
+ } else {
264
+ console.log(ok('Lock file present'));
265
+ const diag = await diagnosticLock(folder);
266
+ console.log(diag.healthy ? ok('Lock diagnostic: healthy') : err('Lock diagnostic: issues'));
267
+ if (diag.issues?.length) diag.issues.forEach((i) => console.log(` - ${i}`));
268
+ }
269
+
270
+ const tokenEnv = process.env.VIBESECUR_INSTALL_TOKEN;
271
+ const rootEnv = process.env.VIBESECUR_BOUND_ROOT;
272
+ console.log(`\n${BOLD}Shell env (optional; IDE sets these when MCP runs):${RESET}`);
273
+ console.log(` VIBESECUR_INSTALL_TOKEN: ${tokenEnv ? dim('set') : dim('not set in this shell (normal)')}`);
274
+ console.log(` VIBESECUR_BOUND_ROOT: ${rootEnv ? dim('set') : dim('not set in this shell (normal)')}`);
275
+ console.log(` VIBESECUR_API_BASE: ${process.env.VIBESECUR_API_BASE ? dim(process.env.VIBESECUR_API_BASE) : dim(`${DEFAULT_API_BASE} (default)`)}`);
276
+
277
+ const ping = await pingBackend(apiBase);
278
+ if (ping.skipped) {
279
+ console.log(`\n${err('Backend ping skipped')}`);
280
+ } else if (ping.ok) {
281
+ const note = ping.status >= 400 ? ' (server responded; check path if unexpected)' : '';
282
+ console.log(`\n${ok(`Backend responded HTTP ${ping.status}${note}`)}`);
283
+ console.log(dim(` ${ping.url || ''}`));
284
+ } else {
285
+ console.log(`\n${err('Backend not reachable')}`);
286
+ if (ping.status !== undefined) console.log(` HTTP ${ping.status}`);
287
+ if (ping.error) console.log(` ${ping.error}`);
288
+ }
289
+
290
+ console.log(`\n${BOLD}CLI:${RESET} ${NPM_PACKAGE_NAME}@${mcpPkg.version}`);
291
+ console.log(dim('If IDE still fails: run init again, paste fresh config, reload IDE.\n'));
118
292
  }
119
293
 
120
294
  function usage() {
121
295
  console.log(`\n${h('Vibesecur MCP')}`);
122
- console.log(' bind <folder> bind install to a project folder');
123
- console.log(' rebind <folder> revoke old token, issue new lock');
124
- console.log(' status [folder] check lock health');
125
- console.log(' config <folder> print IDE config snippets');
126
- console.log(' start start MCP server (stdio)\n');
296
+ console.log(` ${dim('Quick start:')} vibesecur-mcp init`);
297
+ console.log(' init [folder] bind + print IDE configs [--api-base=URL] [--write=cursor|vscode|windsurf|all] [--skip-bind]');
298
+ console.log(' doctor [folder] check lock + backend [--api-base=URL]');
299
+ console.log(' bind <folder> create lock only');
300
+ console.log(' rebind <folder> rotate token');
301
+ console.log(' status [folder] lock health');
302
+ console.log(' config [folder] print snippets only');
303
+ console.log(' start MCP server (stdio)\n');
127
304
  process.exit(1);
128
305
  }
129
306
 
130
307
  try {
131
308
  switch (command) {
309
+ case 'init': await cmdInit(); break;
310
+ case 'doctor': await cmdDoctor(); break;
132
311
  case 'bind': await cmdBind(); break;
133
312
  case 'rebind': await cmdRebind(); break;
134
313
  case 'status': await cmdStatus(); break;
package/src/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Re-exports the Vibesecur rule engine for MCP servers, Cursor hooks, or other tooling.
3
- * Implement MCP protocol handlers in a separate file that imports from here or from
4
- * `@vibesecur/rule-engine` directly.
2
+ * Re-export bundled local rule engine for MCP tooling.
3
+ * This keeps the MCP package self-contained at runtime.
5
4
  */
6
- export * from '@vibesecur/rule-engine';
5
+ export * from './rule-engine/index.js';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * rule-engine/index.js
3
3
  * Bundled inline - no external @vibesecur/rule-engine dep needed.
4
- * This allows `npx @vibesecur/mcp-server` to work standalone.
4
+ * This allows `npx @saiteja1123/mcp-server` to work standalone.
5
5
  */
6
6
  export { JS_RULES, PY_RULES, CHECKLIST } from './rules.js';
7
7
  export { localScan } from './localScan.js';
@@ -24,7 +24,9 @@ export function localScan(code, lang = 'js') {
24
24
  severity: rule.sev,
25
25
  category: rule.cat,
26
26
  lineNumber,
27
+ endLineNumber: lineNumber,
27
28
  snippet,
29
+ snippetPreview: snippet,
28
30
  fix: rule.fix,
29
31
  });
30
32
  }
package/src/server.js CHANGED
@@ -110,7 +110,11 @@ function humanRepoSummary(meta, agg) {
110
110
 
111
111
  function flattenFindings(fileResults) {
112
112
  return fileResults.flatMap((fr) =>
113
- (fr.result.findings || []).map((f) => ({ ...f, filePath: fr.filePath })),
113
+ (fr.result.findings || []).map((f) => ({
114
+ ...f,
115
+ filePath: fr.filePath,
116
+ snippetPreview: f.snippetPreview || f.snippet || '',
117
+ })),
114
118
  );
115
119
  }
116
120
 
@@ -124,10 +128,11 @@ function pickTopFindings(fileResults, n) {
124
128
  return flat.slice(0, n).map((f) => ({
125
129
  filePath: f.filePath,
126
130
  lineNumber: f.lineNumber,
131
+ endLineNumber: f.endLineNumber || f.lineNumber,
127
132
  ruleId: f.ruleId,
128
133
  ruleName: f.ruleName,
129
134
  severity: f.severity,
130
- snippetPreview: (f.snippet || '').slice(0, 120),
135
+ snippetPreview: (f.snippetPreview || '').slice(0, 120),
131
136
  }));
132
137
  }
133
138
 
@@ -284,6 +289,11 @@ server.registerTool('scanFile', {
284
289
  result = localScan(code, useLang);
285
290
  }
286
291
  const findings = result.findings || [];
292
+ const findingsWithLocation = findings.map((f) => ({
293
+ ...f,
294
+ filePath: resolvedPath,
295
+ snippetPreview: f.snippetPreview || f.snippet || '',
296
+ }));
287
297
  const bySev = findings.reduce((a, f) => {
288
298
  a[f.severity] = (a[f.severity] || 0) + 1;
289
299
  return a;
@@ -295,10 +305,13 @@ server.registerTool('scanFile', {
295
305
  lang: useLang,
296
306
  score: result.score,
297
307
  grade: result.grade,
298
- findings: findings.length,
308
+ findings: findingsWithLocation.length,
299
309
  bySeverity: bySev,
300
310
  checklist: result.checklist,
301
- result,
311
+ result: {
312
+ ...result,
313
+ findings: findingsWithLocation,
314
+ },
302
315
  };
303
316
  return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], structuredContent: body };
304
317
  } catch (e) {
@@ -323,6 +336,17 @@ server.registerTool('scanRepo', {
323
336
  await ensureDirectory(resolvedRoot);
324
337
  const { matchedFiles, limitedFiles, fileResults, aggregate, topRiskFiles } =
325
338
  await gatherRepoScan(resolvedRoot, includeGlobs, excludeGlobs, maxFiles);
339
+ const allFindings = flattenFindings(fileResults).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
+ }));
326
350
  const meta = buildScanMeta(resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedFiles.length, limitedFiles.length);
327
351
  const body = {
328
352
  meta,
@@ -334,6 +358,7 @@ server.registerTool('scanRepo', {
334
358
  summary: aggregate.summary,
335
359
  checklist: aggregate.checklist,
336
360
  topRiskFiles,
361
+ allFindings,
337
362
  };
338
363
  return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], structuredContent: { ...body, fileResults } };
339
364
  } catch (e) {
@@ -350,8 +375,16 @@ server.registerTool('scanSummary', {
350
375
  excludeGlobs: z.array(z.string()).default(DEFAULT_EXCLUDE),
351
376
  maxFiles: z.number().int().min(1).max(5000).default(200),
352
377
  topFindings: z.number().int().min(1).max(50).default(20),
378
+ maxFindings: z.number().int().min(20).max(500).default(200),
353
379
  },
354
- }, async ({ rootPath, includeGlobs = DEFAULT_INCLUDE, excludeGlobs = DEFAULT_EXCLUDE, maxFiles = 200, topFindings = 20 }) => {
380
+ }, async ({
381
+ rootPath,
382
+ includeGlobs = DEFAULT_INCLUDE,
383
+ excludeGlobs = DEFAULT_EXCLUDE,
384
+ maxFiles = 200,
385
+ topFindings = 20,
386
+ maxFindings = 200,
387
+ }) => {
355
388
  try {
356
389
  const guard = await guardPath(rootPath);
357
390
  if (!guard.ok) return guardError(guard);
@@ -361,12 +394,24 @@ server.registerTool('scanSummary', {
361
394
  await gatherRepoScan(resolvedRoot, includeGlobs, excludeGlobs, maxFiles);
362
395
  const meta = buildScanMeta(resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedFiles.length, limitedFiles.length);
363
396
  const top = pickTopFindings(fileResults, topFindings);
397
+ const allFindings = flattenFindings(fileResults).slice(0, maxFindings).map((f) => ({
398
+ filePath: f.filePath,
399
+ lineNumber: f.lineNumber,
400
+ endLineNumber: f.endLineNumber || f.lineNumber,
401
+ ruleId: f.ruleId,
402
+ ruleName: f.ruleName,
403
+ severity: f.severity,
404
+ category: f.category,
405
+ snippetPreview: (f.snippetPreview || '').slice(0, 120),
406
+ fix: f.fix,
407
+ }));
364
408
  const payload = {
365
409
  meta,
366
410
  humanSummary: humanRepoSummary(meta, aggregate),
367
411
  summary: aggregate.summary,
368
412
  checklist: { passed: aggregate.checklist.filter((c) => c.pass).length, total: aggregate.checklist.length },
369
413
  topFindings: top,
414
+ allFindings,
370
415
  };
371
416
  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], structuredContent: payload };
372
417
  } catch (e) {
@@ -396,6 +441,17 @@ server.registerTool('scanCurrentWorkspace', {
396
441
  await ensureDirectory(guard.resolvedRoot);
397
442
  const { matchedFiles, limitedFiles, fileResults, aggregate, topRiskFiles } =
398
443
  await gatherRepoScan(guard.resolvedRoot, includeGlobs, excludeGlobs, maxFiles);
444
+ const allFindings = flattenFindings(fileResults).map((f) => ({
445
+ filePath: f.filePath,
446
+ lineNumber: f.lineNumber,
447
+ endLineNumber: f.endLineNumber || f.lineNumber,
448
+ ruleId: f.ruleId,
449
+ ruleName: f.ruleName,
450
+ severity: f.severity,
451
+ category: f.category,
452
+ snippetPreview: (f.snippetPreview || '').slice(0, 120),
453
+ fix: f.fix,
454
+ }));
399
455
  const meta = buildScanMeta(guard.resolvedRoot, includeGlobs, excludeGlobs, maxFiles, matchedFiles.length, limitedFiles.length);
400
456
  const body = {
401
457
  meta,
@@ -407,6 +463,7 @@ server.registerTool('scanCurrentWorkspace', {
407
463
  summary: aggregate.summary,
408
464
  checklist: aggregate.checklist,
409
465
  topRiskFiles,
466
+ allFindings,
410
467
  };
411
468
  return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], structuredContent: { ...body, fileResults } };
412
469
  } catch (e) {