@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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # @securitychecks/mcp
2
2
 
3
- > **Review AI-generated code with AI** — MCP server for production-ready code review.
3
+ > **Security verification for AI-assisted development** — MCP server for backend security invariant checks.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@securitychecks/mcp.svg?style=flat-square)](https://www.npmjs.com/package/@securitychecks/mcp)
6
6
 
7
- MCP server that lets Claude catch what Copilot misseswebhook idempotency, service-layer auth, transaction safety.
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.2.2";
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.2.2";
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 scheck \u2014 the patterns a senior engineer would flag in review. Catches webhook idempotency, auth at service layer, transaction safety, and more.",
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
- content: [
458
- {
459
- type: "text",
460
- text: JSON.stringify(
461
- {
462
- findings: findingsToReturn.map((f) => ({
463
- invariant_id: f.invariantId,
464
- severity: f.severity,
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
- content: [
496
- {
497
- type: "text",
498
- text: JSON.stringify(
499
- {
500
- id: invariant.id,
501
- name: invariant.name,
502
- severity: invariant.severity,
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
- invariants = invariants.filter((i) => i.category === category);
521
- }
522
- if (severity) {
523
- invariants = invariants.filter((i) => i.severity === severity);
524
- }
525
- return {
526
- content: [
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
- return {
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.2.2",
4
- "description": "MCP server for SecurityChecks - expose scheck tools to AI assistants",
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 <hello@securitychecks.ai>",
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.2.2",
37
- "@securitychecks/collector": "0.2.2"
36
+ "@securitychecks/cli": "0.4.0",
37
+ "@securitychecks/collector": "0.4.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^25.1.0",