vibecheck-mcp-server 2.0.1
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 +191 -0
- package/agent-checkpoint.js +364 -0
- package/architect-tools.js +707 -0
- package/audit-mcp.js +206 -0
- package/codebase-architect-tools.js +838 -0
- package/guardrail-2.0-tools.js +748 -0
- package/guardrail-tools.js +1075 -0
- package/hygiene-tools.js +428 -0
- package/index-v1.js +698 -0
- package/index.js +1409 -0
- package/index.old.js +4137 -0
- package/intelligence-tools.js +664 -0
- package/intent-drift-tools.js +873 -0
- package/mdc-generator.js +298 -0
- package/package.json +47 -0
- package/premium-tools.js +1275 -0
- package/test-mcp.js +108 -0
- package/test-tools.js +36 -0
- package/tier-auth.js +147 -0
package/index-v1.js
ADDED
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GUARDRAIL MCP Server - Consolidated Interface
|
|
5
|
+
*
|
|
6
|
+
* 7 Public Tools (New Surface):
|
|
7
|
+
* guardrail.scan - Find truth (integrity, security, hygiene, contracts)
|
|
8
|
+
* guardrail.gate - Enforce truth in CI (fail builds on blockers)
|
|
9
|
+
* guardrail.fix - Apply safe patches (plan -> apply)
|
|
10
|
+
* guardrail.proof - Premium verification (mocks, reality)
|
|
11
|
+
* guardrail.report - Access artifacts and export reports
|
|
12
|
+
* guardrail.policy - View/modify policy configuration
|
|
13
|
+
* guardrail.status - Health, versions, config info
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
17
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
|
+
import {
|
|
19
|
+
CallToolRequestSchema,
|
|
20
|
+
ListToolsRequestSchema,
|
|
21
|
+
ListResourcesRequestSchema,
|
|
22
|
+
ReadResourceRequestSchema,
|
|
23
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
24
|
+
|
|
25
|
+
import fs from "fs/promises";
|
|
26
|
+
import path from "path";
|
|
27
|
+
import { fileURLToPath } from "url";
|
|
28
|
+
import { createRequire } from "module";
|
|
29
|
+
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
+
const __dirname = path.dirname(__filename);
|
|
33
|
+
|
|
34
|
+
const VERSION = "2.0.0";
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// TOOL DEFINITIONS
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
const TOOLS = [
|
|
41
|
+
{
|
|
42
|
+
name: "guardrail.scan",
|
|
43
|
+
description:
|
|
44
|
+
"๐ฎ Find truth. Runs comprehensive integrity, hygiene, and security checks.",
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: "object",
|
|
47
|
+
properties: {
|
|
48
|
+
projectPath: { type: "string", default: "." },
|
|
49
|
+
profile: {
|
|
50
|
+
type: "string",
|
|
51
|
+
enum: ["default", "ship", "ci"],
|
|
52
|
+
description: "Scan profile. 'ship'/'ci' includes all checks.",
|
|
53
|
+
default: "default",
|
|
54
|
+
},
|
|
55
|
+
only: {
|
|
56
|
+
type: "array",
|
|
57
|
+
items: { type: "string" },
|
|
58
|
+
description:
|
|
59
|
+
"Run specific checks: 'integrity', 'hygiene', 'security', 'auth', 'routes', 'contracts', 'mocks'",
|
|
60
|
+
},
|
|
61
|
+
format: {
|
|
62
|
+
type: "string",
|
|
63
|
+
enum: ["text", "json", "markdown"],
|
|
64
|
+
default: "markdown",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "guardrail.gate",
|
|
71
|
+
description:
|
|
72
|
+
"๐ฆ Enforce truth. CI blocker mode that fails on policy violations.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
projectPath: { type: "string", default: "." },
|
|
77
|
+
policy: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Policy profile: default, strict, ci",
|
|
80
|
+
default: "strict",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "guardrail.fix",
|
|
87
|
+
description: "๐ง Make truth real. Apply safe patches and scaffolds.",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
projectPath: { type: "string", default: "." },
|
|
92
|
+
mode: {
|
|
93
|
+
type: "string",
|
|
94
|
+
enum: ["plan", "apply"],
|
|
95
|
+
description: "Preview fixes (plan) or apply them (apply)",
|
|
96
|
+
default: "plan",
|
|
97
|
+
},
|
|
98
|
+
targets: {
|
|
99
|
+
type: "array",
|
|
100
|
+
items: { type: "string" },
|
|
101
|
+
description: "Limit fixes to: secrets, auth, mock, config",
|
|
102
|
+
},
|
|
103
|
+
ai: {
|
|
104
|
+
type: "boolean",
|
|
105
|
+
description: "Use AI to generate fix suggestions",
|
|
106
|
+
default: false,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "guardrail.proof",
|
|
113
|
+
description:
|
|
114
|
+
"๐งช Premium verification. Detect mocks and verify runtime reality.",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
projectPath: { type: "string", default: "." },
|
|
119
|
+
mode: {
|
|
120
|
+
type: "string",
|
|
121
|
+
enum: ["mocks", "reality"],
|
|
122
|
+
description:
|
|
123
|
+
"mocks: static mock detection. reality: runtime verification.",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
required: ["mode"],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "guardrail.report",
|
|
131
|
+
description: "๐ Access scan artifacts and export reports.",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
projectPath: { type: "string", default: "." },
|
|
136
|
+
runId: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Specific run ID (defaults to latest)",
|
|
139
|
+
},
|
|
140
|
+
format: {
|
|
141
|
+
type: "string",
|
|
142
|
+
enum: ["md", "html", "json"],
|
|
143
|
+
default: "md",
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "guardrail.policy",
|
|
150
|
+
description: "โ๏ธ View and modify policy configuration.",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
projectPath: { type: "string", default: "." },
|
|
155
|
+
action: {
|
|
156
|
+
type: "string",
|
|
157
|
+
enum: ["get", "set", "allowlist", "ignore", "reset"],
|
|
158
|
+
default: "get",
|
|
159
|
+
},
|
|
160
|
+
key: { type: "string", description: "Config key to get/set" },
|
|
161
|
+
value: { type: "string", description: "Value to set" },
|
|
162
|
+
target: { type: "string", description: "Target for allowlist/ignore" },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "guardrail.status",
|
|
168
|
+
description: "๐ Server status, health, versions, and config info.",
|
|
169
|
+
inputSchema: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
projectPath: { type: "string", default: "." },
|
|
173
|
+
verbose: { type: "boolean", default: false },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// TOOL HANDLERS
|
|
181
|
+
// ============================================================================
|
|
182
|
+
|
|
183
|
+
class GuardrailMCP {
|
|
184
|
+
constructor() {
|
|
185
|
+
this.server = new Server(
|
|
186
|
+
{ name: "guardrail", version: VERSION },
|
|
187
|
+
{ capabilities: { tools: {}, resources: {} } },
|
|
188
|
+
);
|
|
189
|
+
this.setupHandlers();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
setupHandlers() {
|
|
193
|
+
// List tools
|
|
194
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
195
|
+
tools: TOOLS,
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
// Call tool
|
|
199
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
200
|
+
const { name, arguments: args } = request.params;
|
|
201
|
+
const projectPath = path.resolve(args?.projectPath || ".");
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
switch (name) {
|
|
205
|
+
case "guardrail.scan":
|
|
206
|
+
return await this.handleScan(projectPath, args);
|
|
207
|
+
case "guardrail.gate":
|
|
208
|
+
return await this.handleGate(projectPath, args);
|
|
209
|
+
case "guardrail.fix":
|
|
210
|
+
return await this.handleFix(projectPath, args);
|
|
211
|
+
case "guardrail.proof":
|
|
212
|
+
return await this.handleProof(projectPath, args);
|
|
213
|
+
case "guardrail.report":
|
|
214
|
+
return await this.handleReport(projectPath, args);
|
|
215
|
+
case "guardrail.policy":
|
|
216
|
+
return await this.handlePolicy(projectPath, args);
|
|
217
|
+
case "guardrail.status":
|
|
218
|
+
return await this.handleStatus(projectPath, args);
|
|
219
|
+
|
|
220
|
+
// Legacy mappings
|
|
221
|
+
case "guardrail.hygiene":
|
|
222
|
+
return await this.handleScan(projectPath, {
|
|
223
|
+
...args,
|
|
224
|
+
only: ["hygiene"],
|
|
225
|
+
});
|
|
226
|
+
case "guardrail.security":
|
|
227
|
+
return await this.handleScan(projectPath, {
|
|
228
|
+
...args,
|
|
229
|
+
only: ["security"],
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
default:
|
|
233
|
+
return this.error(`Unknown tool: ${name}`);
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
return this.error(`${name} failed: ${err.message}`);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Resources (unchanged)
|
|
241
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
242
|
+
resources: [
|
|
243
|
+
{
|
|
244
|
+
uri: "guardrail://config",
|
|
245
|
+
name: "Guardrail Config",
|
|
246
|
+
description: "Current guardrail configuration",
|
|
247
|
+
mimeType: "application/json",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
uri: "guardrail://summary",
|
|
251
|
+
name: "Last Scan Summary",
|
|
252
|
+
description: "Summary of the last scan",
|
|
253
|
+
mimeType: "application/json",
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
}));
|
|
257
|
+
|
|
258
|
+
this.server.setRequestHandler(
|
|
259
|
+
ReadResourceRequestSchema,
|
|
260
|
+
async (request) => {
|
|
261
|
+
const { uri } = request.params;
|
|
262
|
+
const projectPath = process.cwd();
|
|
263
|
+
|
|
264
|
+
if (uri === "guardrail://config") {
|
|
265
|
+
const configPath = path.join(projectPath, ".guardrailrc");
|
|
266
|
+
try {
|
|
267
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
268
|
+
return {
|
|
269
|
+
contents: [{ uri, mimeType: "application/json", text: content }],
|
|
270
|
+
};
|
|
271
|
+
} catch {
|
|
272
|
+
return {
|
|
273
|
+
contents: [{ uri, mimeType: "application/json", text: "{}" }],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (uri === "guardrail://summary") {
|
|
279
|
+
const summaryPath = path.join(
|
|
280
|
+
projectPath,
|
|
281
|
+
".guardrail",
|
|
282
|
+
"summary.json",
|
|
283
|
+
);
|
|
284
|
+
try {
|
|
285
|
+
const content = await fs.readFile(summaryPath, "utf-8");
|
|
286
|
+
return {
|
|
287
|
+
contents: [{ uri, mimeType: "application/json", text: content }],
|
|
288
|
+
};
|
|
289
|
+
} catch {
|
|
290
|
+
return {
|
|
291
|
+
contents: [
|
|
292
|
+
{
|
|
293
|
+
uri,
|
|
294
|
+
mimeType: "application/json",
|
|
295
|
+
text: '{"error": "No scan found"}',
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { contents: [] };
|
|
303
|
+
},
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Helper methods
|
|
308
|
+
success(text) {
|
|
309
|
+
return { content: [{ type: "text", text }] };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
error(text) {
|
|
313
|
+
return { content: [{ type: "text", text: `โ ${text}` }], isError: true };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// SCAN
|
|
318
|
+
// ============================================================================
|
|
319
|
+
async handleScan(projectPath, args) {
|
|
320
|
+
const profile = args?.profile || "default";
|
|
321
|
+
let only = args?.only || [];
|
|
322
|
+
const format = args?.format || "markdown";
|
|
323
|
+
|
|
324
|
+
let output = "# ๐ฎ Guardrail Scan\n\n";
|
|
325
|
+
output += `**Profile:** ${profile}\n`;
|
|
326
|
+
if (only.length > 0) output += `**Only:** ${only.join(", ")}\n`;
|
|
327
|
+
output += "\n";
|
|
328
|
+
|
|
329
|
+
// Determine checks to run
|
|
330
|
+
let checksToRun = ["integrity"];
|
|
331
|
+
if (profile === "ship" || profile === "ci") {
|
|
332
|
+
checksToRun = ["integrity", "hygiene", "security"];
|
|
333
|
+
}
|
|
334
|
+
if (only.length > 0) {
|
|
335
|
+
checksToRun = [];
|
|
336
|
+
if (only.includes("integrity")) checksToRun.push("integrity");
|
|
337
|
+
if (only.includes("hygiene")) checksToRun.push("hygiene");
|
|
338
|
+
if (only.includes("security")) checksToRun.push("security");
|
|
339
|
+
if (only.includes("contracts") || only.includes("api"))
|
|
340
|
+
checksToRun.push("api");
|
|
341
|
+
if (only.includes("auth")) checksToRun.push("auth");
|
|
342
|
+
if (only.includes("routes")) checksToRun.push("routes");
|
|
343
|
+
if (only.includes("mocks")) checksToRun.push("mock");
|
|
344
|
+
|
|
345
|
+
// Fallback if specific integrity sub-checks are requested but integrity not explicit
|
|
346
|
+
if (
|
|
347
|
+
["api", "auth", "secrets", "routes", "mock"].some((c) =>
|
|
348
|
+
only.includes(c),
|
|
349
|
+
) &&
|
|
350
|
+
!checksToRun.includes("integrity")
|
|
351
|
+
) {
|
|
352
|
+
checksToRun.push("integrity");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const results = {
|
|
357
|
+
score: 100,
|
|
358
|
+
grade: "A",
|
|
359
|
+
canShip: true,
|
|
360
|
+
deductions: [],
|
|
361
|
+
blockers: [],
|
|
362
|
+
counts: {},
|
|
363
|
+
checks: {},
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// 1. Integrity Check (Main)
|
|
367
|
+
if (
|
|
368
|
+
checksToRun.includes("integrity") ||
|
|
369
|
+
checksToRun.some((c) => ["api", "auth", "routes", "mock"].includes(c))
|
|
370
|
+
) {
|
|
371
|
+
try {
|
|
372
|
+
const { auditProductionIntegrity } = require(
|
|
373
|
+
path.join(
|
|
374
|
+
__dirname,
|
|
375
|
+
"..",
|
|
376
|
+
"scripts",
|
|
377
|
+
"audit-production-integrity.js",
|
|
378
|
+
),
|
|
379
|
+
);
|
|
380
|
+
const { results: integrityResults, integrity } =
|
|
381
|
+
await auditProductionIntegrity(projectPath);
|
|
382
|
+
|
|
383
|
+
results.score = integrity.score;
|
|
384
|
+
results.grade = integrity.grade;
|
|
385
|
+
results.canShip = integrity.canShip;
|
|
386
|
+
results.deductions = integrity.deductions;
|
|
387
|
+
|
|
388
|
+
// Build summary
|
|
389
|
+
output += `## Integrity Score: ${integrity.score}/100 (${integrity.grade})\n\n`;
|
|
390
|
+
|
|
391
|
+
// Add specific check results if requested
|
|
392
|
+
if (
|
|
393
|
+
only.length === 0 ||
|
|
394
|
+
only.includes("integrity") ||
|
|
395
|
+
only.includes("api")
|
|
396
|
+
) {
|
|
397
|
+
output += `- **API Wiring:** ${integrityResults.api?.summary?.missingBackend || 0} missing endpoints\n`;
|
|
398
|
+
}
|
|
399
|
+
if (
|
|
400
|
+
only.length === 0 ||
|
|
401
|
+
only.includes("integrity") ||
|
|
402
|
+
only.includes("auth")
|
|
403
|
+
) {
|
|
404
|
+
output += `- **Auth Coverage:** ${integrityResults.auth?.analysis?.sensitiveUnprotected?.length || 0} issues\n`;
|
|
405
|
+
}
|
|
406
|
+
if (
|
|
407
|
+
only.length === 0 ||
|
|
408
|
+
only.includes("integrity") ||
|
|
409
|
+
only.includes("mocks")
|
|
410
|
+
) {
|
|
411
|
+
output += `- **Mock Code:** ${integrityResults.mocks?.issues?.filter((i) => i.severity === "critical").length || 0} blockers\n`;
|
|
412
|
+
}
|
|
413
|
+
} catch (err) {
|
|
414
|
+
output += `โ ๏ธ Integrity check failed: ${err.message}\n`;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// 2. Hygiene Check
|
|
419
|
+
if (checksToRun.includes("hygiene")) {
|
|
420
|
+
try {
|
|
421
|
+
const {
|
|
422
|
+
findDuplicates,
|
|
423
|
+
findUnusedFiles,
|
|
424
|
+
collectAllErrors,
|
|
425
|
+
calculateHygieneScore,
|
|
426
|
+
} = require(path.join(__dirname, "..", "scripts", "hygiene"));
|
|
427
|
+
|
|
428
|
+
const hygieneResults = {
|
|
429
|
+
duplicates: findDuplicates(projectPath),
|
|
430
|
+
unused: findUnusedFiles(projectPath),
|
|
431
|
+
errors: collectAllErrors(projectPath),
|
|
432
|
+
};
|
|
433
|
+
const hygieneScore = calculateHygieneScore(hygieneResults);
|
|
434
|
+
|
|
435
|
+
output += `\n## Hygiene Score: ${hygieneScore.score}/100\n`;
|
|
436
|
+
output += `- **Duplicates:** ${hygieneResults.duplicates?.exact?.length || 0}\n`;
|
|
437
|
+
output += `- **Unused Files:** ${hygieneResults.unused?.unused?.definitelyUnused?.length || 0}\n`;
|
|
438
|
+
output += `- **Errors:** ${hygieneResults.errors?.summary?.total || 0}\n`;
|
|
439
|
+
} catch (err) {
|
|
440
|
+
output += `โ ๏ธ Hygiene check failed: ${err.message}\n`;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// 3. Security Check
|
|
445
|
+
if (checksToRun.includes("security")) {
|
|
446
|
+
try {
|
|
447
|
+
const { auditEnvSecrets } = require(
|
|
448
|
+
path.join(__dirname, "..", "scripts", "audit-env-secrets.js"),
|
|
449
|
+
);
|
|
450
|
+
const secResults = await auditEnvSecrets(projectPath);
|
|
451
|
+
const critical =
|
|
452
|
+
secResults.secrets?.filter((s) => s.severity === "critical").length ||
|
|
453
|
+
0;
|
|
454
|
+
|
|
455
|
+
output += `\n## Security\n`;
|
|
456
|
+
output += `- **Critical Secrets:** ${critical}\n`;
|
|
457
|
+
} catch (err) {
|
|
458
|
+
output += `โ ๏ธ Security check failed: ${err.message}\n`;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// 4. Compliance Check (IaC + PII)
|
|
463
|
+
if (
|
|
464
|
+
checksToRun.includes("compliance") ||
|
|
465
|
+
profile === "ship" ||
|
|
466
|
+
profile === "ci"
|
|
467
|
+
) {
|
|
468
|
+
try {
|
|
469
|
+
const { runComplianceScan } = require(
|
|
470
|
+
path.join(
|
|
471
|
+
__dirname,
|
|
472
|
+
"..",
|
|
473
|
+
"bin",
|
|
474
|
+
"runners",
|
|
475
|
+
"lib",
|
|
476
|
+
"compliance-bridge.js",
|
|
477
|
+
),
|
|
478
|
+
);
|
|
479
|
+
const complianceResults = await runComplianceScan(projectPath);
|
|
480
|
+
|
|
481
|
+
output += `\n## Compliance\n`;
|
|
482
|
+
output += `- **IaC Issues:** ${complianceResults.iac.length}\n`;
|
|
483
|
+
output += `- **PII Findings:** ${complianceResults.pii.length}\n`;
|
|
484
|
+
|
|
485
|
+
results.checks.compliance = complianceResults;
|
|
486
|
+
} catch (err) {
|
|
487
|
+
output += `โ ๏ธ Compliance check failed: ${err.message}\n`;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// 5. AI Guardrails Check
|
|
492
|
+
if (checksToRun.includes("ai") || profile === "ship" || profile === "ci") {
|
|
493
|
+
try {
|
|
494
|
+
const { runHallucinationCheck } = require(
|
|
495
|
+
path.join(__dirname, "..", "bin", "runners", "lib", "ai-bridge.js"),
|
|
496
|
+
);
|
|
497
|
+
const aiResults = await runHallucinationCheck(projectPath);
|
|
498
|
+
|
|
499
|
+
output += `\n## AI Guardrails\n`;
|
|
500
|
+
output += `- **Hallucinations:** ${aiResults.issues.length}\n`;
|
|
501
|
+
|
|
502
|
+
results.checks.ai = aiResults;
|
|
503
|
+
} catch (err) {
|
|
504
|
+
output += `โ ๏ธ AI check failed: ${err.message}\n`;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
output += "\n---\n_Context Enhanced by Guardrail AI_";
|
|
509
|
+
|
|
510
|
+
if (format === "json") {
|
|
511
|
+
return this.success(JSON.stringify({ output, results }, null, 2));
|
|
512
|
+
}
|
|
513
|
+
return this.success(output);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ============================================================================
|
|
517
|
+
// GATE
|
|
518
|
+
// ============================================================================
|
|
519
|
+
async handleGate(projectPath, args) {
|
|
520
|
+
// Reuse scan logic with CI defaults
|
|
521
|
+
return this.handleScan(projectPath, { ...args, profile: "ci" });
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// ============================================================================
|
|
525
|
+
// FIX
|
|
526
|
+
// ============================================================================
|
|
527
|
+
async handleFix(projectPath, args) {
|
|
528
|
+
const mode = args?.mode || "plan";
|
|
529
|
+
const targets = args?.targets || ["all"];
|
|
530
|
+
|
|
531
|
+
let output = "# ๐ง Guardrail Fix\n\n";
|
|
532
|
+
output += `**Mode:** ${mode}\n`;
|
|
533
|
+
output += `**Targets:** ${targets.join(", ")}\n\n`;
|
|
534
|
+
|
|
535
|
+
if (mode === "apply") {
|
|
536
|
+
output +=
|
|
537
|
+
"โ ๏ธ Automated fixes are not yet fully implemented via MCP. Please run `guardrail fix` in terminal for interactive mode.\n";
|
|
538
|
+
output += "Showing plan instead:\n\n";
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
const { auditProductionIntegrity } = require(
|
|
543
|
+
path.join(__dirname, "..", "scripts", "audit-production-integrity.js"),
|
|
544
|
+
);
|
|
545
|
+
const { results } = await auditProductionIntegrity(projectPath);
|
|
546
|
+
|
|
547
|
+
if (results.env?.secrets?.length > 0) {
|
|
548
|
+
output += "### ๐ Secrets\n";
|
|
549
|
+
output += "- Move hardcoded secrets to .env\n";
|
|
550
|
+
}
|
|
551
|
+
if (results.auth?.analysis?.sensitiveUnprotected?.length > 0) {
|
|
552
|
+
output += "### ๐ Auth\n";
|
|
553
|
+
output += "- Add auth middleware to sensitive endpoints\n";
|
|
554
|
+
}
|
|
555
|
+
if (results.mocks?.issues?.length > 0) {
|
|
556
|
+
output += "### ๐งช Mock Code\n";
|
|
557
|
+
output += "- Remove mock/test code from production bundles\n";
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (
|
|
561
|
+
!results.env?.secrets?.length &&
|
|
562
|
+
!results.auth?.analysis?.sensitiveUnprotected?.length &&
|
|
563
|
+
!results.mocks?.issues?.length
|
|
564
|
+
) {
|
|
565
|
+
output += "โ
No fixes needed.\n";
|
|
566
|
+
}
|
|
567
|
+
} catch (err) {
|
|
568
|
+
output += `โ Analysis failed: ${err.message}\n`;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return this.success(output);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// ============================================================================
|
|
575
|
+
// PROOF
|
|
576
|
+
// ============================================================================
|
|
577
|
+
async handleProof(projectPath, args) {
|
|
578
|
+
const mode = args?.mode;
|
|
579
|
+
|
|
580
|
+
if (!mode) return this.error("Mode required: mocks | reality");
|
|
581
|
+
|
|
582
|
+
let output = `# ๐งช Guardrail Proof: ${mode}\n\n`;
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
if (mode === "mocks") {
|
|
586
|
+
const { auditMockBlocker } = require(
|
|
587
|
+
path.join(__dirname, "..", "scripts", "audit-mock-blocker.js"),
|
|
588
|
+
);
|
|
589
|
+
const results = await auditMockBlocker(projectPath);
|
|
590
|
+
|
|
591
|
+
const issues = results.issues.length;
|
|
592
|
+
output += `**Status:** ${issues === 0 ? "โ
PASSED" : "๐ซ FAILED"}\n`;
|
|
593
|
+
output += `**Issues Found:** ${issues}\n\n`;
|
|
594
|
+
|
|
595
|
+
if (issues > 0) {
|
|
596
|
+
output += "### Issues\n";
|
|
597
|
+
for (const issue of results.issues.slice(0, 10)) {
|
|
598
|
+
output += `- ${issue.name} in ${issue.file}\n`;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
} else if (mode === "reality") {
|
|
602
|
+
// Reality check maps to production integrity currently
|
|
603
|
+
const { auditProductionIntegrity } = require(
|
|
604
|
+
path.join(
|
|
605
|
+
__dirname,
|
|
606
|
+
"..",
|
|
607
|
+
"scripts",
|
|
608
|
+
"audit-production-integrity.js",
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
const { integrity } = await auditProductionIntegrity(projectPath);
|
|
612
|
+
|
|
613
|
+
output += `**Status:** ${integrity.canShip ? "โ
PASSED" : "๐ซ FAILED"}\n`;
|
|
614
|
+
output += `**Score:** ${integrity.score}/100\n`;
|
|
615
|
+
} else {
|
|
616
|
+
return this.error("Invalid mode. Use 'mocks' or 'reality'.");
|
|
617
|
+
}
|
|
618
|
+
} catch (err) {
|
|
619
|
+
output += `โ Proof failed: ${err.message}\n`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return this.success(output);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// ============================================================================
|
|
626
|
+
// REPORT
|
|
627
|
+
// ============================================================================
|
|
628
|
+
async handleReport(projectPath, args) {
|
|
629
|
+
const outputDir = path.join(projectPath, ".guardrail");
|
|
630
|
+
try {
|
|
631
|
+
const summaryPath = path.join(outputDir, "summary.json");
|
|
632
|
+
const summary = JSON.parse(await fs.readFile(summaryPath, "utf-8"));
|
|
633
|
+
|
|
634
|
+
let output = "# ๐ Guardrail Report\n\n";
|
|
635
|
+
output += `**Date:** ${summary.timestamp}\n`;
|
|
636
|
+
output += `**Score:** ${summary.score}\n`;
|
|
637
|
+
output += `**Verdict:** ${summary.canShip ? "SHIP" : "NO-SHIP"}\n`;
|
|
638
|
+
|
|
639
|
+
return this.success(output);
|
|
640
|
+
} catch {
|
|
641
|
+
return this.error("No report found. Run guardrail.scan first.");
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// ============================================================================
|
|
646
|
+
// POLICY
|
|
647
|
+
// ============================================================================
|
|
648
|
+
async handlePolicy(projectPath, args) {
|
|
649
|
+
const action = args?.action || "get";
|
|
650
|
+
const configPath = path.join(projectPath, ".guardrailrc");
|
|
651
|
+
|
|
652
|
+
let config = {};
|
|
653
|
+
try {
|
|
654
|
+
config = JSON.parse(await fs.readFile(configPath, "utf-8"));
|
|
655
|
+
} catch {}
|
|
656
|
+
|
|
657
|
+
if (action === "get") {
|
|
658
|
+
return this.success(JSON.stringify(config, null, 2));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Set/Modify logic simplified for MCP
|
|
662
|
+
return this.success(
|
|
663
|
+
"Policy modification via MCP is read-only in this version. Edit .guardrailrc directly.",
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// ============================================================================
|
|
668
|
+
// STATUS
|
|
669
|
+
// ============================================================================
|
|
670
|
+
async handleStatus(projectPath, args) {
|
|
671
|
+
let output = "# ๐ Guardrail Status\n\n";
|
|
672
|
+
output += `- **Version:** ${VERSION}\n`;
|
|
673
|
+
output += `- **Path:** ${projectPath}\n`;
|
|
674
|
+
|
|
675
|
+
try {
|
|
676
|
+
await fs.access(path.join(projectPath, ".guardrailrc"));
|
|
677
|
+
output += "- **Config:** โ
Present\n";
|
|
678
|
+
} catch {
|
|
679
|
+
output += "- **Config:** โ ๏ธ Missing\n";
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return this.success(output);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
async run() {
|
|
686
|
+
const transport = new StdioServerTransport();
|
|
687
|
+
await this.server.connect(transport);
|
|
688
|
+
console.error("Guardrail MCP Server running on stdio");
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Main
|
|
693
|
+
if (fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
694
|
+
new GuardrailMCP().run().catch((err) => {
|
|
695
|
+
console.error(err);
|
|
696
|
+
process.exit(1);
|
|
697
|
+
});
|
|
698
|
+
}
|