@purplesquirrel/guardrails-mcp-server 1.0.0 → 1.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/index.js +393 -0
- package/package.json +4 -1
package/index.js
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AI Agent Guardrails MCP Server
|
|
5
|
+
* Security layer for Claude Code and AI agents
|
|
6
|
+
*
|
|
7
|
+
* Provides input validation, output filtering, policy enforcement, and audit logging
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import {
|
|
13
|
+
CallToolRequestSchema,
|
|
14
|
+
ListToolsRequestSchema,
|
|
15
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
+
|
|
17
|
+
import { GuardrailsEngine } from './src/engine/GuardrailsEngine.js';
|
|
18
|
+
import { InputValidator } from './src/validators/InputValidator.js';
|
|
19
|
+
import { OutputFilter } from './src/filters/OutputFilter.js';
|
|
20
|
+
import { PolicyEngine } from './src/policies/PolicyEngine.js';
|
|
21
|
+
import { AuditLogger } from './src/audit/AuditLogger.js';
|
|
22
|
+
|
|
23
|
+
// Initialize components
|
|
24
|
+
const config = {
|
|
25
|
+
enableInputValidation: true,
|
|
26
|
+
enableOutputFiltering: true,
|
|
27
|
+
enablePolicyEnforcement: true,
|
|
28
|
+
enableAuditLogging: true,
|
|
29
|
+
enableRateLimiting: true,
|
|
30
|
+
maxRequestsPerMinute: parseInt(process.env.GUARDRAILS_RATE_LIMIT || '60'),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const engine = new GuardrailsEngine(config);
|
|
34
|
+
const validator = new InputValidator();
|
|
35
|
+
const filter = new OutputFilter();
|
|
36
|
+
const policyEngine = new PolicyEngine();
|
|
37
|
+
const logger = new AuditLogger({ enableConsoleLog: false });
|
|
38
|
+
|
|
39
|
+
// Create server
|
|
40
|
+
const server = new Server(
|
|
41
|
+
{
|
|
42
|
+
name: "guardrails-mcp-server",
|
|
43
|
+
version: "1.0.1",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
capabilities: {
|
|
47
|
+
tools: {},
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Tool definitions
|
|
53
|
+
const tools = [
|
|
54
|
+
{
|
|
55
|
+
name: "validate_input",
|
|
56
|
+
description: "Validate and sanitize user input for prompt injection, malicious patterns, and sensitive data",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
content: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "The input content to validate"
|
|
63
|
+
},
|
|
64
|
+
context: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Optional context about the input source"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
required: ["content"]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "filter_output",
|
|
74
|
+
description: "Filter and redact sensitive information from AI output",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
content: {
|
|
79
|
+
type: "string",
|
|
80
|
+
description: "The output content to filter"
|
|
81
|
+
},
|
|
82
|
+
redactPII: {
|
|
83
|
+
type: "boolean",
|
|
84
|
+
description: "Whether to redact personally identifiable information",
|
|
85
|
+
default: true
|
|
86
|
+
},
|
|
87
|
+
redactSecrets: {
|
|
88
|
+
type: "boolean",
|
|
89
|
+
description: "Whether to redact secrets and credentials",
|
|
90
|
+
default: true
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
required: ["content"]
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "check_policy",
|
|
98
|
+
description: "Check if an action is allowed by security policies",
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
action: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "The action to check (e.g., 'file_read', 'network_access')"
|
|
105
|
+
},
|
|
106
|
+
resource: {
|
|
107
|
+
type: "string",
|
|
108
|
+
description: "The resource being accessed"
|
|
109
|
+
},
|
|
110
|
+
user: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "The user or agent performing the action"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
required: ["action", "resource"]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "get_audit_logs",
|
|
120
|
+
description: "Retrieve audit logs for guardrails events",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
limit: {
|
|
125
|
+
type: "number",
|
|
126
|
+
description: "Maximum number of logs to return",
|
|
127
|
+
default: 100
|
|
128
|
+
},
|
|
129
|
+
type: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Filter by event type"
|
|
132
|
+
},
|
|
133
|
+
since: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "ISO timestamp to filter logs after"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "get_metrics",
|
|
142
|
+
description: "Get guardrails metrics and statistics",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "add_policy",
|
|
150
|
+
description: "Add a custom security policy",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
id: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "Unique policy identifier"
|
|
157
|
+
},
|
|
158
|
+
name: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "Human-readable policy name"
|
|
161
|
+
},
|
|
162
|
+
description: {
|
|
163
|
+
type: "string",
|
|
164
|
+
description: "Policy description"
|
|
165
|
+
},
|
|
166
|
+
pattern: {
|
|
167
|
+
type: "string",
|
|
168
|
+
description: "Regex pattern to match"
|
|
169
|
+
},
|
|
170
|
+
action: {
|
|
171
|
+
type: "string",
|
|
172
|
+
enum: ["deny", "warn"],
|
|
173
|
+
description: "Action when pattern matches"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
required: ["id", "name", "pattern"]
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "add_blocked_pattern",
|
|
181
|
+
description: "Add a pattern to block in inputs",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: "object",
|
|
184
|
+
properties: {
|
|
185
|
+
pattern: {
|
|
186
|
+
type: "string",
|
|
187
|
+
description: "Regex pattern to block"
|
|
188
|
+
},
|
|
189
|
+
name: {
|
|
190
|
+
type: "string",
|
|
191
|
+
description: "Name for this pattern"
|
|
192
|
+
},
|
|
193
|
+
severity: {
|
|
194
|
+
type: "string",
|
|
195
|
+
enum: ["low", "medium", "high", "critical"],
|
|
196
|
+
description: "Severity level when pattern is matched"
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
required: ["pattern", "name"]
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "get_summary",
|
|
204
|
+
description: "Get a summary report of guardrails activity",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
type: "object",
|
|
207
|
+
properties: {
|
|
208
|
+
hours: {
|
|
209
|
+
type: "number",
|
|
210
|
+
description: "Number of hours to include in summary",
|
|
211
|
+
default: 24
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
// List tools handler
|
|
219
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
220
|
+
return { tools };
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Call tool handler
|
|
224
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
225
|
+
const { name, arguments: args } = request.params;
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
switch (name) {
|
|
229
|
+
case "validate_input": {
|
|
230
|
+
const result = await validator.validate(args.content, { source: args.context });
|
|
231
|
+
await logger.log({
|
|
232
|
+
type: result.valid ? 'INPUT_VALIDATED' : 'INPUT_VALIDATION_FAILED',
|
|
233
|
+
input: args.content?.substring(0, 100),
|
|
234
|
+
violations: result.violations
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
content: [{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: JSON.stringify({
|
|
240
|
+
isValid: result.valid,
|
|
241
|
+
issues: result.violations,
|
|
242
|
+
stats: result.stats
|
|
243
|
+
}, null, 2)
|
|
244
|
+
}]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
case "filter_output": {
|
|
249
|
+
const result = await filter.filter(args.content, {
|
|
250
|
+
redactPII: args.redactPII !== false,
|
|
251
|
+
redactSecrets: args.redactSecrets !== false
|
|
252
|
+
});
|
|
253
|
+
await logger.log({
|
|
254
|
+
type: 'OUTPUT_FILTERED',
|
|
255
|
+
redactionsApplied: result.redactions?.length || 0
|
|
256
|
+
});
|
|
257
|
+
return {
|
|
258
|
+
content: [{
|
|
259
|
+
type: "text",
|
|
260
|
+
text: JSON.stringify({
|
|
261
|
+
filtered: result.response,
|
|
262
|
+
modified: result.modified,
|
|
263
|
+
blocked: result.blocked,
|
|
264
|
+
redactions: result.redactions
|
|
265
|
+
}, null, 2)
|
|
266
|
+
}]
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
case "check_policy": {
|
|
271
|
+
const result = await policyEngine.evaluate({
|
|
272
|
+
name: args.action,
|
|
273
|
+
resource: args.resource
|
|
274
|
+
}, {
|
|
275
|
+
userId: args.user || 'anonymous'
|
|
276
|
+
});
|
|
277
|
+
await logger.log({
|
|
278
|
+
type: result.allowed ? 'POLICY_CHECK_PASSED' : 'POLICY_VIOLATION',
|
|
279
|
+
action: args.action,
|
|
280
|
+
resource: args.resource,
|
|
281
|
+
allowed: result.allowed,
|
|
282
|
+
policy: result.violatedPolicy
|
|
283
|
+
});
|
|
284
|
+
return {
|
|
285
|
+
content: [{
|
|
286
|
+
type: "text",
|
|
287
|
+
text: JSON.stringify(result, null, 2)
|
|
288
|
+
}]
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
case "get_audit_logs": {
|
|
293
|
+
const logs = logger.getLogs({
|
|
294
|
+
limit: args.limit || 100,
|
|
295
|
+
type: args.type,
|
|
296
|
+
startTime: args.since
|
|
297
|
+
});
|
|
298
|
+
return {
|
|
299
|
+
content: [{
|
|
300
|
+
type: "text",
|
|
301
|
+
text: JSON.stringify(logs, null, 2)
|
|
302
|
+
}]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
case "get_metrics": {
|
|
307
|
+
const metrics = logger.getMetrics();
|
|
308
|
+
return {
|
|
309
|
+
content: [{
|
|
310
|
+
type: "text",
|
|
311
|
+
text: JSON.stringify(metrics, null, 2)
|
|
312
|
+
}]
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
case "add_policy": {
|
|
317
|
+
policyEngine.createSimplePolicy({
|
|
318
|
+
id: args.id,
|
|
319
|
+
name: args.name,
|
|
320
|
+
description: args.description || '',
|
|
321
|
+
pattern: args.pattern,
|
|
322
|
+
action: args.action || 'deny',
|
|
323
|
+
message: `Policy ${args.name} violated`
|
|
324
|
+
});
|
|
325
|
+
await logger.log({
|
|
326
|
+
type: 'POLICY_ADDED',
|
|
327
|
+
policyId: args.id,
|
|
328
|
+
policyName: args.name
|
|
329
|
+
});
|
|
330
|
+
return {
|
|
331
|
+
content: [{
|
|
332
|
+
type: "text",
|
|
333
|
+
text: JSON.stringify({ success: true, message: `Policy '${args.name}' added` })
|
|
334
|
+
}]
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
case "add_blocked_pattern": {
|
|
339
|
+
validator.addInjectionPattern(args.pattern);
|
|
340
|
+
await logger.log({
|
|
341
|
+
type: 'PATTERN_ADDED',
|
|
342
|
+
patternName: args.name,
|
|
343
|
+
severity: args.severity || 'medium'
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
content: [{
|
|
347
|
+
type: "text",
|
|
348
|
+
text: JSON.stringify({ success: true, message: `Pattern '${args.name}' added to blocked list` })
|
|
349
|
+
}]
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
case "get_summary": {
|
|
354
|
+
const summary = logger.getSummary(args.hours || 24);
|
|
355
|
+
return {
|
|
356
|
+
content: [{
|
|
357
|
+
type: "text",
|
|
358
|
+
text: JSON.stringify(summary, null, 2)
|
|
359
|
+
}]
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
default:
|
|
364
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
await logger.log({
|
|
368
|
+
type: 'ERROR',
|
|
369
|
+
level: 'error',
|
|
370
|
+
tool: name,
|
|
371
|
+
error: error.message
|
|
372
|
+
});
|
|
373
|
+
return {
|
|
374
|
+
content: [{
|
|
375
|
+
type: "text",
|
|
376
|
+
text: JSON.stringify({ error: error.message })
|
|
377
|
+
}],
|
|
378
|
+
isError: true
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Start server
|
|
384
|
+
async function main() {
|
|
385
|
+
const transport = new StdioServerTransport();
|
|
386
|
+
await server.connect(transport);
|
|
387
|
+
console.error("Guardrails MCP Server running on stdio");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
main().catch((error) => {
|
|
391
|
+
console.error("Fatal error:", error);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
});
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purplesquirrel/guardrails-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "AI Agent Guardrails MCP Server - Security layer for Claude Code and AI agents",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"guardrails-mcp-server": "./index.js"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
11
|
"start": "node index.js",
|
|
9
12
|
"test": "node --test tests/",
|