contextguard 0.1.3
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/.github/ISSUE_TEMPLATE/bug_report.md +57 -0
- package/CONTRIBUTING.md +532 -0
- package/LICENSE +21 -0
- package/README.md +163 -0
- package/SECURITY.md +254 -0
- package/dist/mcp-security-wrapper.js +398 -0
- package/eslint.config.mts +23 -0
- package/mcp_security.log +2 -0
- package/package.json +46 -0
- package/security.json +12 -0
- package/src/mcp-security-wrapper.ts +527 -0
- package/src/test-server.ts +295 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Copyright (c) 2025 Amir Mironi
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.SecurityLogger = exports.SecurityPolicy = exports.MCPSecurityWrapper = void 0;
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const crypto_1 = require("crypto");
|
|
47
|
+
class SecurityPolicy {
|
|
48
|
+
constructor(config) {
|
|
49
|
+
this.config = {
|
|
50
|
+
maxToolCallsPerMinute: 30,
|
|
51
|
+
blockedPatterns: [],
|
|
52
|
+
allowedFilePaths: [],
|
|
53
|
+
alertThreshold: 5,
|
|
54
|
+
enablePromptInjectionDetection: true,
|
|
55
|
+
enableSensitiveDataDetection: true,
|
|
56
|
+
...config,
|
|
57
|
+
};
|
|
58
|
+
// Sensitive data patterns
|
|
59
|
+
this.sensitiveDataPatterns = [
|
|
60
|
+
/(?:password|secret|api[_-]?key|token)\s*[:=]\s*['"]?[\w\-.]+['"]?/gi,
|
|
61
|
+
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Email
|
|
62
|
+
/\b\d{3}-\d{2}-\d{4}\b/g, // SSN
|
|
63
|
+
/sk-[a-zA-Z0-9]{48}/g, // OpenAI API keys
|
|
64
|
+
/ghp_[a-zA-Z0-9]{36}/g, // GitHub tokens
|
|
65
|
+
/AKIA[0-9A-Z]{16}/g, // AWS Access Keys
|
|
66
|
+
];
|
|
67
|
+
// Prompt injection patterns
|
|
68
|
+
this.promptInjectionPatterns = [
|
|
69
|
+
/ignore\s+(previous|all)\s+(instructions|prompts)/gi,
|
|
70
|
+
/system:\s*you\s+are\s+now/gi,
|
|
71
|
+
/forget\s+(everything|all)/gi,
|
|
72
|
+
/new\s+instructions:/gi,
|
|
73
|
+
/\[INST\].*?\[\/INST\]/gs,
|
|
74
|
+
/<\|im_start\|>/g,
|
|
75
|
+
/disregard\s+previous/gi,
|
|
76
|
+
/override\s+previous/gi,
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
checkPromptInjection(text) {
|
|
80
|
+
if (!this.config.enablePromptInjectionDetection)
|
|
81
|
+
return [];
|
|
82
|
+
const violations = [];
|
|
83
|
+
for (const pattern of this.promptInjectionPatterns) {
|
|
84
|
+
const matches = text.match(pattern);
|
|
85
|
+
if (matches) {
|
|
86
|
+
violations.push(`Potential prompt injection detected: "${matches[0].substring(0, 50)}..."`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return violations;
|
|
90
|
+
}
|
|
91
|
+
checkSensitiveData(text) {
|
|
92
|
+
if (!this.config.enableSensitiveDataDetection)
|
|
93
|
+
return [];
|
|
94
|
+
const violations = [];
|
|
95
|
+
for (const pattern of this.sensitiveDataPatterns) {
|
|
96
|
+
const matches = text.match(pattern);
|
|
97
|
+
if (matches) {
|
|
98
|
+
violations.push(`Sensitive data pattern detected (redacted): ${pattern.source.substring(0, 30)}...`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return violations;
|
|
102
|
+
}
|
|
103
|
+
checkFileAccess(filePath) {
|
|
104
|
+
const violations = [];
|
|
105
|
+
if (filePath.includes("..")) {
|
|
106
|
+
violations.push(`Path traversal attempt detected: ${filePath}`);
|
|
107
|
+
}
|
|
108
|
+
const dangerousPaths = [
|
|
109
|
+
"/etc",
|
|
110
|
+
"/root",
|
|
111
|
+
"/sys",
|
|
112
|
+
"/proc",
|
|
113
|
+
"C:\\Windows\\System32",
|
|
114
|
+
];
|
|
115
|
+
if (dangerousPaths.some((dangerous) => filePath.startsWith(dangerous))) {
|
|
116
|
+
violations.push(`Access to dangerous path detected: ${filePath}`);
|
|
117
|
+
}
|
|
118
|
+
if (this.config.allowedFilePaths &&
|
|
119
|
+
this.config.allowedFilePaths.length > 0) {
|
|
120
|
+
const isAllowed = this.config.allowedFilePaths.some((allowed) => filePath.startsWith(allowed));
|
|
121
|
+
if (!isAllowed) {
|
|
122
|
+
violations.push(`File path not in allowed list: ${filePath}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return violations;
|
|
126
|
+
}
|
|
127
|
+
checkRateLimit(timestamps) {
|
|
128
|
+
const oneMinuteAgo = Date.now() - 60000;
|
|
129
|
+
const recentCalls = timestamps.filter((t) => t > oneMinuteAgo);
|
|
130
|
+
return recentCalls.length < (this.config.maxToolCallsPerMinute || 30);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.SecurityPolicy = SecurityPolicy;
|
|
134
|
+
class SecurityLogger {
|
|
135
|
+
constructor(logFile = "mcp_security.log") {
|
|
136
|
+
this.events = [];
|
|
137
|
+
this.logFile = logFile;
|
|
138
|
+
}
|
|
139
|
+
logEvent(eventType, severity, details, sessionId) {
|
|
140
|
+
const event = {
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
eventType,
|
|
143
|
+
severity,
|
|
144
|
+
details,
|
|
145
|
+
sessionId,
|
|
146
|
+
};
|
|
147
|
+
this.events.push(event);
|
|
148
|
+
fs.appendFileSync(this.logFile, JSON.stringify(event) + "\n");
|
|
149
|
+
if (severity === "HIGH" || severity === "CRITICAL") {
|
|
150
|
+
console.error(`[SECURITY ALERT] ${eventType}: ${JSON.stringify(details)}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
getStatistics() {
|
|
154
|
+
return {
|
|
155
|
+
totalEvents: this.events.length,
|
|
156
|
+
eventsByType: this.countByField("eventType"),
|
|
157
|
+
eventsBySeverity: this.countByField("severity"),
|
|
158
|
+
recentEvents: this.events.slice(-10),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
countByField(field) {
|
|
162
|
+
const counts = {};
|
|
163
|
+
for (const event of this.events) {
|
|
164
|
+
const value = String(event[field]);
|
|
165
|
+
counts[value] = (counts[value] || 0) + 1;
|
|
166
|
+
}
|
|
167
|
+
return counts;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.SecurityLogger = SecurityLogger;
|
|
171
|
+
class MCPSecurityWrapper {
|
|
172
|
+
constructor(serverCommand, policy, logger) {
|
|
173
|
+
this.process = null;
|
|
174
|
+
this.toolCallTimestamps = [];
|
|
175
|
+
this.clientMessageBuffer = "";
|
|
176
|
+
this.serverMessageBuffer = "";
|
|
177
|
+
this.serverCommand = serverCommand;
|
|
178
|
+
this.policy = policy;
|
|
179
|
+
this.logger = logger;
|
|
180
|
+
this.sessionId = (0, crypto_1.createHash)("md5")
|
|
181
|
+
.update(Date.now().toString())
|
|
182
|
+
.digest("hex")
|
|
183
|
+
.substring(0, 8);
|
|
184
|
+
}
|
|
185
|
+
async start() {
|
|
186
|
+
this.process = (0, child_process_1.spawn)(this.serverCommand[0], this.serverCommand.slice(1), {
|
|
187
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
188
|
+
});
|
|
189
|
+
if (!this.process.stdout || !this.process.stdin || !this.process.stderr) {
|
|
190
|
+
throw new Error("Failed to create child process streams");
|
|
191
|
+
}
|
|
192
|
+
this.logger.logEvent("SERVER_START", "LOW", {
|
|
193
|
+
command: this.serverCommand.join(" "),
|
|
194
|
+
pid: this.process.pid,
|
|
195
|
+
}, this.sessionId);
|
|
196
|
+
this.process.stderr.pipe(process.stderr);
|
|
197
|
+
this.process.stdout.on("data", (data) => {
|
|
198
|
+
const output = data.toString();
|
|
199
|
+
this.handleServerOutput(output);
|
|
200
|
+
});
|
|
201
|
+
process.stdin.on("data", (data) => {
|
|
202
|
+
const input = data.toString();
|
|
203
|
+
this.handleClientInput(input);
|
|
204
|
+
});
|
|
205
|
+
this.process.on("exit", (code) => {
|
|
206
|
+
this.logger.logEvent("SERVER_EXIT", "MEDIUM", {
|
|
207
|
+
exitCode: code,
|
|
208
|
+
}, this.sessionId);
|
|
209
|
+
console.error("\n=== MCP Security Statistics ===");
|
|
210
|
+
console.error(JSON.stringify(this.logger.getStatistics(), null, 2));
|
|
211
|
+
// Use setImmediate to allow pending I/O to complete
|
|
212
|
+
setImmediate(() => {
|
|
213
|
+
process.exit(code || 0);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
this.process.on("error", (err) => {
|
|
217
|
+
this.logger.logEvent("SERVER_ERROR", "HIGH", {
|
|
218
|
+
error: err.message,
|
|
219
|
+
}, this.sessionId);
|
|
220
|
+
console.error("Server process error:", err);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
handleClientInput(input) {
|
|
224
|
+
this.clientMessageBuffer += input;
|
|
225
|
+
const lines = this.clientMessageBuffer.split("\n");
|
|
226
|
+
this.clientMessageBuffer = lines.pop() || "";
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
if (line.trim()) {
|
|
229
|
+
this.processClientMessage(line);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
processClientMessage(line) {
|
|
234
|
+
try {
|
|
235
|
+
const message = JSON.parse(line);
|
|
236
|
+
this.logger.logEvent("CLIENT_REQUEST", "LOW", {
|
|
237
|
+
method: message.method,
|
|
238
|
+
id: message.id,
|
|
239
|
+
}, this.sessionId);
|
|
240
|
+
const violations = [];
|
|
241
|
+
let shouldBlock = false;
|
|
242
|
+
if (message.method === "tools/call") {
|
|
243
|
+
const now = Date.now();
|
|
244
|
+
this.toolCallTimestamps.push(now);
|
|
245
|
+
// Clean up old timestamps to prevent memory leak
|
|
246
|
+
const oneMinuteAgo = now - 60000;
|
|
247
|
+
this.toolCallTimestamps = this.toolCallTimestamps.filter((t) => t > oneMinuteAgo);
|
|
248
|
+
if (!this.policy.checkRateLimit(this.toolCallTimestamps)) {
|
|
249
|
+
violations.push("Rate limit exceeded for tool calls");
|
|
250
|
+
shouldBlock = true;
|
|
251
|
+
this.logger.logEvent("RATE_LIMIT_EXCEEDED", "HIGH", {
|
|
252
|
+
method: message.method,
|
|
253
|
+
toolName: message.params?.name,
|
|
254
|
+
}, this.sessionId);
|
|
255
|
+
}
|
|
256
|
+
const paramsStr = JSON.stringify(message.params);
|
|
257
|
+
const injectionViolations = this.policy.checkPromptInjection(paramsStr);
|
|
258
|
+
violations.push(...injectionViolations);
|
|
259
|
+
const sensitiveViolations = this.policy.checkSensitiveData(paramsStr);
|
|
260
|
+
violations.push(...sensitiveViolations);
|
|
261
|
+
// Check multiple possible file path parameter locations
|
|
262
|
+
const filePathParams = [
|
|
263
|
+
message.params?.arguments?.path,
|
|
264
|
+
message.params?.arguments?.filePath,
|
|
265
|
+
message.params?.arguments?.file,
|
|
266
|
+
message.params?.arguments?.directory,
|
|
267
|
+
message.params?.path,
|
|
268
|
+
message.params?.filePath,
|
|
269
|
+
].filter((path) => typeof path === 'string');
|
|
270
|
+
for (const filePath of filePathParams) {
|
|
271
|
+
const fileViolations = this.policy.checkFileAccess(filePath);
|
|
272
|
+
violations.push(...fileViolations);
|
|
273
|
+
}
|
|
274
|
+
this.logger.logEvent("TOOL_CALL", violations.length > 0 ? "HIGH" : "LOW", {
|
|
275
|
+
toolName: message.params?.name,
|
|
276
|
+
hasViolations: violations.length > 0,
|
|
277
|
+
violations,
|
|
278
|
+
}, this.sessionId);
|
|
279
|
+
}
|
|
280
|
+
if (violations.length > 0) {
|
|
281
|
+
this.logger.logEvent("SECURITY_VIOLATION", "CRITICAL", {
|
|
282
|
+
violations,
|
|
283
|
+
message: message,
|
|
284
|
+
blocked: shouldBlock,
|
|
285
|
+
}, this.sessionId);
|
|
286
|
+
console.error(`\n⚠️ SECURITY VIOLATIONS DETECTED:\n${violations.join("\n")}\n`);
|
|
287
|
+
if (shouldBlock) {
|
|
288
|
+
console.error("🚫 REQUEST BLOCKED\n");
|
|
289
|
+
// Send error response back to client
|
|
290
|
+
if (message.id !== undefined) {
|
|
291
|
+
const errorResponse = {
|
|
292
|
+
jsonrpc: message.jsonrpc,
|
|
293
|
+
id: message.id,
|
|
294
|
+
error: {
|
|
295
|
+
code: -32000,
|
|
296
|
+
message: "Security violation: Request blocked",
|
|
297
|
+
data: { violations },
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
process.stdout.write(JSON.stringify(errorResponse) + "\n");
|
|
301
|
+
}
|
|
302
|
+
return; // Don't forward to server
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (this.process && this.process.stdin) {
|
|
306
|
+
this.process.stdin.write(line + "\n");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
this.logger.logEvent("PARSE_ERROR", "MEDIUM", {
|
|
311
|
+
error: err instanceof Error ? err.message : String(err),
|
|
312
|
+
line: line.substring(0, 100),
|
|
313
|
+
}, this.sessionId);
|
|
314
|
+
console.error(`Failed to parse client message: ${err}`);
|
|
315
|
+
// Forward unparseable messages
|
|
316
|
+
if (this.process && this.process.stdin) {
|
|
317
|
+
this.process.stdin.write(line + "\n");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
handleServerOutput(output) {
|
|
322
|
+
// Forward output immediately
|
|
323
|
+
process.stdout.write(output);
|
|
324
|
+
// Buffer and parse for logging
|
|
325
|
+
this.serverMessageBuffer += output;
|
|
326
|
+
const lines = this.serverMessageBuffer.split("\n");
|
|
327
|
+
this.serverMessageBuffer = lines.pop() || "";
|
|
328
|
+
for (const line of lines) {
|
|
329
|
+
if (line.trim()) {
|
|
330
|
+
try {
|
|
331
|
+
const message = JSON.parse(line);
|
|
332
|
+
this.logger.logEvent("SERVER_RESPONSE", "LOW", {
|
|
333
|
+
id: message.id,
|
|
334
|
+
hasError: !!message.error,
|
|
335
|
+
}, this.sessionId);
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
// Log parse errors for server output
|
|
339
|
+
this.logger.logEvent("SERVER_PARSE_ERROR", "LOW", {
|
|
340
|
+
error: err instanceof Error ? err.message : String(err),
|
|
341
|
+
line: line.substring(0, 100),
|
|
342
|
+
}, this.sessionId);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
exports.MCPSecurityWrapper = MCPSecurityWrapper;
|
|
349
|
+
async function main() {
|
|
350
|
+
const args = process.argv.slice(2);
|
|
351
|
+
if (args.length === 0 || args.includes("--help")) {
|
|
352
|
+
console.log(`
|
|
353
|
+
MCP Security Wrapper - MVP
|
|
354
|
+
|
|
355
|
+
Usage:
|
|
356
|
+
npx ts-node mcp-security-wrapper.ts --server "node server.js" [--config security.json]
|
|
357
|
+
|
|
358
|
+
Options:
|
|
359
|
+
--server <command> Command to start the MCP server (required)
|
|
360
|
+
--config <file> Path to security config JSON file (optional)
|
|
361
|
+
--help Show this help message
|
|
362
|
+
|
|
363
|
+
Example:
|
|
364
|
+
npx ts-node mcp-security-wrapper.ts --server "node server.js" --config security.json
|
|
365
|
+
`);
|
|
366
|
+
process.exit(0);
|
|
367
|
+
}
|
|
368
|
+
let serverCommand = "";
|
|
369
|
+
let configFile = "";
|
|
370
|
+
for (let i = 0; i < args.length; i++) {
|
|
371
|
+
if (args[i] === "--server" && args[i + 1]) {
|
|
372
|
+
serverCommand = args[i + 1];
|
|
373
|
+
i++;
|
|
374
|
+
}
|
|
375
|
+
else if (args[i] === "--config" && args[i + 1]) {
|
|
376
|
+
configFile = args[i + 1];
|
|
377
|
+
i++;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (!serverCommand) {
|
|
381
|
+
console.error("Error: --server argument is required");
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
let config = {};
|
|
385
|
+
if (configFile && fs.existsSync(configFile)) {
|
|
386
|
+
config = JSON.parse(fs.readFileSync(configFile, "utf-8"));
|
|
387
|
+
}
|
|
388
|
+
const policy = new SecurityPolicy(config);
|
|
389
|
+
const logger = new SecurityLogger();
|
|
390
|
+
const wrapper = new MCPSecurityWrapper(serverCommand.split(" "), policy, logger);
|
|
391
|
+
await wrapper.start();
|
|
392
|
+
}
|
|
393
|
+
if (require.main === module) {
|
|
394
|
+
main().catch((err) => {
|
|
395
|
+
console.error("Fatal error:", err);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
import { defineConfig } from "eslint/config";
|
|
5
|
+
|
|
6
|
+
export default defineConfig([
|
|
7
|
+
{
|
|
8
|
+
ignores: ["dist/**", "node_modules/**"],
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
|
12
|
+
plugins: { js },
|
|
13
|
+
extends: ["js/recommended"],
|
|
14
|
+
languageOptions: { globals: globals.browser },
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
rules: {
|
|
18
|
+
// "no-unused-vars": "warn",
|
|
19
|
+
// "no-explicit-any": "info",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
tseslint.configs.recommended,
|
|
23
|
+
]);
|
package/mcp_security.log
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{"timestamp":"2025-10-09T12:42:00.791Z","eventType":"SERVER_START","severity":"LOW","details":{"command":"node /absolute/path/to/your-mcp-server.js","pid":33305},"sessionId":"5820789f"}
|
|
2
|
+
{"timestamp":"2025-10-09T12:42:00.823Z","eventType":"SERVER_EXIT","severity":"MEDIUM","details":{"exitCode":1},"sessionId":"5820789f"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "contextguard",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Security monitoring wrapper for MCP servers",
|
|
5
|
+
"main": "dist/mcp-security-wrapper.js",
|
|
6
|
+
"types": "dist/mcp-security-wrapper.d.ts",
|
|
7
|
+
"homepage": "https://github.com/amironi/contextguard#readme",
|
|
8
|
+
"author": "Amir Mironi",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"bin": {
|
|
11
|
+
"contextguard": "./dist/mcp-security-wrapper.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"publish": "npm publish",
|
|
16
|
+
"start": "node dist/server.js",
|
|
17
|
+
"dev": "ts-node src/mcp-security-wrapper.ts",
|
|
18
|
+
"test": "jest",
|
|
19
|
+
"lint": "eslint ."
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"security",
|
|
24
|
+
"ai",
|
|
25
|
+
"llm",
|
|
26
|
+
"claude"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/amironi/contextguard.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/amironi/contextguard/issues"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@eslint/js": "^9.37.0",
|
|
37
|
+
"@types/node": "^24.7.0",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^8.46.0",
|
|
39
|
+
"@typescript-eslint/parser": "^8.46.0",
|
|
40
|
+
"eslint": "^9.37.0",
|
|
41
|
+
"globals": "^16.4.0",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "^5.9.3",
|
|
44
|
+
"typescript-eslint": "^8.46.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/security.json
ADDED