@securitychecks/mcp 0.2.2 → 0.4.0
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/README.md +2 -2
- package/dist/index.js +94 -118
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @securitychecks/mcp
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Security verification for AI-assisted development** — MCP server for backend security invariant checks.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@securitychecks/mcp)
|
|
6
6
|
|
|
7
|
-
MCP server that lets Claude
|
|
7
|
+
MCP server that lets Claude verify security invariants in your code — auth enforcement, injection safety, access control, webhook idempotency.
|
|
8
8
|
|
|
9
9
|
## What is this?
|
|
10
10
|
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { getInvariantById, ALL_INVARIANTS } from '@securitychecks/collector';
|
|
6
|
-
import { generateTestSkeleton, getStaffQuestion, audit, computeReadinessScore } from '@securitychecks/cli';
|
|
6
|
+
import { generateFixPrompt, generateTestSkeleton, getStaffQuestion, audit, computeReadinessScore } from '@securitychecks/cli';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import { execSync } from 'child_process';
|
|
@@ -127,7 +127,7 @@ async function handleFeedbackTool(args, options) {
|
|
|
127
127
|
});
|
|
128
128
|
writeFileSync(feedbackFile, JSON.stringify(feedbackData, null, 2));
|
|
129
129
|
try {
|
|
130
|
-
const clientVersion = "0.
|
|
130
|
+
const clientVersion = "0.4.0";
|
|
131
131
|
const endpoint = "https://api.securitychecks.ai/v1/feedback";
|
|
132
132
|
const controller = new AbortController();
|
|
133
133
|
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
@@ -177,7 +177,7 @@ async function handleFeedbackTool(args, options) {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
// src/index.ts
|
|
180
|
-
var version = "0.
|
|
180
|
+
var version = "0.4.0";
|
|
181
181
|
var server = new Server(
|
|
182
182
|
{
|
|
183
183
|
name: "scheck",
|
|
@@ -193,7 +193,7 @@ var TOOL_PREFIXES = ["scheck"];
|
|
|
193
193
|
var TOOL_DEFS = [
|
|
194
194
|
{
|
|
195
195
|
suffix: "run",
|
|
196
|
-
description: "Run
|
|
196
|
+
description: "Run security checks on the codebase. Verifies auth enforcement, injection safety, access control, webhook idempotency, and more.",
|
|
197
197
|
inputSchema: {
|
|
198
198
|
type: "object",
|
|
199
199
|
properties: {
|
|
@@ -332,6 +332,37 @@ var TOOL_DEFS = [
|
|
|
332
332
|
},
|
|
333
333
|
required: ["invariant_id", "verdict"]
|
|
334
334
|
}
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
suffix: "fix_prompt",
|
|
338
|
+
description: "Generate an AI fix prompt for a specific invariant \u2014 a structured prompt you can paste into any AI coding assistant to fix the issue.",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
type: "object",
|
|
341
|
+
properties: {
|
|
342
|
+
invariant_id: {
|
|
343
|
+
type: "string",
|
|
344
|
+
description: "The invariant ID to generate a fix prompt for"
|
|
345
|
+
},
|
|
346
|
+
file: {
|
|
347
|
+
type: "string",
|
|
348
|
+
description: "File where the violation was found"
|
|
349
|
+
},
|
|
350
|
+
line: {
|
|
351
|
+
type: "integer",
|
|
352
|
+
description: "Line number of the violation"
|
|
353
|
+
},
|
|
354
|
+
message: {
|
|
355
|
+
type: "string",
|
|
356
|
+
description: "Finding message or description"
|
|
357
|
+
},
|
|
358
|
+
framework: {
|
|
359
|
+
type: "string",
|
|
360
|
+
enum: ["jest", "vitest", "playwright"],
|
|
361
|
+
description: "Test framework for the proof test (default: vitest)"
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
required: ["invariant_id"]
|
|
365
|
+
}
|
|
335
366
|
}
|
|
336
367
|
];
|
|
337
368
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -351,12 +382,7 @@ server.setRequestHandler(
|
|
|
351
382
|
async (request) => {
|
|
352
383
|
const { name, arguments: args } = request.params;
|
|
353
384
|
const tool = normalizeToolName(name);
|
|
354
|
-
if (!tool) {
|
|
355
|
-
return {
|
|
356
|
-
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
357
|
-
isError: true
|
|
358
|
-
};
|
|
359
|
-
}
|
|
385
|
+
if (!tool) return textResult(`Unknown tool: ${name}`, true);
|
|
360
386
|
switch (tool) {
|
|
361
387
|
case "run": {
|
|
362
388
|
const path2 = args?.["path"] || process.cwd();
|
|
@@ -404,7 +430,15 @@ server.setRequestHandler(
|
|
|
404
430
|
evidence: formatEvidenceForMcp(f.evidence, includeContext),
|
|
405
431
|
required_proof: f.requiredProof,
|
|
406
432
|
suggested_test: f.suggestedTest,
|
|
407
|
-
staff_engineer_asks: getStaffQuestion(f.invariantId)
|
|
433
|
+
staff_engineer_asks: getStaffQuestion(f.invariantId),
|
|
434
|
+
ai_fix_prompt: generateFixPrompt(
|
|
435
|
+
getInvariantById(f.invariantId) ?? { id: f.invariantId, name: f.invariantId },
|
|
436
|
+
{
|
|
437
|
+
file: f.evidence?.[0]?.file,
|
|
438
|
+
line: f.evidence?.[0]?.line,
|
|
439
|
+
message: f.message
|
|
440
|
+
}
|
|
441
|
+
)
|
|
408
442
|
}));
|
|
409
443
|
return {
|
|
410
444
|
content: [
|
|
@@ -434,16 +468,7 @@ server.setRequestHandler(
|
|
|
434
468
|
}
|
|
435
469
|
}
|
|
436
470
|
case "list_findings": {
|
|
437
|
-
if (!lastAuditResult)
|
|
438
|
-
return {
|
|
439
|
-
content: [
|
|
440
|
-
{
|
|
441
|
-
type: "text",
|
|
442
|
-
text: "No scan has been run yet. Use scheck_run first."
|
|
443
|
-
}
|
|
444
|
-
]
|
|
445
|
-
};
|
|
446
|
-
}
|
|
471
|
+
if (!lastAuditResult) return textResult("No scan has been run yet. Use scheck_run first.");
|
|
447
472
|
const severity = args?.["severity"];
|
|
448
473
|
const includeContext = args?.["include_context"] === true;
|
|
449
474
|
const maxFindings = typeof args?.["max_findings"] === "number" ? Math.max(1, Math.min(500, args["max_findings"])) : 200;
|
|
@@ -453,124 +478,74 @@ server.setRequestHandler(
|
|
|
453
478
|
}
|
|
454
479
|
const truncated = findings.length > maxFindings;
|
|
455
480
|
const findingsToReturn = truncated ? findings.slice(0, maxFindings) : findings;
|
|
456
|
-
return {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
message: f.message,
|
|
466
|
-
evidence: formatEvidenceForMcp(f.evidence, includeContext)
|
|
467
|
-
})),
|
|
468
|
-
meta: {
|
|
469
|
-
total: findings.length,
|
|
470
|
-
truncated,
|
|
471
|
-
max_findings: maxFindings
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
null,
|
|
475
|
-
2
|
|
476
|
-
)
|
|
477
|
-
}
|
|
478
|
-
]
|
|
479
|
-
};
|
|
481
|
+
return jsonResult({
|
|
482
|
+
findings: findingsToReturn.map((f) => ({
|
|
483
|
+
invariant_id: f.invariantId,
|
|
484
|
+
severity: f.severity,
|
|
485
|
+
message: f.message,
|
|
486
|
+
evidence: formatEvidenceForMcp(f.evidence, includeContext)
|
|
487
|
+
})),
|
|
488
|
+
meta: { total: findings.length, truncated, max_findings: maxFindings }
|
|
489
|
+
});
|
|
480
490
|
}
|
|
481
491
|
case "explain": {
|
|
482
492
|
const invariantId = args?.["invariant_id"];
|
|
483
493
|
const invariant = getInvariantById(invariantId);
|
|
484
494
|
if (!invariant) {
|
|
485
|
-
return {
|
|
486
|
-
content: [
|
|
487
|
-
{
|
|
488
|
-
type: "text",
|
|
489
|
-
text: `Unknown pattern: ${invariantId}. Use scheck_list_invariants to see available patterns.`
|
|
490
|
-
}
|
|
491
|
-
]
|
|
492
|
-
};
|
|
495
|
+
return textResult(`Unknown pattern: ${invariantId}. Use scheck_list_invariants to see available patterns.`);
|
|
493
496
|
}
|
|
494
|
-
return {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
category: invariant.category,
|
|
504
|
-
description: invariant.description,
|
|
505
|
-
required_proof: invariant.requiredProof,
|
|
506
|
-
staff_engineer_asks: getStaffQuestion(invariant.id)
|
|
507
|
-
},
|
|
508
|
-
null,
|
|
509
|
-
2
|
|
510
|
-
)
|
|
511
|
-
}
|
|
512
|
-
]
|
|
513
|
-
};
|
|
497
|
+
return jsonResult({
|
|
498
|
+
id: invariant.id,
|
|
499
|
+
name: invariant.name,
|
|
500
|
+
severity: invariant.severity,
|
|
501
|
+
category: invariant.category,
|
|
502
|
+
description: invariant.description,
|
|
503
|
+
required_proof: invariant.requiredProof,
|
|
504
|
+
staff_engineer_asks: getStaffQuestion(invariant.id)
|
|
505
|
+
});
|
|
514
506
|
}
|
|
515
507
|
case "list_invariants": {
|
|
516
508
|
const category = args?.["category"];
|
|
517
509
|
const severity = args?.["severity"];
|
|
518
510
|
let invariants = ALL_INVARIANTS;
|
|
519
|
-
if (category)
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
{
|
|
528
|
-
type: "text",
|
|
529
|
-
text: JSON.stringify(
|
|
530
|
-
invariants.map((i) => ({
|
|
531
|
-
id: i.id,
|
|
532
|
-
name: i.name,
|
|
533
|
-
severity: i.severity,
|
|
534
|
-
category: i.category
|
|
535
|
-
})),
|
|
536
|
-
null,
|
|
537
|
-
2
|
|
538
|
-
)
|
|
539
|
-
}
|
|
540
|
-
]
|
|
541
|
-
};
|
|
511
|
+
if (category) invariants = invariants.filter((i) => i.category === category);
|
|
512
|
+
if (severity) invariants = invariants.filter((i) => i.severity === severity);
|
|
513
|
+
return jsonResult(invariants.map((i) => ({
|
|
514
|
+
id: i.id,
|
|
515
|
+
name: i.name,
|
|
516
|
+
severity: i.severity,
|
|
517
|
+
category: i.category
|
|
518
|
+
})));
|
|
542
519
|
}
|
|
543
520
|
case "generate_test": {
|
|
544
521
|
const invariantId = args?.["invariant_id"];
|
|
545
|
-
const framework = args?.["framework"] || "vitest";
|
|
546
|
-
const context = args?.["context"];
|
|
547
522
|
const invariant = getInvariantById(invariantId);
|
|
548
|
-
if (!invariant) {
|
|
549
|
-
|
|
550
|
-
content: [
|
|
551
|
-
{
|
|
552
|
-
type: "text",
|
|
553
|
-
text: `Unknown invariant: ${invariantId}`
|
|
554
|
-
}
|
|
555
|
-
]
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
const test = generateTestSkeleton(invariant, framework, context);
|
|
559
|
-
return {
|
|
560
|
-
content: [
|
|
561
|
-
{
|
|
562
|
-
type: "text",
|
|
563
|
-
text: test
|
|
564
|
-
}
|
|
565
|
-
]
|
|
566
|
-
};
|
|
523
|
+
if (!invariant) return textResult(`Unknown invariant: ${invariantId}`);
|
|
524
|
+
return textResult(generateTestSkeleton(invariant, args?.["framework"] || "vitest", args?.["context"]));
|
|
567
525
|
}
|
|
568
526
|
case "feedback": {
|
|
569
527
|
return handleFeedbackTool(args);
|
|
570
528
|
}
|
|
529
|
+
case "fix_prompt": {
|
|
530
|
+
const invariantId = args?.["invariant_id"];
|
|
531
|
+
const invariant = getInvariantById(invariantId);
|
|
532
|
+
if (!invariant) return textResult(`Unknown invariant: ${invariantId}. Use scheck_list_invariants to see available invariants.`);
|
|
533
|
+
return textResult(generateFixPrompt(invariant, {
|
|
534
|
+
file: args?.["file"],
|
|
535
|
+
line: typeof args?.["line"] === "number" ? args["line"] : void 0,
|
|
536
|
+
message: args?.["message"],
|
|
537
|
+
framework: args?.["framework"] || "vitest"
|
|
538
|
+
}));
|
|
539
|
+
}
|
|
571
540
|
}
|
|
572
541
|
}
|
|
573
542
|
);
|
|
543
|
+
function textResult(text, isError) {
|
|
544
|
+
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
545
|
+
}
|
|
546
|
+
function jsonResult(data) {
|
|
547
|
+
return textResult(JSON.stringify(data, null, 2));
|
|
548
|
+
}
|
|
574
549
|
function normalizeToolName(name) {
|
|
575
550
|
const suffix = name.replace(/^scheck_/, "");
|
|
576
551
|
const allowed = /* @__PURE__ */ new Set([
|
|
@@ -579,7 +554,8 @@ function normalizeToolName(name) {
|
|
|
579
554
|
"explain",
|
|
580
555
|
"list_invariants",
|
|
581
556
|
"generate_test",
|
|
582
|
-
"feedback"
|
|
557
|
+
"feedback",
|
|
558
|
+
"fix_prompt"
|
|
583
559
|
]);
|
|
584
560
|
return allowed.has(suffix) ? suffix : null;
|
|
585
561
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/safety.ts","../src/feedback.ts","../src/index.ts"],"names":["path"],"mappings":"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","file":"index.js","sourcesContent":["import path from 'node:path';\nimport fs from 'node:fs';\nimport { execSync } from 'node:child_process';\n\nexport type McpSafetyOptions = {\n cwd: string;\n allowedRoots: string[];\n};\n\nexport function parseAllowedRootsFromEnv(\n env: Record<string, string | undefined>,\n cwd: string\n): string[] {\n const raw =\n env['SCHECK_MCP_ALLOWED_ROOTS'] ??\n env['MCP_ALLOWED_ROOTS'] ??\n env['SCHECK_ALLOWED_ROOTS'];\n\n const roots = (raw ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n .map((p) => path.resolve(cwd, p));\n\n if (roots.length > 0) return roots;\n\n const gitRoot = getGitRoot(cwd);\n if (gitRoot) return [gitRoot];\n\n throw new Error(\n 'Refusing to scan because no allowed roots are configured and no git repository was detected. ' +\n 'Run scheck-mcp from inside a git repo, or set SCHECK_MCP_ALLOWED_ROOTS (or MCP_ALLOWED_ROOTS).'\n );\n}\n\nfunction getGitRoot(cwd: string): string | null {\n try {\n const out = execSync('git rev-parse --show-toplevel', {\n cwd,\n stdio: ['ignore', 'pipe', 'ignore'],\n encoding: 'utf-8',\n }).trim();\n return out.length > 0 ? out : null;\n } catch {\n return null;\n }\n}\n\nfunction isWithinRoot(candidatePath: string, root: string): boolean {\n const relative = path.relative(root, candidatePath);\n // `path.relative()` returns '' when equal. Equality should be allowed.\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nexport function resolveAndValidateTargetPath(\n requestedPath: string | undefined,\n options: McpSafetyOptions\n): string {\n const { cwd, allowedRoots } = options;\n const input = (requestedPath && requestedPath.trim().length > 0)\n ? requestedPath\n : cwd;\n\n const resolved = path.resolve(cwd, input);\n\n if (!fs.existsSync(resolved)) {\n throw new Error(`Target path does not exist: ${resolved}`);\n }\n\n // Fail closed on symlink escapes: validate based on real paths, not just `path.resolve`.\n const realResolved = fs.realpathSync(resolved);\n const realAllowedRoots = allowedRoots.map((r) => fs.realpathSync(path.resolve(cwd, r)));\n\n const allowed = realAllowedRoots.some((root) => isWithinRoot(realResolved, root));\n\n if (!allowed) {\n const rootsList = realAllowedRoots.join(', ');\n throw new Error(\n `Refusing to scan outside allowed roots. Requested: ${realResolved}. Allowed roots: ${rootsList}. ` +\n `Set SCHECK_MCP_ALLOWED_ROOTS (or MCP_ALLOWED_ROOTS) to override.`\n );\n }\n\n return realResolved;\n}\n\nexport type EvidenceForMcp = { file: string; line: number; context?: string };\n\nexport function formatEvidenceForMcp(\n evidence: EvidenceForMcp[],\n includeContext: boolean\n): EvidenceForMcp[] {\n if (includeContext) return evidence;\n return evidence.map(({ file, line }) => ({ file, line }));\n}\n","export type FeedbackVerdict = 'true_positive' | 'false_positive';\n\nexport interface FeedbackToolArgs {\n invariant_id?: string;\n verdict?: string;\n reason?: string;\n}\n\nexport interface FeedbackToolResponse {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n}\n\n// Keep tool responses aligned with the MCP SDK result types.\n// (Our response object is a valid CallToolResult / CompatibilityCallToolResult.)\nexport type McpToolResult = import('@modelcontextprotocol/sdk/types.js').CompatibilityCallToolResult;\n\nexport async function handleFeedbackTool(\n args: FeedbackToolArgs | undefined,\n options?: {\n cwd?: string;\n now?: () => Date;\n fetchFn?: typeof fetch;\n }\n): Promise<McpToolResult> {\n const invariantId = args?.invariant_id;\n const verdict = args?.verdict as FeedbackVerdict | undefined;\n const reason = args?.reason;\n\n if (!invariantId || !verdict) {\n return {\n content: [\n {\n type: 'text',\n text: 'Error: invariant_id and verdict are required.',\n },\n ],\n isError: true,\n };\n }\n\n if (!['true_positive', 'false_positive'].includes(verdict)) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: verdict must be \"true_positive\" or \"false_positive\", got \"${verdict}\"`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const { writeFileSync, readFileSync, existsSync, mkdirSync, lstatSync } = await import('fs');\n const { join } = await import('path');\n const cwd = options?.cwd ?? process.cwd();\n const feedbackDir = join(cwd, '.scheck');\n const feedbackFile = join(feedbackDir, 'feedback.json');\n const now = options?.now ?? (() => new Date());\n\n if (!existsSync(feedbackDir)) {\n mkdirSync(feedbackDir, { recursive: true });\n } else {\n const st = lstatSync(feedbackDir);\n // Avoid writing through malicious symlinks (e.g., repo-controlled `.scheck` -> /etc).\n if (st.isSymbolicLink()) {\n throw new Error('Refusing to write feedback: .scheck is a symlink');\n }\n if (!st.isDirectory()) {\n throw new Error('Refusing to write feedback: .scheck is not a directory');\n }\n }\n\n if (existsSync(feedbackFile)) {\n const st = lstatSync(feedbackFile);\n if (st.isSymbolicLink()) {\n throw new Error('Refusing to write feedback: feedback.json is a symlink');\n }\n if (!st.isFile()) {\n throw new Error('Refusing to write feedback: feedback.json is not a file');\n }\n }\n\n let feedbackData: Array<{\n invariantId: string;\n verdict: string;\n reason?: string;\n timestamp: string;\n }> = [];\n if (existsSync(feedbackFile)) {\n try {\n feedbackData = JSON.parse(readFileSync(feedbackFile, 'utf-8'));\n } catch {\n feedbackData = [];\n }\n }\n\n feedbackData.push({\n invariantId,\n verdict,\n reason,\n timestamp: now().toISOString(),\n });\n\n writeFileSync(feedbackFile, JSON.stringify(feedbackData, null, 2));\n\n try {\n const clientVersion = process.env['MCP_VERSION'] ?? '0.0.0-dev';\n const endpoint = 'https://api.securitychecks.ai/v1/feedback';\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 3000);\n const fetchFn = options?.fetchFn ?? (typeof fetch === 'function' ? fetch : undefined);\n if (fetchFn) {\n fetchFn(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ invariantId, verdict, reason, clientVersion }),\n signal: controller.signal,\n })\n .catch(() => {})\n .finally(() => clearTimeout(timeoutId));\n } else {\n clearTimeout(timeoutId);\n }\n } catch {\n // Silent failure for API reporting\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n recorded: true,\n invariantId,\n verdict,\n reason: reason ?? null,\n storedLocally: true,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error recording feedback: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n}\n","#!/usr/bin/env node\n\n/**\n * SecurityChecks MCP Server (scheck)\n *\n * Catch what Copilot misses — production-ready code review.\n * MCP server that exposes scheck tools for LLM integration.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { CompatibilityCallToolResult, CreateTaskResult } from '@modelcontextprotocol/sdk/types.js';\nimport {\n getInvariantById,\n ALL_INVARIANTS,\n type AuditResult,\n type Finding,\n} from '@securitychecks/collector';\nimport { audit, computeReadinessScore } from '@securitychecks/cli';\nimport {\n formatEvidenceForMcp,\n parseAllowedRootsFromEnv,\n resolveAndValidateTargetPath,\n type EvidenceForMcp,\n} from './safety.js';\nimport { generateTestSkeleton, getStaffQuestion } from '@securitychecks/cli';\nimport { handleFeedbackTool } from './feedback.js';\n\n// Version injected at build time via tsup define\nconst version = process.env['MCP_VERSION'] ?? '0.0.0-dev';\n\nconst server = new Server(\n {\n name: 'scheck',\n version: version,\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\nconst TOOL_PREFIXES = ['scheck'] as const;\ntype ToolSuffix = 'run' | 'list_findings' | 'explain' | 'list_invariants' | 'generate_test' | 'feedback';\ntype ToolDef = {\n suffix: ToolSuffix;\n description: string;\n inputSchema: unknown;\n};\n\nconst TOOL_DEFS: ToolDef[] = [\n {\n suffix: 'run',\n description:\n 'Run scheck — the patterns a senior engineer would flag in review. ' +\n 'Catches webhook idempotency, auth at service layer, transaction safety, and more.',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Target path to audit (default: current directory)',\n },\n include_context: {\n type: 'boolean',\n description:\n 'Include code context snippets in results (may expose source code to the assistant).',\n },\n max_findings: {\n type: 'integer',\n minimum: 1,\n maximum: 500,\n description: 'Limit number of findings returned (default: 200)',\n },\n only: {\n type: 'array',\n items: { type: 'string' },\n description: 'Only run specific invariant checks by ID',\n },\n skip: {\n type: 'array',\n items: { type: 'string' },\n description: 'Skip specific invariant checks by ID',\n },\n },\n },\n },\n {\n suffix: 'list_findings',\n description:\n 'List issues a staff engineer would flag — current findings from the last run, by severity.',\n inputSchema: {\n type: 'object',\n properties: {\n severity: {\n type: 'string',\n enum: ['P0', 'P1', 'P2'],\n description: 'Filter findings by severity',\n },\n include_context: {\n type: 'boolean',\n description:\n 'Include code context snippets in results (may expose source code to the assistant).',\n },\n max_findings: {\n type: 'integer',\n minimum: 1,\n maximum: 500,\n description: 'Limit number of findings returned (default: 200)',\n },\n },\n },\n },\n {\n suffix: 'explain',\n description:\n 'What a staff engineer checks: explain why a pattern matters, real incidents it prevents, and proof needed.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'The invariant ID to explain (e.g., AUTHZ.SERVICE_LAYER.ENFORCED)',\n },\n },\n required: ['invariant_id'],\n },\n },\n {\n suffix: 'list_invariants',\n description:\n 'List all patterns a staff engineer checks for — the patterns that prevent production incidents.',\n inputSchema: {\n type: 'object',\n properties: {\n category: {\n type: 'string',\n description: 'Filter by category (authz, revocation, webhooks, transactions, etc.)',\n },\n severity: {\n type: 'string',\n enum: ['P0', 'P1', 'P2'],\n description: 'Filter by severity',\n },\n },\n },\n },\n {\n suffix: 'generate_test',\n description:\n 'Generate test code that proves a pattern is enforced — the proof a staff engineer would ask for.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'The invariant ID to generate a test for',\n },\n framework: {\n type: 'string',\n enum: ['jest', 'vitest', 'playwright'],\n description: 'Test framework to generate code for (default: vitest)',\n },\n context: {\n type: 'string',\n description:\n 'Additional context about the specific violation to generate a more targeted test',\n },\n },\n required: ['invariant_id'],\n },\n },\n {\n suffix: 'feedback',\n description:\n 'Report whether a finding was a true positive or false positive to improve accuracy.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'Invariant ID (e.g., AUTHZ.SERVICE_LAYER.ENFORCED)',\n },\n verdict: {\n type: 'string',\n enum: ['true_positive', 'false_positive'],\n description: 'Whether the finding was a true positive or false positive',\n },\n reason: {\n type: 'string',\n enum: [\n 'not_applicable',\n 'acceptable_risk',\n 'wrong_location',\n 'outdated_pattern',\n 'missing_context',\n ],\n description: 'Reason for the verdict',\n },\n },\n required: ['invariant_id', 'verdict'],\n },\n },\n];\n\n// List available tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: TOOL_PREFIXES.flatMap((prefix) =>\n TOOL_DEFS.map((def) => ({\n name: `${prefix}_${def.suffix}`,\n description: def.description,\n inputSchema: def.inputSchema,\n }))\n ),\n };\n});\n\n// Track last audit result for list_findings\nlet lastAuditResult: AuditResult | null = null;\n\n// Handle tool calls\nserver.setRequestHandler(\n CallToolRequestSchema,\n async (request): Promise<CompatibilityCallToolResult | CreateTaskResult> => {\n const { name, arguments: args } = request.params;\n const tool = normalizeToolName(name);\n if (!tool) {\n return {\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n\n switch (tool) {\n case 'run': {\n const path = (args?.['path'] as string) || process.cwd();\n const only = args?.['only'] as string[] | undefined;\n const skip = args?.['skip'] as string[] | undefined;\n const includeContext = args?.['include_context'] === true;\n const maxFindings =\n typeof args?.['max_findings'] === 'number'\n ? Math.max(1, Math.min(500, args['max_findings'] as number))\n : 200;\n\n try {\n const allowedRoots = parseAllowedRootsFromEnv(process.env, process.cwd());\n const targetPath = resolveAndValidateTargetPath(path, {\n cwd: process.cwd(),\n allowedRoots,\n });\n\n const result = await audit({\n targetPath,\n only,\n skip,\n });\n\n lastAuditResult = result;\n\n // Format findings for LLM consumption\n const findings: Finding[] = result.results.flatMap((r) => r.findings);\n const truncated = findings.length > maxFindings;\n const findingsToReturn = truncated ? findings.slice(0, maxFindings) : findings;\n const readiness = computeReadinessScore(result.summary);\n const summary = {\n total_checks: result.summary.total,\n passed: result.summary.passed,\n failed: result.summary.failed,\n findings_count: findings.length,\n by_severity: result.summary.byPriority,\n truncated,\n max_findings: maxFindings,\n readiness_score: {\n score: readiness.score,\n grade: readiness.grade,\n total: readiness.total,\n passed: readiness.passed,\n failed: readiness.failed,\n hasP0: readiness.hasP0,\n },\n };\n\n const formattedFindings = findingsToReturn.map((f: Finding) => ({\n invariant_id: f.invariantId,\n severity: f.severity,\n message: f.message,\n evidence: formatEvidenceForMcp(f.evidence as EvidenceForMcp[], includeContext),\n required_proof: f.requiredProof,\n suggested_test: f.suggestedTest,\n staff_engineer_asks: getStaffQuestion(f.invariantId),\n }));\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n summary,\n findings: formattedFindings,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: formatMcpRunError(error),\n },\n ],\n isError: true,\n };\n }\n }\n\n case 'list_findings': {\n if (!lastAuditResult) {\n return {\n content: [\n {\n type: 'text',\n text: 'No scan has been run yet. Use scheck_run first.',\n },\n ],\n };\n }\n\n const severity = args?.['severity'] as string | undefined;\n const includeContext = args?.['include_context'] === true;\n const maxFindings =\n typeof args?.['max_findings'] === 'number'\n ? Math.max(1, Math.min(500, args['max_findings'] as number))\n : 200;\n let findings: Finding[] = lastAuditResult.results.flatMap((r) => r.findings);\n\n if (severity) {\n findings = findings.filter((f: Finding) => f.severity === severity);\n }\n\n const truncated = findings.length > maxFindings;\n const findingsToReturn = truncated ? findings.slice(0, maxFindings) : findings;\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n findings: findingsToReturn.map((f: Finding) => ({\n invariant_id: f.invariantId,\n severity: f.severity,\n message: f.message,\n evidence: formatEvidenceForMcp(f.evidence as EvidenceForMcp[], includeContext),\n })),\n meta: {\n total: findings.length,\n truncated,\n max_findings: maxFindings,\n },\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n case 'explain': {\n const invariantId = args?.['invariant_id'] as string;\n const invariant = getInvariantById(invariantId);\n\n if (!invariant) {\n return {\n content: [\n {\n type: 'text',\n text: `Unknown pattern: ${invariantId}. Use scheck_list_invariants to see available patterns.`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n id: invariant.id,\n name: invariant.name,\n severity: invariant.severity,\n category: invariant.category,\n description: invariant.description,\n required_proof: invariant.requiredProof,\n staff_engineer_asks: getStaffQuestion(invariant.id),\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n case 'list_invariants': {\n const category = args?.['category'] as string | undefined;\n const severity = args?.['severity'] as string | undefined;\n\n let invariants = ALL_INVARIANTS;\n\n if (category) {\n invariants = invariants.filter((i) => i.category === category);\n }\n if (severity) {\n invariants = invariants.filter((i) => i.severity === severity);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n invariants.map((i) => ({\n id: i.id,\n name: i.name,\n severity: i.severity,\n category: i.category,\n })),\n null,\n 2\n ),\n },\n ],\n };\n }\n\n case 'generate_test': {\n const invariantId = args?.['invariant_id'] as string;\n const framework = (args?.['framework'] as string) || 'vitest';\n const context = args?.['context'] as string | undefined;\n\n const invariant = getInvariantById(invariantId);\n if (!invariant) {\n return {\n content: [\n {\n type: 'text',\n text: `Unknown invariant: ${invariantId}`,\n },\n ],\n };\n }\n\n const test = generateTestSkeleton(invariant, framework, context);\n\n return {\n content: [\n {\n type: 'text',\n text: test,\n },\n ],\n };\n }\n\n case 'feedback': {\n return handleFeedbackTool(args as Record<string, unknown> | undefined);\n }\n }\n }\n);\n\nfunction normalizeToolName(name: string): ToolSuffix | null {\n const suffix = name.replace(/^scheck_/, '') as ToolSuffix;\n const allowed = new Set<ToolSuffix>([\n 'run',\n 'list_findings',\n 'explain',\n 'list_invariants',\n 'generate_test',\n 'feedback',\n ]);\n return allowed.has(suffix) ? suffix : null;\n}\n\nfunction formatMcpRunError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/no allowed roots are configured and no git repository was detected/i.test(message)) {\n return [\n 'Refusing to scan: no git repository detected and no allowed roots configured.',\n '',\n 'Fix:',\n '- Start the MCP server from inside the repo you want to scan, or',\n '- Set SCHECK_MCP_ALLOWED_ROOTS (or MCP_ALLOWED_ROOTS) in your MCP server config.',\n '',\n 'Example (Claude Code):',\n '{',\n ' \"mcpServers\": {',\n ' \"scheck\": {',\n ' \"command\": \"scheck-mcp\",',\n ' \"env\": { \"SCHECK_MCP_ALLOWED_ROOTS\": \".\" }',\n ' }',\n ' }',\n '}',\n ].join('\\n');\n }\n\n return `Error running audit: ${message}`;\n}\n\n// Start the server\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error('SecurityChecks MCP server (scheck) running on stdio');\n}\n\nmain().catch(console.error);\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/safety.ts","../src/feedback.ts","../src/index.ts"],"names":["path"],"mappings":"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","file":"index.js","sourcesContent":["import path from 'node:path';\nimport fs from 'node:fs';\nimport { execSync } from 'node:child_process';\n\nexport type McpSafetyOptions = {\n cwd: string;\n allowedRoots: string[];\n};\n\nexport function parseAllowedRootsFromEnv(\n env: Record<string, string | undefined>,\n cwd: string\n): string[] {\n const raw =\n env['SCHECK_MCP_ALLOWED_ROOTS'] ??\n env['MCP_ALLOWED_ROOTS'] ??\n env['SCHECK_ALLOWED_ROOTS'];\n\n const roots = (raw ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n .map((p) => path.resolve(cwd, p));\n\n if (roots.length > 0) return roots;\n\n const gitRoot = getGitRoot(cwd);\n if (gitRoot) return [gitRoot];\n\n throw new Error(\n 'Refusing to scan because no allowed roots are configured and no git repository was detected. ' +\n 'Run scheck-mcp from inside a git repo, or set SCHECK_MCP_ALLOWED_ROOTS (or MCP_ALLOWED_ROOTS).'\n );\n}\n\nfunction getGitRoot(cwd: string): string | null {\n try {\n const out = execSync('git rev-parse --show-toplevel', {\n cwd,\n stdio: ['ignore', 'pipe', 'ignore'],\n encoding: 'utf-8',\n }).trim();\n return out.length > 0 ? out : null;\n } catch {\n return null;\n }\n}\n\nfunction isWithinRoot(candidatePath: string, root: string): boolean {\n const relative = path.relative(root, candidatePath);\n // `path.relative()` returns '' when equal. Equality should be allowed.\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nexport function resolveAndValidateTargetPath(\n requestedPath: string | undefined,\n options: McpSafetyOptions\n): string {\n const { cwd, allowedRoots } = options;\n const input = (requestedPath && requestedPath.trim().length > 0)\n ? requestedPath\n : cwd;\n\n const resolved = path.resolve(cwd, input);\n\n if (!fs.existsSync(resolved)) {\n throw new Error(`Target path does not exist: ${resolved}`);\n }\n\n // Fail closed on symlink escapes: validate based on real paths, not just `path.resolve`.\n const realResolved = fs.realpathSync(resolved);\n const realAllowedRoots = allowedRoots.map((r) => fs.realpathSync(path.resolve(cwd, r)));\n\n const allowed = realAllowedRoots.some((root) => isWithinRoot(realResolved, root));\n\n if (!allowed) {\n const rootsList = realAllowedRoots.join(', ');\n throw new Error(\n `Refusing to scan outside allowed roots. Requested: ${realResolved}. Allowed roots: ${rootsList}. ` +\n `Set SCHECK_MCP_ALLOWED_ROOTS (or MCP_ALLOWED_ROOTS) to override.`\n );\n }\n\n return realResolved;\n}\n\nexport type EvidenceForMcp = { file: string; line: number; context?: string };\n\nexport function formatEvidenceForMcp(\n evidence: EvidenceForMcp[],\n includeContext: boolean\n): EvidenceForMcp[] {\n if (includeContext) return evidence;\n return evidence.map(({ file, line }) => ({ file, line }));\n}\n","export type FeedbackVerdict = 'true_positive' | 'false_positive';\n\nexport interface FeedbackToolArgs {\n invariant_id?: string;\n verdict?: string;\n reason?: string;\n}\n\nexport interface FeedbackToolResponse {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n}\n\n// Keep tool responses aligned with the MCP SDK result types.\n// (Our response object is a valid CallToolResult / CompatibilityCallToolResult.)\nexport type McpToolResult = import('@modelcontextprotocol/sdk/types.js').CompatibilityCallToolResult;\n\nexport async function handleFeedbackTool(\n args: FeedbackToolArgs | undefined,\n options?: {\n cwd?: string;\n now?: () => Date;\n fetchFn?: typeof fetch;\n }\n): Promise<McpToolResult> {\n const invariantId = args?.invariant_id;\n const verdict = args?.verdict as FeedbackVerdict | undefined;\n const reason = args?.reason;\n\n if (!invariantId || !verdict) {\n return {\n content: [\n {\n type: 'text',\n text: 'Error: invariant_id and verdict are required.',\n },\n ],\n isError: true,\n };\n }\n\n if (!['true_positive', 'false_positive'].includes(verdict)) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: verdict must be \"true_positive\" or \"false_positive\", got \"${verdict}\"`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const { writeFileSync, readFileSync, existsSync, mkdirSync, lstatSync } = await import('fs');\n const { join } = await import('path');\n const cwd = options?.cwd ?? process.cwd();\n const feedbackDir = join(cwd, '.scheck');\n const feedbackFile = join(feedbackDir, 'feedback.json');\n const now = options?.now ?? (() => new Date());\n\n if (!existsSync(feedbackDir)) {\n mkdirSync(feedbackDir, { recursive: true });\n } else {\n const st = lstatSync(feedbackDir);\n // Avoid writing through malicious symlinks (e.g., repo-controlled `.scheck` -> /etc).\n if (st.isSymbolicLink()) {\n throw new Error('Refusing to write feedback: .scheck is a symlink');\n }\n if (!st.isDirectory()) {\n throw new Error('Refusing to write feedback: .scheck is not a directory');\n }\n }\n\n if (existsSync(feedbackFile)) {\n const st = lstatSync(feedbackFile);\n if (st.isSymbolicLink()) {\n throw new Error('Refusing to write feedback: feedback.json is a symlink');\n }\n if (!st.isFile()) {\n throw new Error('Refusing to write feedback: feedback.json is not a file');\n }\n }\n\n let feedbackData: Array<{\n invariantId: string;\n verdict: string;\n reason?: string;\n timestamp: string;\n }> = [];\n if (existsSync(feedbackFile)) {\n try {\n feedbackData = JSON.parse(readFileSync(feedbackFile, 'utf-8'));\n } catch {\n feedbackData = [];\n }\n }\n\n feedbackData.push({\n invariantId,\n verdict,\n reason,\n timestamp: now().toISOString(),\n });\n\n writeFileSync(feedbackFile, JSON.stringify(feedbackData, null, 2));\n\n try {\n const clientVersion = process.env['MCP_VERSION'] ?? '0.0.0-dev';\n const endpoint = 'https://api.securitychecks.ai/v1/feedback';\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 3000);\n const fetchFn = options?.fetchFn ?? (typeof fetch === 'function' ? fetch : undefined);\n if (fetchFn) {\n fetchFn(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ invariantId, verdict, reason, clientVersion }),\n signal: controller.signal,\n })\n .catch(() => {})\n .finally(() => clearTimeout(timeoutId));\n } else {\n clearTimeout(timeoutId);\n }\n } catch {\n // Silent failure for API reporting\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n recorded: true,\n invariantId,\n verdict,\n reason: reason ?? null,\n storedLocally: true,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error recording feedback: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n}\n","#!/usr/bin/env node\n\n/**\n * SecurityChecks MCP Server (scheck)\n *\n * Backend security verification with evidence.\n * MCP server that exposes scheck tools for LLM integration.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { CompatibilityCallToolResult, CreateTaskResult } from '@modelcontextprotocol/sdk/types.js';\nimport {\n getInvariantById,\n ALL_INVARIANTS,\n type AuditResult,\n type Finding,\n} from '@securitychecks/collector';\nimport { audit, computeReadinessScore } from '@securitychecks/cli';\nimport {\n formatEvidenceForMcp,\n parseAllowedRootsFromEnv,\n resolveAndValidateTargetPath,\n type EvidenceForMcp,\n} from './safety.js';\nimport { generateTestSkeleton, getStaffQuestion, generateFixPrompt } from '@securitychecks/cli';\nimport { handleFeedbackTool } from './feedback.js';\n\n// Version injected at build time via tsup define\nconst version = process.env['MCP_VERSION'] ?? '0.0.0-dev';\n\nconst server = new Server(\n {\n name: 'scheck',\n version: version,\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\nconst TOOL_PREFIXES = ['scheck'] as const;\ntype ToolSuffix = 'run' | 'list_findings' | 'explain' | 'list_invariants' | 'generate_test' | 'feedback' | 'fix_prompt';\ntype ToolDef = {\n suffix: ToolSuffix;\n description: string;\n inputSchema: unknown;\n};\n\nconst TOOL_DEFS: ToolDef[] = [\n {\n suffix: 'run',\n description:\n 'Run security checks on the codebase. Verifies auth enforcement, injection safety, access control, ' +\n 'webhook idempotency, and more.',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Target path to audit (default: current directory)',\n },\n include_context: {\n type: 'boolean',\n description:\n 'Include code context snippets in results (may expose source code to the assistant).',\n },\n max_findings: {\n type: 'integer',\n minimum: 1,\n maximum: 500,\n description: 'Limit number of findings returned (default: 200)',\n },\n only: {\n type: 'array',\n items: { type: 'string' },\n description: 'Only run specific invariant checks by ID',\n },\n skip: {\n type: 'array',\n items: { type: 'string' },\n description: 'Skip specific invariant checks by ID',\n },\n },\n },\n },\n {\n suffix: 'list_findings',\n description:\n 'List issues a staff engineer would flag — current findings from the last run, by severity.',\n inputSchema: {\n type: 'object',\n properties: {\n severity: {\n type: 'string',\n enum: ['P0', 'P1', 'P2'],\n description: 'Filter findings by severity',\n },\n include_context: {\n type: 'boolean',\n description:\n 'Include code context snippets in results (may expose source code to the assistant).',\n },\n max_findings: {\n type: 'integer',\n minimum: 1,\n maximum: 500,\n description: 'Limit number of findings returned (default: 200)',\n },\n },\n },\n },\n {\n suffix: 'explain',\n description:\n 'What a staff engineer checks: explain why a pattern matters, real incidents it prevents, and proof needed.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'The invariant ID to explain (e.g., AUTHZ.SERVICE_LAYER.ENFORCED)',\n },\n },\n required: ['invariant_id'],\n },\n },\n {\n suffix: 'list_invariants',\n description:\n 'List all patterns a staff engineer checks for — the patterns that prevent production incidents.',\n inputSchema: {\n type: 'object',\n properties: {\n category: {\n type: 'string',\n description: 'Filter by category (authz, revocation, webhooks, transactions, etc.)',\n },\n severity: {\n type: 'string',\n enum: ['P0', 'P1', 'P2'],\n description: 'Filter by severity',\n },\n },\n },\n },\n {\n suffix: 'generate_test',\n description:\n 'Generate test code that proves a pattern is enforced — the proof a staff engineer would ask for.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'The invariant ID to generate a test for',\n },\n framework: {\n type: 'string',\n enum: ['jest', 'vitest', 'playwright'],\n description: 'Test framework to generate code for (default: vitest)',\n },\n context: {\n type: 'string',\n description:\n 'Additional context about the specific violation to generate a more targeted test',\n },\n },\n required: ['invariant_id'],\n },\n },\n {\n suffix: 'feedback',\n description:\n 'Report whether a finding was a true positive or false positive to improve accuracy.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'Invariant ID (e.g., AUTHZ.SERVICE_LAYER.ENFORCED)',\n },\n verdict: {\n type: 'string',\n enum: ['true_positive', 'false_positive'],\n description: 'Whether the finding was a true positive or false positive',\n },\n reason: {\n type: 'string',\n enum: [\n 'not_applicable',\n 'acceptable_risk',\n 'wrong_location',\n 'outdated_pattern',\n 'missing_context',\n ],\n description: 'Reason for the verdict',\n },\n },\n required: ['invariant_id', 'verdict'],\n },\n },\n {\n suffix: 'fix_prompt',\n description:\n 'Generate an AI fix prompt for a specific invariant — a structured prompt you can paste into any AI coding assistant to fix the issue.',\n inputSchema: {\n type: 'object',\n properties: {\n invariant_id: {\n type: 'string',\n description: 'The invariant ID to generate a fix prompt for',\n },\n file: {\n type: 'string',\n description: 'File where the violation was found',\n },\n line: {\n type: 'integer',\n description: 'Line number of the violation',\n },\n message: {\n type: 'string',\n description: 'Finding message or description',\n },\n framework: {\n type: 'string',\n enum: ['jest', 'vitest', 'playwright'],\n description: 'Test framework for the proof test (default: vitest)',\n },\n },\n required: ['invariant_id'],\n },\n },\n];\n\n// List available tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: TOOL_PREFIXES.flatMap((prefix) =>\n TOOL_DEFS.map((def) => ({\n name: `${prefix}_${def.suffix}`,\n description: def.description,\n inputSchema: def.inputSchema,\n }))\n ),\n };\n});\n\n// Track last audit result for list_findings\nlet lastAuditResult: AuditResult | null = null;\n\n// Handle tool calls\nserver.setRequestHandler(\n CallToolRequestSchema,\n async (request): Promise<CompatibilityCallToolResult | CreateTaskResult> => {\n const { name, arguments: args } = request.params;\n const tool = normalizeToolName(name);\n if (!tool) return textResult(`Unknown tool: ${name}`, true);\n\n switch (tool) {\n case 'run': {\n const path = (args?.['path'] as string) || process.cwd();\n const only = args?.['only'] as string[] | undefined;\n const skip = args?.['skip'] as string[] | undefined;\n const includeContext = args?.['include_context'] === true;\n const maxFindings =\n typeof args?.['max_findings'] === 'number'\n ? Math.max(1, Math.min(500, args['max_findings'] as number))\n : 200;\n\n try {\n const allowedRoots = parseAllowedRootsFromEnv(process.env, process.cwd());\n const targetPath = resolveAndValidateTargetPath(path, {\n cwd: process.cwd(),\n allowedRoots,\n });\n\n const result = await audit({\n targetPath,\n only,\n skip,\n });\n\n lastAuditResult = result;\n\n // Format findings for LLM consumption\n const findings: Finding[] = result.results.flatMap((r) => r.findings);\n const truncated = findings.length > maxFindings;\n const findingsToReturn = truncated ? findings.slice(0, maxFindings) : findings;\n const readiness = computeReadinessScore(result.summary);\n const summary = {\n total_checks: result.summary.total,\n passed: result.summary.passed,\n failed: result.summary.failed,\n findings_count: findings.length,\n by_severity: result.summary.byPriority,\n truncated,\n max_findings: maxFindings,\n readiness_score: {\n score: readiness.score,\n grade: readiness.grade,\n total: readiness.total,\n passed: readiness.passed,\n failed: readiness.failed,\n hasP0: readiness.hasP0,\n },\n };\n\n const formattedFindings = findingsToReturn.map((f: Finding) => ({\n invariant_id: f.invariantId,\n severity: f.severity,\n message: f.message,\n evidence: formatEvidenceForMcp(f.evidence as EvidenceForMcp[], includeContext),\n required_proof: f.requiredProof,\n suggested_test: f.suggestedTest,\n staff_engineer_asks: getStaffQuestion(f.invariantId),\n ai_fix_prompt: generateFixPrompt(\n getInvariantById(f.invariantId) ?? { id: f.invariantId, name: f.invariantId },\n {\n file: (f.evidence as EvidenceForMcp[])?.[0]?.file,\n line: (f.evidence as EvidenceForMcp[])?.[0]?.line,\n message: f.message,\n }\n ),\n }));\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n summary,\n findings: formattedFindings,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: formatMcpRunError(error),\n },\n ],\n isError: true,\n };\n }\n }\n\n case 'list_findings': {\n if (!lastAuditResult) return textResult('No scan has been run yet. Use scheck_run first.');\n\n const severity = args?.['severity'] as string | undefined;\n const includeContext = args?.['include_context'] === true;\n const maxFindings =\n typeof args?.['max_findings'] === 'number'\n ? Math.max(1, Math.min(500, args['max_findings'] as number))\n : 200;\n let findings: Finding[] = lastAuditResult.results.flatMap((r) => r.findings);\n\n if (severity) {\n findings = findings.filter((f: Finding) => f.severity === severity);\n }\n\n const truncated = findings.length > maxFindings;\n const findingsToReturn = truncated ? findings.slice(0, maxFindings) : findings;\n\n return jsonResult({\n findings: findingsToReturn.map((f: Finding) => ({\n invariant_id: f.invariantId, severity: f.severity,\n message: f.message, evidence: formatEvidenceForMcp(f.evidence as EvidenceForMcp[], includeContext),\n })),\n meta: { total: findings.length, truncated, max_findings: maxFindings },\n });\n }\n\n case 'explain': {\n const invariantId = args?.['invariant_id'] as string;\n const invariant = getInvariantById(invariantId);\n if (!invariant) {\n return textResult(`Unknown pattern: ${invariantId}. Use scheck_list_invariants to see available patterns.`);\n }\n return jsonResult({\n id: invariant.id, name: invariant.name, severity: invariant.severity,\n category: invariant.category, description: invariant.description,\n required_proof: invariant.requiredProof, staff_engineer_asks: getStaffQuestion(invariant.id),\n });\n }\n\n case 'list_invariants': {\n const category = args?.['category'] as string | undefined;\n const severity = args?.['severity'] as string | undefined;\n let invariants = ALL_INVARIANTS;\n if (category) invariants = invariants.filter((i) => i.category === category);\n if (severity) invariants = invariants.filter((i) => i.severity === severity);\n return jsonResult(invariants.map((i) => ({\n id: i.id, name: i.name, severity: i.severity, category: i.category,\n })));\n }\n\n case 'generate_test': {\n const invariantId = args?.['invariant_id'] as string;\n const invariant = getInvariantById(invariantId);\n if (!invariant) return textResult(`Unknown invariant: ${invariantId}`);\n return textResult(generateTestSkeleton(invariant, (args?.['framework'] as string) || 'vitest', args?.['context'] as string | undefined));\n }\n\n case 'feedback': {\n return handleFeedbackTool(args as Record<string, unknown> | undefined);\n }\n\n case 'fix_prompt': {\n const invariantId = args?.['invariant_id'] as string;\n const invariant = getInvariantById(invariantId);\n if (!invariant) return textResult(`Unknown invariant: ${invariantId}. Use scheck_list_invariants to see available invariants.`);\n return textResult(generateFixPrompt(invariant, {\n file: args?.['file'] as string | undefined,\n line: typeof args?.['line'] === 'number' ? (args['line'] as number) : undefined,\n message: args?.['message'] as string | undefined,\n framework: (args?.['framework'] as string) || 'vitest',\n }));\n }\n }\n }\n);\n\nfunction textResult(text: string, isError?: boolean): CompatibilityCallToolResult {\n return { content: [{ type: 'text', text }], ...(isError ? { isError: true } : {}) };\n}\n\nfunction jsonResult(data: unknown): CompatibilityCallToolResult {\n return textResult(JSON.stringify(data, null, 2));\n}\n\nfunction normalizeToolName(name: string): ToolSuffix | null {\n const suffix = name.replace(/^scheck_/, '') as ToolSuffix;\n const allowed = new Set<ToolSuffix>([\n 'run',\n 'list_findings',\n 'explain',\n 'list_invariants',\n 'generate_test',\n 'feedback',\n 'fix_prompt',\n ]);\n return allowed.has(suffix) ? suffix : null;\n}\n\nfunction formatMcpRunError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/no allowed roots are configured and no git repository was detected/i.test(message)) {\n return [\n 'Refusing to scan: no git repository detected and no allowed roots configured.',\n '',\n 'Fix:',\n '- Start the MCP server from inside the repo you want to scan, or',\n '- Set SCHECK_MCP_ALLOWED_ROOTS (or MCP_ALLOWED_ROOTS) in your MCP server config.',\n '',\n 'Example (Claude Code):',\n '{',\n ' \"mcpServers\": {',\n ' \"scheck\": {',\n ' \"command\": \"scheck-mcp\",',\n ' \"env\": { \"SCHECK_MCP_ALLOWED_ROOTS\": \".\" }',\n ' }',\n ' }',\n '}',\n ].join('\\n');\n }\n\n return `Error running audit: ${message}`;\n}\n\n// Start the server\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error('SecurityChecks MCP server (scheck) running on stdio');\n}\n\nmain().catch(console.error);\n"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@securitychecks/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for SecurityChecks -
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "MCP server for SecurityChecks - verify security invariants via AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"author": "SecurityChecks <
|
|
7
|
+
"author": "SecurityChecks <support@securitychecks.ai>",
|
|
8
8
|
"homepage": "https://securitychecks.ai",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
36
|
-
"@securitychecks/cli": "0.
|
|
37
|
-
"@securitychecks/collector": "0.
|
|
36
|
+
"@securitychecks/cli": "0.4.0",
|
|
37
|
+
"@securitychecks/collector": "0.4.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^25.1.0",
|