defense-mcp-server 0.9.4 → 0.9.5
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/build/index.js +98 -76
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -104,10 +104,6 @@ process.on("unhandledRejection", (reason) => {
|
|
|
104
104
|
});
|
|
105
105
|
// ── Main entry point ─────────────────────────────────────────────────────────
|
|
106
106
|
async function main() {
|
|
107
|
-
const server = new McpServer({
|
|
108
|
-
name: "defense-mcp-server",
|
|
109
|
-
version: VERSION,
|
|
110
|
-
});
|
|
111
107
|
// ── Phase 1: Dependency Validation & Auto-Install ────────────────────────
|
|
112
108
|
//
|
|
113
109
|
// Before registering tools, validate that all required system binaries
|
|
@@ -216,66 +212,79 @@ async function main() {
|
|
|
216
212
|
catch {
|
|
217
213
|
// Non-fatal — third-party check failure must not block startup
|
|
218
214
|
}
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
215
|
+
// ── Phase 2: Tool registration ────────────────────────────────────────────
|
|
216
|
+
//
|
|
217
|
+
// registerAllTools() creates a fresh McpServer, wraps it with pre-flight
|
|
218
|
+
// middleware, and registers every tool module. For stdio we call it once;
|
|
219
|
+
// for HTTP we call it per session so each client gets its own instance
|
|
220
|
+
// (the MCP SDK only allows one transport per McpServer).
|
|
221
|
+
function registerAllTools() {
|
|
222
|
+
const srv = new McpServer({
|
|
223
|
+
name: "defense-mcp-server",
|
|
224
|
+
version: VERSION,
|
|
225
|
+
});
|
|
226
|
+
const wrapped = createPreflightServer(srv);
|
|
227
|
+
let registered = 0;
|
|
228
|
+
let failed = 0;
|
|
229
|
+
const failedModules = [];
|
|
230
|
+
function safeRegister(name, fn) {
|
|
231
|
+
try {
|
|
232
|
+
fn(wrapped);
|
|
233
|
+
registered++;
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
failed++;
|
|
237
|
+
failedModules.push(name);
|
|
238
|
+
console.error(`[startup] ⚠ Failed to register ${name} tools: ${err instanceof Error ? err.message : err}`);
|
|
239
|
+
}
|
|
234
240
|
}
|
|
241
|
+
// Sudo privilege management (must be registered first — prerequisite for other tools)
|
|
242
|
+
safeRegister("sudo-management", registerSudoManagementTools);
|
|
243
|
+
// Original tool modules
|
|
244
|
+
safeRegister("firewall", registerFirewallTools);
|
|
245
|
+
safeRegister("hardening", registerHardeningTools);
|
|
246
|
+
safeRegister("integrity", registerIntegrityTools);
|
|
247
|
+
safeRegister("logging", registerLoggingTools);
|
|
248
|
+
safeRegister("network-defense", registerNetworkDefenseTools);
|
|
249
|
+
safeRegister("compliance", registerComplianceTools);
|
|
250
|
+
safeRegister("malware", registerMalwareTools);
|
|
251
|
+
safeRegister("backup", registerBackupTools);
|
|
252
|
+
safeRegister("access-control", registerAccessControlTools);
|
|
253
|
+
safeRegister("encryption", registerEncryptionTools);
|
|
254
|
+
safeRegister("container-security", registerContainerSecurityTools);
|
|
255
|
+
safeRegister("meta", registerMetaTools);
|
|
256
|
+
safeRegister("patch-management", registerPatchManagementTools);
|
|
257
|
+
safeRegister("secrets", registerSecretsTools);
|
|
258
|
+
safeRegister("incident-response", registerIncidentResponseTools);
|
|
259
|
+
// New tool modules
|
|
260
|
+
safeRegister("supply-chain-security", registerSupplyChainSecurityTools);
|
|
261
|
+
safeRegister("zero-trust-network", registerZeroTrustNetworkTools);
|
|
262
|
+
safeRegister("ebpf-security", registerEbpfSecurityTools);
|
|
263
|
+
safeRegister("app-hardening", registerAppHardeningTools);
|
|
264
|
+
// v0.6.0 tool modules
|
|
265
|
+
safeRegister("api-security", registerApiSecurityTools);
|
|
266
|
+
safeRegister("cloud-security", registerCloudSecurityTools);
|
|
267
|
+
safeRegister("deception", registerDeceptionTools);
|
|
268
|
+
safeRegister("dns-security", registerDnsSecurityTools);
|
|
269
|
+
safeRegister("process-security", registerProcessSecurityTools);
|
|
270
|
+
safeRegister("threat-intel", registerThreatIntelTools);
|
|
271
|
+
safeRegister("vulnerability-management", registerVulnerabilityManagementTools);
|
|
272
|
+
safeRegister("waf", registerWafTools);
|
|
273
|
+
safeRegister("wireless-security", registerWirelessSecurityTools);
|
|
274
|
+
return { server: srv, registered, failed, failedModules };
|
|
235
275
|
}
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
safeRegister("firewall", registerFirewallTools);
|
|
240
|
-
safeRegister("hardening", registerHardeningTools);
|
|
241
|
-
safeRegister("integrity", registerIntegrityTools);
|
|
242
|
-
safeRegister("logging", registerLoggingTools);
|
|
243
|
-
safeRegister("network-defense", registerNetworkDefenseTools);
|
|
244
|
-
safeRegister("compliance", registerComplianceTools);
|
|
245
|
-
safeRegister("malware", registerMalwareTools);
|
|
246
|
-
safeRegister("backup", registerBackupTools);
|
|
247
|
-
safeRegister("access-control", registerAccessControlTools);
|
|
248
|
-
safeRegister("encryption", registerEncryptionTools);
|
|
249
|
-
safeRegister("container-security", registerContainerSecurityTools);
|
|
250
|
-
safeRegister("meta", registerMetaTools);
|
|
251
|
-
safeRegister("patch-management", registerPatchManagementTools);
|
|
252
|
-
safeRegister("secrets", registerSecretsTools);
|
|
253
|
-
safeRegister("incident-response", registerIncidentResponseTools);
|
|
254
|
-
// New tool modules
|
|
255
|
-
safeRegister("supply-chain-security", registerSupplyChainSecurityTools);
|
|
256
|
-
safeRegister("zero-trust-network", registerZeroTrustNetworkTools);
|
|
257
|
-
safeRegister("ebpf-security", registerEbpfSecurityTools);
|
|
258
|
-
safeRegister("app-hardening", registerAppHardeningTools);
|
|
259
|
-
// v0.6.0 tool modules
|
|
260
|
-
safeRegister("api-security", registerApiSecurityTools);
|
|
261
|
-
safeRegister("cloud-security", registerCloudSecurityTools);
|
|
262
|
-
safeRegister("deception", registerDeceptionTools);
|
|
263
|
-
safeRegister("dns-security", registerDnsSecurityTools);
|
|
264
|
-
safeRegister("process-security", registerProcessSecurityTools);
|
|
265
|
-
safeRegister("threat-intel", registerThreatIntelTools);
|
|
266
|
-
safeRegister("vulnerability-management", registerVulnerabilityManagementTools);
|
|
267
|
-
safeRegister("waf", registerWafTools);
|
|
268
|
-
safeRegister("wireless-security", registerWirelessSecurityTools);
|
|
269
|
-
// Fail hard if no modules loaded at all
|
|
270
|
-
if (registered === 0) {
|
|
276
|
+
// Validate that tool registration works (fail-fast on startup)
|
|
277
|
+
const initial = registerAllTools();
|
|
278
|
+
if (initial.registered === 0) {
|
|
271
279
|
throw new Error("No tool modules loaded — server cannot start");
|
|
272
280
|
}
|
|
273
281
|
// ── Phase 3: Connect transport ───────────────────────────────────────────
|
|
274
282
|
const transportMode = process.env.MCP_TRANSPORT ?? "stdio";
|
|
275
|
-
const registrationMsg = `Registered ${registered} tool modules with consolidated defensive security tools` +
|
|
276
|
-
`${failed > 0 ? ` (${failed} failed: ${failedModules.join(", ")})` : ""}`;
|
|
283
|
+
const registrationMsg = `Registered ${initial.registered} tool modules with consolidated defensive security tools` +
|
|
284
|
+
`${initial.failed > 0 ? ` (${initial.failed} failed: ${initial.failedModules.join(", ")})` : ""}`;
|
|
277
285
|
if (transportMode === "http") {
|
|
278
286
|
const port = parseInt(process.env.MCP_PORT ?? "3100", 10);
|
|
287
|
+
const apiKey = process.env.MCP_API_KEY ?? "";
|
|
279
288
|
// Server card for Smithery discovery
|
|
280
289
|
const serverCard = {
|
|
281
290
|
serverInfo: {
|
|
@@ -283,7 +292,7 @@ async function main() {
|
|
|
283
292
|
version: VERSION,
|
|
284
293
|
description: "31 defensive security tools (250+ actions) for Linux system hardening, compliance, and threat detection",
|
|
285
294
|
},
|
|
286
|
-
authentication: { required:
|
|
295
|
+
authentication: { required: !!apiKey },
|
|
287
296
|
tools: [
|
|
288
297
|
{ name: "access_control", description: "Access control: SSH, PAM, sudo, user audit, password policy, shell restriction" },
|
|
289
298
|
{ name: "api_security", description: "API security: local API discovery, auth audit, rate limiting, TLS verify, CORS check" },
|
|
@@ -320,63 +329,74 @@ async function main() {
|
|
|
320
329
|
resources: [],
|
|
321
330
|
prompts: [],
|
|
322
331
|
};
|
|
323
|
-
// Track active
|
|
332
|
+
// Track active sessions: each gets its own McpServer + transport pair
|
|
324
333
|
const sessions = new Map();
|
|
325
334
|
const httpServer = createServer(async (req, res) => {
|
|
326
335
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
327
336
|
const pathname = url.pathname;
|
|
328
|
-
// CORS
|
|
337
|
+
// CORS headers
|
|
329
338
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
330
339
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
331
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id, smithery-*");
|
|
340
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, mcp-session-id, smithery-*");
|
|
332
341
|
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
|
|
333
342
|
if (req.method === "OPTIONS") {
|
|
334
343
|
res.writeHead(204).end();
|
|
335
344
|
return;
|
|
336
345
|
}
|
|
337
|
-
// Health check
|
|
346
|
+
// Health check (unauthenticated)
|
|
338
347
|
if (req.method === "GET" && pathname === "/health") {
|
|
339
348
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
340
|
-
res.end(JSON.stringify({ status: "ok", version: VERSION, tools: registered }));
|
|
349
|
+
res.end(JSON.stringify({ status: "ok", version: VERSION, tools: initial.registered }));
|
|
341
350
|
return;
|
|
342
351
|
}
|
|
343
|
-
// Smithery server card
|
|
352
|
+
// Smithery server card (unauthenticated)
|
|
344
353
|
if (req.method === "GET" && pathname === "/.well-known/mcp/server-card.json") {
|
|
345
354
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
346
355
|
res.end(JSON.stringify(serverCard));
|
|
347
356
|
return;
|
|
348
357
|
}
|
|
358
|
+
// API key authentication (when MCP_API_KEY is set)
|
|
359
|
+
if (apiKey) {
|
|
360
|
+
const authHeader = req.headers["authorization"] ?? "";
|
|
361
|
+
const provided = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
362
|
+
if (provided !== apiKey) {
|
|
363
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
364
|
+
res.end(JSON.stringify({ error: "Unauthorized", message: "Valid Bearer token required" }));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
349
368
|
// MCP Streamable HTTP endpoint
|
|
350
369
|
if (pathname === "/mcp" || pathname === "/") {
|
|
351
370
|
const sessionId = req.headers["mcp-session-id"];
|
|
352
371
|
if (req.method === "GET" || req.method === "POST") {
|
|
353
|
-
// Reuse existing session or create new
|
|
354
|
-
let
|
|
355
|
-
if (!
|
|
356
|
-
|
|
372
|
+
// Reuse existing session or create a new McpServer + transport pair
|
|
373
|
+
let session = sessionId ? sessions.get(sessionId) : undefined;
|
|
374
|
+
if (!session) {
|
|
375
|
+
const { server: sessionServer } = registerAllTools();
|
|
376
|
+
const transport = new StreamableHTTPServerTransport({
|
|
357
377
|
sessionIdGenerator: () => crypto.randomUUID(),
|
|
358
378
|
});
|
|
359
|
-
await
|
|
360
|
-
|
|
379
|
+
await sessionServer.connect(transport);
|
|
380
|
+
session = { server: sessionServer, transport };
|
|
361
381
|
transport.onclose = () => {
|
|
362
|
-
const sid = [...sessions.entries()].find(([,
|
|
382
|
+
const sid = [...sessions.entries()].find(([, s]) => s.transport === transport)?.[0];
|
|
363
383
|
if (sid)
|
|
364
384
|
sessions.delete(sid);
|
|
365
385
|
};
|
|
366
386
|
}
|
|
367
|
-
await transport.handleRequest(req, res);
|
|
387
|
+
await session.transport.handleRequest(req, res);
|
|
368
388
|
// Capture session ID from response headers if new session
|
|
369
389
|
if (!sessionId) {
|
|
370
390
|
const newSid = res.getHeader("mcp-session-id");
|
|
371
|
-
if (newSid &&
|
|
372
|
-
sessions.set(newSid,
|
|
391
|
+
if (newSid && session)
|
|
392
|
+
sessions.set(newSid, session);
|
|
373
393
|
}
|
|
374
394
|
return;
|
|
375
395
|
}
|
|
376
396
|
if (req.method === "DELETE") {
|
|
377
397
|
if (sessionId && sessions.has(sessionId)) {
|
|
378
|
-
const
|
|
379
|
-
await transport.handleRequest(req, res);
|
|
398
|
+
const session = sessions.get(sessionId);
|
|
399
|
+
await session.transport.handleRequest(req, res);
|
|
380
400
|
sessions.delete(sessionId);
|
|
381
401
|
}
|
|
382
402
|
else {
|
|
@@ -390,14 +410,16 @@ async function main() {
|
|
|
390
410
|
httpServer.listen(port, () => {
|
|
391
411
|
console.error(`Defense MCP Server v${VERSION} running on HTTP port ${port}`);
|
|
392
412
|
console.error(registrationMsg);
|
|
413
|
+
console.error(`[startup] Authentication: ${apiKey ? "ENABLED (MCP_API_KEY)" : "DISABLED — set MCP_API_KEY to require Bearer auth"}`);
|
|
393
414
|
console.error("[startup] MCP endpoint: http://0.0.0.0:" + port + "/mcp");
|
|
394
415
|
console.error("[startup] Health check: http://0.0.0.0:" + port + "/health");
|
|
395
416
|
console.error("[startup] Server card: http://0.0.0.0:" + port + "/.well-known/mcp/server-card.json");
|
|
396
417
|
});
|
|
397
418
|
}
|
|
398
419
|
else {
|
|
420
|
+
// stdio mode: single server instance, single transport
|
|
399
421
|
const transport = new StdioServerTransport();
|
|
400
|
-
await server.connect(transport);
|
|
422
|
+
await initial.server.connect(transport);
|
|
401
423
|
console.error(`Defense MCP Server v${VERSION} running on stdio`);
|
|
402
424
|
console.error(registrationMsg);
|
|
403
425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "defense-mcp-server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Defense MCP Server — 31 defensive security tools with 250+ actions for system hardening, compliance, and threat detection on Linux",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|