mcp-nervous-system 1.4.0 → 1.5.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/index.js +217 -6
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -129,7 +129,7 @@ const MCP_VERSION = '2024-11-05';
|
|
|
129
129
|
// Server info
|
|
130
130
|
const SERVER_INFO = {
|
|
131
131
|
name: 'nervous-system',
|
|
132
|
-
version: '1.
|
|
132
|
+
version: '1.5.0'
|
|
133
133
|
};
|
|
134
134
|
|
|
135
135
|
// ============================================================
|
|
@@ -138,7 +138,7 @@ const SERVER_INFO = {
|
|
|
138
138
|
|
|
139
139
|
const FRAMEWORK = {
|
|
140
140
|
name: 'The Nervous System',
|
|
141
|
-
version: '1.
|
|
141
|
+
version: '1.5.0',
|
|
142
142
|
author: 'Arthur Palyan',
|
|
143
143
|
tagline: 'Anthropic built the brain. Arthur built the nervous system that keeps it from hurting itself.',
|
|
144
144
|
problem: 'LLMs lose context between sessions, loop on problems instead of dispatching, silently fail without progress notes, edit protected files, drift from the real problem, and solve instead of asking.',
|
|
@@ -616,6 +616,27 @@ const TOOLS = [
|
|
|
616
616
|
}
|
|
617
617
|
}
|
|
618
618
|
}
|
|
619
|
+
},
|
|
620
|
+
// NEW: Security Audit
|
|
621
|
+
{
|
|
622
|
+
name: 'security_audit',
|
|
623
|
+
annotations: { title: 'Security Audit', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
624
|
+
description: 'Scans for security vulnerabilities - hardcoded passwords in HTML, exposed API keys, missing TLS, missing rate limiting, exposed bot tokens, and insecure file permissions.',
|
|
625
|
+
inputSchema: { type: 'object', properties: {} }
|
|
626
|
+
},
|
|
627
|
+
// NEW: Auto Propagate
|
|
628
|
+
{
|
|
629
|
+
name: 'auto_propagate',
|
|
630
|
+
annotations: { title: 'Auto Propagate', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
631
|
+
description: 'Runs all 3 propagators (role, version, content) and reports what changed vs what was already current. Ensures all downstream files match source-of-truth values.',
|
|
632
|
+
inputSchema: { type: 'object', properties: {} }
|
|
633
|
+
},
|
|
634
|
+
// NEW: Session Close
|
|
635
|
+
{
|
|
636
|
+
name: 'session_close',
|
|
637
|
+
annotations: { title: 'Session Close', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
638
|
+
description: 'One-call session close. Runs drift_audit scope=full, then all 3 propagators. Returns combined results. The end-of-session button.',
|
|
639
|
+
inputSchema: { type: 'object', properties: {} }
|
|
619
640
|
}
|
|
620
641
|
];
|
|
621
642
|
|
|
@@ -976,7 +997,7 @@ function auditWebsite() {
|
|
|
976
997
|
// Source of truth values
|
|
977
998
|
const pkgFile = '/root/github-repos/mcp-nervous-system/package.json';
|
|
978
999
|
const pkg = safeReadJSON(pkgFile);
|
|
979
|
-
const expectedVersion = pkg ? pkg.version : '1.
|
|
1000
|
+
const expectedVersion = pkg ? pkg.version : '1.5.0';
|
|
980
1001
|
const actualToolCount = TOOLS.length;
|
|
981
1002
|
const actualResourceCount = RESOURCES.length;
|
|
982
1003
|
|
|
@@ -1184,6 +1205,177 @@ function runDriftAudit(scope) {
|
|
|
1184
1205
|
};
|
|
1185
1206
|
}
|
|
1186
1207
|
|
|
1208
|
+
// ============================================================
|
|
1209
|
+
// SECURITY AUDIT ENGINE
|
|
1210
|
+
// ============================================================
|
|
1211
|
+
|
|
1212
|
+
function runSecurityAudit() {
|
|
1213
|
+
const vulnerabilities = [];
|
|
1214
|
+
let checksPassed = 0;
|
|
1215
|
+
|
|
1216
|
+
// 1. Scan HTML files for hardcoded passwords/secrets
|
|
1217
|
+
const htmlDir = '/root/family-home/';
|
|
1218
|
+
const secretPatterns = [
|
|
1219
|
+
/Liarzhek1\$/g,
|
|
1220
|
+
/Levelsofself1\$/g,
|
|
1221
|
+
/\d{10}:AA[A-Za-z0-9_-]{30,}/g,
|
|
1222
|
+
/sk-ant-[a-zA-Z0-9_-]+/g,
|
|
1223
|
+
/npm_[A-Za-z0-9]{20,}/g,
|
|
1224
|
+
/ghp_[A-Za-z0-9]{20,}/g,
|
|
1225
|
+
/BOT_TOKEN\s*[:=]\s*['"][^'"]+['"]/gi,
|
|
1226
|
+
/PAPA_FULL\s*[:=]\s*['"][^'"]+['"]/gi,
|
|
1227
|
+
/PAPA_READ\s*[:=]\s*['"][^'"]+['"]/gi
|
|
1228
|
+
];
|
|
1229
|
+
try {
|
|
1230
|
+
const htmlFiles = fs.readdirSync(htmlDir).filter(f => f.endsWith('.html'));
|
|
1231
|
+
for (const hf of htmlFiles) {
|
|
1232
|
+
const content = safeReadFile(htmlDir + hf);
|
|
1233
|
+
if (!content) continue;
|
|
1234
|
+
let fileClean = true;
|
|
1235
|
+
for (const pat of secretPatterns) {
|
|
1236
|
+
pat.lastIndex = 0;
|
|
1237
|
+
const matches = content.match(pat);
|
|
1238
|
+
if (matches && matches.length > 0) {
|
|
1239
|
+
vulnerabilities.push({ type: 'hardcoded_secret', file: hf, pattern: pat.source, count: matches.length });
|
|
1240
|
+
fileClean = false;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
if (fileClean) checksPassed++;
|
|
1244
|
+
}
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
vulnerabilities.push({ type: 'scan_error', file: 'html_scan', detail: e.message });
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// 2. Check auth endpoints use server-side validation
|
|
1250
|
+
const serverContent = safeReadFile('/root/family-home/server.js');
|
|
1251
|
+
if (serverContent) {
|
|
1252
|
+
if (serverContent.includes('getSessionFromReq') || serverContent.includes('getAccessLevel')) {
|
|
1253
|
+
checksPassed++;
|
|
1254
|
+
} else {
|
|
1255
|
+
vulnerabilities.push({ type: 'missing_server_auth', file: 'server.js', detail: 'No server-side auth validation found' });
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// 3. Verify GUEST_HIDDEN_FILES covers sensitive files
|
|
1260
|
+
if (serverContent) {
|
|
1261
|
+
const sensitiveFiles = ['api-credentials.json', 'family-roles.json', 'system-config.json', 'llm-providers.json'];
|
|
1262
|
+
for (const sf of sensitiveFiles) {
|
|
1263
|
+
if (serverContent.includes('"' + sf + '"') || serverContent.includes("'" + sf + "'")) {
|
|
1264
|
+
checksPassed++;
|
|
1265
|
+
} else {
|
|
1266
|
+
vulnerabilities.push({ type: 'unhidden_sensitive_file', file: sf, detail: 'Not in GUEST_HIDDEN_FILES' });
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// 4. Check Caddy TLS
|
|
1272
|
+
const caddyContent = safeReadFile('/etc/caddy/Caddyfile');
|
|
1273
|
+
if (caddyContent) {
|
|
1274
|
+
if (caddyContent.includes('tls') || caddyContent.includes('https://') || caddyContent.includes('100levelup.com')) {
|
|
1275
|
+
checksPassed++;
|
|
1276
|
+
} else {
|
|
1277
|
+
vulnerabilities.push({ type: 'missing_tls', file: 'Caddyfile', detail: 'No TLS configuration found' });
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// 5. Check bridge rate limiting
|
|
1282
|
+
if (serverContent && serverContent.includes('rate') || fs.existsSync('/root/rate-limit.js') || fs.existsSync('/root/bridge-ratelimit.js')) {
|
|
1283
|
+
checksPassed++;
|
|
1284
|
+
} else {
|
|
1285
|
+
vulnerabilities.push({ type: 'missing_rate_limit', file: 'bridge', detail: 'No rate limiting found for bridge' });
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// 6. Check bot tokens not in public HTML
|
|
1289
|
+
try {
|
|
1290
|
+
const htmlFiles = fs.readdirSync(htmlDir).filter(f => f.endsWith('.html'));
|
|
1291
|
+
let tokenFound = false;
|
|
1292
|
+
for (const hf of htmlFiles) {
|
|
1293
|
+
const content = safeReadFile(htmlDir + hf);
|
|
1294
|
+
if (!content) continue;
|
|
1295
|
+
const tokenMatch = content.match(/\d{10}:AA[A-Za-z0-9_-]{30,}/g);
|
|
1296
|
+
if (tokenMatch) {
|
|
1297
|
+
vulnerabilities.push({ type: 'exposed_bot_token', file: hf, count: tokenMatch.length });
|
|
1298
|
+
tokenFound = true;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (!tokenFound) checksPassed++;
|
|
1302
|
+
} catch (e) {}
|
|
1303
|
+
|
|
1304
|
+
// 7. Check api-credentials.json permissions
|
|
1305
|
+
try {
|
|
1306
|
+
const credFile = '/root/family-data/api-credentials.json';
|
|
1307
|
+
if (fs.existsSync(credFile)) {
|
|
1308
|
+
const stats = fs.statSync(credFile);
|
|
1309
|
+
const mode = (stats.mode & 0o777).toString(8);
|
|
1310
|
+
if (mode === '600') {
|
|
1311
|
+
checksPassed++;
|
|
1312
|
+
} else {
|
|
1313
|
+
vulnerabilities.push({ type: 'insecure_permissions', file: 'api-credentials.json', detail: 'Mode is ' + mode + ', should be 600' });
|
|
1314
|
+
}
|
|
1315
|
+
} else {
|
|
1316
|
+
checksPassed++; // No creds file = no risk
|
|
1317
|
+
}
|
|
1318
|
+
} catch (e) {}
|
|
1319
|
+
|
|
1320
|
+
// 8. Check for Telegram tokens, API keys, npm tokens in family-home
|
|
1321
|
+
try {
|
|
1322
|
+
const allFiles = fs.readdirSync(htmlDir);
|
|
1323
|
+
const dangerPatterns = [
|
|
1324
|
+
{ name: 'telegram_token', pat: /\d{10}:AA[A-Za-z0-9_-]{30,}/g },
|
|
1325
|
+
{ name: 'anthropic_key', pat: /sk-ant-[a-zA-Z0-9_-]{20,}/g },
|
|
1326
|
+
{ name: 'npm_token', pat: /npm_[A-Za-z0-9]{20,}/g }
|
|
1327
|
+
];
|
|
1328
|
+
for (const f of allFiles) {
|
|
1329
|
+
if (f.endsWith('.html') || f.endsWith('.js') || f.endsWith('.json')) {
|
|
1330
|
+
const content = safeReadFile(htmlDir + f);
|
|
1331
|
+
if (!content) continue;
|
|
1332
|
+
for (const dp of dangerPatterns) {
|
|
1333
|
+
dp.pat.lastIndex = 0;
|
|
1334
|
+
const m = content.match(dp.pat);
|
|
1335
|
+
if (m) {
|
|
1336
|
+
vulnerabilities.push({ type: 'exposed_' + dp.name, file: f, count: m.length });
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
checksPassed++;
|
|
1342
|
+
} catch (e) {}
|
|
1343
|
+
|
|
1344
|
+
return {
|
|
1345
|
+
status: vulnerabilities.length === 0 ? 'secure' : 'vulnerabilities_found',
|
|
1346
|
+
vulnerability_count: vulnerabilities.length,
|
|
1347
|
+
checks_passed: checksPassed,
|
|
1348
|
+
vulnerabilities
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// ============================================================
|
|
1353
|
+
// AUTO PROPAGATE ENGINE
|
|
1354
|
+
// ============================================================
|
|
1355
|
+
|
|
1356
|
+
function runAutoPropagators() {
|
|
1357
|
+
const results = [];
|
|
1358
|
+
const scripts = [
|
|
1359
|
+
{ name: 'role', path: '/root/family-workers/role-propagator.js' },
|
|
1360
|
+
{ name: 'version', path: '/root/family-workers/version-propagator.js' },
|
|
1361
|
+
{ name: 'content', path: '/root/family-workers/content-propagator.js' }
|
|
1362
|
+
];
|
|
1363
|
+
for (const script of scripts) {
|
|
1364
|
+
try {
|
|
1365
|
+
const out = execSync('node ' + script.path + ' 2>&1', { timeout: 15000 }).toString();
|
|
1366
|
+
const current = out.indexOf('Already current') !== -1;
|
|
1367
|
+
results.push({ propagator: script.name, status: current ? 'current' : 'updated', output: out.trim().substring(0, 500) });
|
|
1368
|
+
} catch (e) {
|
|
1369
|
+
results.push({ propagator: script.name, status: 'error', error: e.message.substring(0, 200) });
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return {
|
|
1373
|
+
timestamp: new Date().toISOString(),
|
|
1374
|
+
propagators_run: results.length,
|
|
1375
|
+
results
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1187
1379
|
// ============================================================
|
|
1188
1380
|
// Handle tool calls
|
|
1189
1381
|
// ============================================================
|
|
@@ -1286,6 +1478,25 @@ function handleToolCall(name, args) {
|
|
|
1286
1478
|
return runDriftAudit(scope);
|
|
1287
1479
|
}
|
|
1288
1480
|
|
|
1481
|
+
case 'security_audit': {
|
|
1482
|
+
return runSecurityAudit();
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
case 'auto_propagate': {
|
|
1486
|
+
return runAutoPropagators();
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
case 'session_close': {
|
|
1490
|
+
const driftResult = runDriftAudit('full');
|
|
1491
|
+
const propagateResult = runAutoPropagators();
|
|
1492
|
+
return {
|
|
1493
|
+
timestamp: new Date().toISOString(),
|
|
1494
|
+
drift_audit: driftResult,
|
|
1495
|
+
propagation: propagateResult,
|
|
1496
|
+
summary: driftResult.drift_count === 0 ? 'Session clean - no drifts, propagators run' : `${driftResult.drift_count} drifts found - review before closing`
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1289
1500
|
default:
|
|
1290
1501
|
return { error: 'Unknown tool' };
|
|
1291
1502
|
}
|
|
@@ -1407,7 +1618,7 @@ const server = http.createServer((req, res) => {
|
|
|
1407
1618
|
// Health check
|
|
1408
1619
|
if (req.method === 'GET' && url.pathname === '/health') {
|
|
1409
1620
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1410
|
-
res.end(JSON.stringify({ status: 'ok', service: 'nervous-system-mcp', version: '1.
|
|
1621
|
+
res.end(JSON.stringify({ status: 'ok', service: 'nervous-system-mcp', version: '1.5.0', protocol: MCP_VERSION }));
|
|
1411
1622
|
return;
|
|
1412
1623
|
}
|
|
1413
1624
|
|
|
@@ -1524,7 +1735,7 @@ const server = http.createServer((req, res) => {
|
|
|
1524
1735
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1525
1736
|
res.end(JSON.stringify({
|
|
1526
1737
|
name: 'The Nervous System MCP Server',
|
|
1527
|
-
version: '1.
|
|
1738
|
+
version: '1.5.0',
|
|
1528
1739
|
protocol: MCP_VERSION,
|
|
1529
1740
|
description: 'LLM behavioral enforcement framework. 7 core rules, preflight checks, session handoffs, worklogs, violation logging, kill switch, hash-chained audit, and forced reflection cycles. Built by Arthur Palyan.',
|
|
1530
1741
|
endpoints: {
|
|
@@ -1546,7 +1757,7 @@ const server = http.createServer((req, res) => {
|
|
|
1546
1757
|
migrateExistingViolations();
|
|
1547
1758
|
|
|
1548
1759
|
server.listen(PORT, '127.0.0.1', () => {
|
|
1549
|
-
console.error(`[MCP Server] Nervous System v1.
|
|
1760
|
+
console.error(`[MCP Server] Nervous System v1.5.0 running on port ${PORT}`);
|
|
1550
1761
|
console.error(`[MCP Server] SSE: /sse | HTTP: /mcp | Health: /health | Kill: POST /kill | Audit: GET /audit/verify | Dispatches: GET /dispatches`);
|
|
1551
1762
|
console.error(`[MCP Server] Protocol: ${MCP_VERSION}`);
|
|
1552
1763
|
console.error(`[MCP Server] Tools: ${TOOLS.length} (including kill switch, audit chain, dispatch, drift audit)`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-nervous-system",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "The Nervous System - LLM Behavioral Enforcement Framework. 7 mechanically enforced rules,
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "The Nervous System - LLM Behavioral Enforcement Framework. 7 mechanically enforced rules, 15 tools including kill switch, audit chain, dispatch, drift audit, security audit, and session close. MCP server for Claude Desktop, Claude Code, and any MCP-compatible client.",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mcp-nervous-system": "./stdio.js"
|