mcp-nervous-system 1.5.1 → 1.6.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  7 mechanically enforced rules that prevent the most common failure modes when LLMs have access to real infrastructure: context loss, silent failures, file damage, goal drift, and overreach.
6
6
 
7
- Built by [Arthur Palyan](https://www.levelsofself.com) at Palyan Family AI System. 15 tools including configuration drift detection and emergency kill switch. Battle-tested on an 11-member AI family running 28 processes 24/7 on a single VPS. 58+ violations logged, 0 bypassed.
7
+ Built by [Arthur Palyan](https://www.levelsofself.com) at Palyan Family AI System. 16 tools including configuration drift detection and emergency kill switch. Battle-tested on an 11-member AI family running 30 processes 24/7 on a single VPS. 58+ violations logged, 0 bypassed.
8
8
 
9
9
  ## The Problem
10
10
 
@@ -60,7 +60,7 @@ Protocol: MCP 2024-11-05 (Streamable HTTP + SSE)
60
60
  Authentication: None required
61
61
  ```
62
62
 
63
- ## NEW in v1.5.0
63
+ ## NEW in v1.5.1
64
64
 
65
65
  **drift_audit** (free tier)
66
66
  Configuration drift detection across 5 scopes: roles, versions, files, processes, and website. Scans source-of-truth files (family-roles.json, package.json, UNTOUCHABLE_FILES.txt) against all downstream references - HTML pages, JSON configs, markdown docs, and running PM2 processes. Change one file, drift_audit tells you everywhere else that needs updating. The closed loop that keeps your entire system consistent.
@@ -0,0 +1,172 @@
1
+ # The Nervous System
2
+
3
+ **LLM Behavioral Enforcement Framework**
4
+
5
+ 7 mechanically enforced rules that prevent the most common failure modes when LLMs have access to real infrastructure: context loss, silent failures, file damage, goal drift, and overreach.
6
+
7
+ Built by [Arthur Palyan](https://www.levelsofself.com) at Palyan Family AI System. 15 tools including configuration drift detection and emergency kill switch. Battle-tested on an 11-member AI family running 28 processes 24/7 on a single VPS. 58+ violations logged, 0 bypassed.
8
+
9
+ ## The Problem
10
+
11
+ When you give an LLM access to your file system, bash, and production infrastructure, it will eventually:
12
+
13
+ - Edit a file it shouldn't touch
14
+ - Lose context between sessions and start over
15
+ - Drift from the original objective during long tasks
16
+ - Fail silently when a session times out
17
+ - Make logic changes without asking
18
+ - Disappear into debug loops
19
+
20
+ The Nervous System solves all of these with rules enforced by external mechanisms the LLM cannot override.
21
+
22
+ ## Install
23
+
24
+ ### Claude Desktop
25
+
26
+ Add to your `claude_desktop_config.json`:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "nervous-system": {
32
+ "command": "npx",
33
+ "args": ["-y", "mcp-nervous-system"]
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### Claude Code
40
+
41
+ ```bash
42
+ claude mcp add nervous-system npx mcp-nervous-system
43
+ ```
44
+
45
+ ### Direct
46
+
47
+ ```bash
48
+ npx mcp-nervous-system
49
+ ```
50
+
51
+ Server starts on port 3475 with SSE, HTTP, and health endpoints.
52
+
53
+ ### Hosted (No Install)
54
+
55
+ The server is live and ready to use:
56
+
57
+ ```
58
+ URL: https://api.100levelup.com/mcp-ns/
59
+ Protocol: MCP 2024-11-05 (Streamable HTTP + SSE)
60
+ Authentication: None required
61
+ ```
62
+
63
+ ## NEW in v1.5.0
64
+
65
+ **drift_audit** (free tier)
66
+ Configuration drift detection across 5 scopes: roles, versions, files, processes, and website. Scans source-of-truth files (family-roles.json, package.json, UNTOUCHABLE_FILES.txt) against all downstream references - HTML pages, JSON configs, markdown docs, and running PM2 processes. Change one file, drift_audit tells you everywhere else that needs updating. The closed loop that keeps your entire system consistent.
67
+
68
+ **Positioning: Auto mode decides what Claude CAN do. The Nervous System governs HOW it behaves while doing it.**
69
+
70
+ ## The 7 Rules
71
+
72
+ | # | Rule | What It Prevents |
73
+ |---|------|-----------------|
74
+ | 1 | **Dispatch Don't Do** | Debug loops, rabbit holes. Tasks > 2 messages get dispatched. |
75
+ | 2 | **Untouchable** | File damage. Protected files mechanically blocked from editing. |
76
+ | 3 | **Write Progress** | Silent failures. Progress noted before each action. |
77
+ | 4 | **Step Back Every 4** | Goal drift. Forced reflection every 4 messages. |
78
+ | 5 | **Delegate and Return** | Invisible work. Background tasks reported immediately. |
79
+ | 6 | **Ask Before Touching** | Unauthorized changes. Logic changes need human approval. |
80
+ | 7 | **Hand Off** | Context loss. Written handoffs every 3-4 exchanges. |
81
+
82
+ ## MCP Tools (12)
83
+
84
+ | Tool | Description |
85
+ |------|------------|
86
+ | `get_framework` | Complete framework: all rules, permission protocol, enforcement patterns |
87
+ | `guardrail_rules` | The 7 core rules with triggers, enforcement, and failure modes |
88
+ | `preflight_check` | File protection system: shell script blocks edits to protected files |
89
+ | `session_handoff` | Context preservation: templates for handoff documents |
90
+ | `worklog` | Progress documentation pattern |
91
+ | `violation_logging` | Audit trail: timestamp, type, context for every violation |
92
+ | `step_back_check` | Forced reflection system |
93
+ | `get_nervous_system_info` | System overview and operational stats |
94
+ | `emergency_kill_switch` | Emergency shutdown of all PM2 processes. Requires kill switch secret. Logs to tamper-evident audit trail |
95
+ | `verify_audit_chain` | Walks the SHA-256 hash-chained audit log and verifies every entry. Returns chain integrity status |
96
+ | `dispatch_to_llm` | Spawns a background LLM agent to handle a task. Checks RAM, enforces max 2 concurrent dispatches |
97
+ | `drift_audit` | Configuration drift detection across roles, versions, files, processes, and website. Finds stale values everywhere. |
98
+
99
+ ## Kill Switch
100
+
101
+ The `emergency_kill_switch` tool provides an emergency shutdown capability. Send a POST request to `/kill` with the kill switch secret to immediately stop all PM2 processes. Every activation is logged to the tamper-evident audit trail with SHA-256 hash chaining, so kill switch events cannot be hidden or altered after the fact.
102
+
103
+ - Requires authentication (kill switch secret)
104
+ - Logs to hash-chained audit trail
105
+ - Returns confirmation with affected process count
106
+
107
+ ## Tamper-Evident Audit Trail
108
+
109
+ Every guardrail violation, kill switch activation, and dispatch event is recorded in a SHA-256 hash-chained audit log. Each entry includes the hash of the previous entry, making it cryptographically impossible to alter or delete past records without breaking the chain.
110
+
111
+ - Use `verify_audit_chain` to walk the entire chain and verify integrity
112
+ - Returns: valid/invalid status, entry count, and break point if tampered
113
+ - 58+ violations logged, 0 bypassed, 0 chain breaks
114
+
115
+ ## Dispatch to LLM
116
+
117
+ The `dispatch_to_llm` tool enables a brain + agents architecture. Instead of one LLM session doing everything, complex tasks get dispatched to background agents that run independently under the same 7 rules.
118
+
119
+ - Checks available RAM (requires 500MB+)
120
+ - Enforces max 2 concurrent dispatches
121
+ - Returns PID and log file path for monitoring
122
+ - Every dispatched agent runs under the same nervous system guardrails
123
+
124
+ ## EU AI Act Compliance
125
+
126
+ The Nervous System provides practical compliance tools for the EU AI Act. See the full compliance page at:
127
+
128
+ https://api.100levelup.com/family/eu-ai-act.html
129
+
130
+ ## Resources (5)
131
+
132
+ - `nervous-system://framework` - The complete framework
133
+ - `nervous-system://quick-start` - Quick start guide
134
+ - `nervous-system://rules` - The 7 core rules
135
+ - `nervous-system://templates` - Templates for handoffs, worklogs, preflight
136
+ - `nervous-system://drift-audit` - Configuration drift detection
137
+
138
+ ## Production Stats
139
+
140
+ From the live Palyan Family AI System deployment (Feb 28 - Mar 5, 2026):
141
+
142
+ - **58+** violations caught
143
+ - **29** edits blocked by preflight
144
+ - **13** unique files protected
145
+ - **0** rules bypassed
146
+ - **28** processes monitored
147
+ - **7** days continuous operation
148
+
149
+ ## Live Demo
150
+
151
+ Try it yourself (no login required):
152
+
153
+ - **[Interactive Demo](https://api.100levelup.com/family/arthur.html?guest=1)** - Talk to a governed LLM and try to break the rules
154
+ - **[Audit Dashboard](https://api.100levelup.com/family/audit.html)** - See real violation history with timeline
155
+ - **[System Status](https://api.100levelup.com/family/status.html)** - Live health checks
156
+ - **[API Documentation](https://api.100levelup.com/family/api-docs.html)** - Full tool and resource reference
157
+ - **[Case Study](https://api.100levelup.com/family/case-study.html)** - Production deployment data
158
+ - **[Plain English Rules](https://api.100levelup.com/family/rules-plain.html)** - For non-technical stakeholders
159
+ - **[Incident Response](https://api.100levelup.com/family/incident-response.html)** - Detection, containment, resolution
160
+ - **[EU AI Act Compliance](https://api.100levelup.com/family/eu-ai-act.html)** - Practical EU AI Act compliance tools
161
+
162
+ ## Philosophy
163
+
164
+ > "LLMs can't reliably self-enforce promises. Guardrails work via preflight.sh, violation logs, and catching drift. Build enforcement systems, don't make promises."
165
+
166
+ If a guardrail can be violated by the thing it guards, it is not a guardrail. It is a suggestion.
167
+
168
+ Every rule in the Nervous System is enforced by an external mechanism: a shell script, a timer, a separate monitoring process. The LLM cannot override, circumvent, or ignore them.
169
+
170
+ ## License
171
+
172
+ MIT
@@ -0,0 +1,172 @@
1
+ # The Nervous System
2
+
3
+ **LLM Behavioral Enforcement Framework**
4
+
5
+ 7 mechanically enforced rules that prevent the most common failure modes when LLMs have access to real infrastructure: context loss, silent failures, file damage, goal drift, and overreach.
6
+
7
+ Built by [Arthur Palyan](https://www.levelsofself.com) at Palyan Family AI System. 15 tools including configuration drift detection and emergency kill switch. Battle-tested on an 11-member AI family running 30 processes 24/7 on a single VPS. 58+ violations logged, 0 bypassed.
8
+
9
+ ## The Problem
10
+
11
+ When you give an LLM access to your file system, bash, and production infrastructure, it will eventually:
12
+
13
+ - Edit a file it shouldn't touch
14
+ - Lose context between sessions and start over
15
+ - Drift from the original objective during long tasks
16
+ - Fail silently when a session times out
17
+ - Make logic changes without asking
18
+ - Disappear into debug loops
19
+
20
+ The Nervous System solves all of these with rules enforced by external mechanisms the LLM cannot override.
21
+
22
+ ## Install
23
+
24
+ ### Claude Desktop
25
+
26
+ Add to your `claude_desktop_config.json`:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "nervous-system": {
32
+ "command": "npx",
33
+ "args": ["-y", "mcp-nervous-system"]
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### Claude Code
40
+
41
+ ```bash
42
+ claude mcp add nervous-system npx mcp-nervous-system
43
+ ```
44
+
45
+ ### Direct
46
+
47
+ ```bash
48
+ npx mcp-nervous-system
49
+ ```
50
+
51
+ Server starts on port 3475 with SSE, HTTP, and health endpoints.
52
+
53
+ ### Hosted (No Install)
54
+
55
+ The server is live and ready to use:
56
+
57
+ ```
58
+ URL: https://api.100levelup.com/mcp-ns/
59
+ Protocol: MCP 2024-11-05 (Streamable HTTP + SSE)
60
+ Authentication: None required
61
+ ```
62
+
63
+ ## NEW in v1.5.0
64
+
65
+ **drift_audit** (free tier)
66
+ Configuration drift detection across 5 scopes: roles, versions, files, processes, and website. Scans source-of-truth files (family-roles.json, package.json, UNTOUCHABLE_FILES.txt) against all downstream references - HTML pages, JSON configs, markdown docs, and running PM2 processes. Change one file, drift_audit tells you everywhere else that needs updating. The closed loop that keeps your entire system consistent.
67
+
68
+ **Positioning: Auto mode decides what Claude CAN do. The Nervous System governs HOW it behaves while doing it.**
69
+
70
+ ## The 7 Rules
71
+
72
+ | # | Rule | What It Prevents |
73
+ |---|------|-----------------|
74
+ | 1 | **Dispatch Don't Do** | Debug loops, rabbit holes. Tasks > 2 messages get dispatched. |
75
+ | 2 | **Untouchable** | File damage. Protected files mechanically blocked from editing. |
76
+ | 3 | **Write Progress** | Silent failures. Progress noted before each action. |
77
+ | 4 | **Step Back Every 4** | Goal drift. Forced reflection every 4 messages. |
78
+ | 5 | **Delegate and Return** | Invisible work. Background tasks reported immediately. |
79
+ | 6 | **Ask Before Touching** | Unauthorized changes. Logic changes need human approval. |
80
+ | 7 | **Hand Off** | Context loss. Written handoffs every 3-4 exchanges. |
81
+
82
+ ## MCP Tools (12)
83
+
84
+ | Tool | Description |
85
+ |------|------------|
86
+ | `get_framework` | Complete framework: all rules, permission protocol, enforcement patterns |
87
+ | `guardrail_rules` | The 7 core rules with triggers, enforcement, and failure modes |
88
+ | `preflight_check` | File protection system: shell script blocks edits to protected files |
89
+ | `session_handoff` | Context preservation: templates for handoff documents |
90
+ | `worklog` | Progress documentation pattern |
91
+ | `violation_logging` | Audit trail: timestamp, type, context for every violation |
92
+ | `step_back_check` | Forced reflection system |
93
+ | `get_nervous_system_info` | System overview and operational stats |
94
+ | `emergency_kill_switch` | Emergency shutdown of all PM2 processes. Requires kill switch secret. Logs to tamper-evident audit trail |
95
+ | `verify_audit_chain` | Walks the SHA-256 hash-chained audit log and verifies every entry. Returns chain integrity status |
96
+ | `dispatch_to_llm` | Spawns a background LLM agent to handle a task. Checks RAM, enforces max 2 concurrent dispatches |
97
+ | `drift_audit` | Configuration drift detection across roles, versions, files, processes, and website. Finds stale values everywhere. |
98
+
99
+ ## Kill Switch
100
+
101
+ The `emergency_kill_switch` tool provides an emergency shutdown capability. Send a POST request to `/kill` with the kill switch secret to immediately stop all PM2 processes. Every activation is logged to the tamper-evident audit trail with SHA-256 hash chaining, so kill switch events cannot be hidden or altered after the fact.
102
+
103
+ - Requires authentication (kill switch secret)
104
+ - Logs to hash-chained audit trail
105
+ - Returns confirmation with affected process count
106
+
107
+ ## Tamper-Evident Audit Trail
108
+
109
+ Every guardrail violation, kill switch activation, and dispatch event is recorded in a SHA-256 hash-chained audit log. Each entry includes the hash of the previous entry, making it cryptographically impossible to alter or delete past records without breaking the chain.
110
+
111
+ - Use `verify_audit_chain` to walk the entire chain and verify integrity
112
+ - Returns: valid/invalid status, entry count, and break point if tampered
113
+ - 58+ violations logged, 0 bypassed, 0 chain breaks
114
+
115
+ ## Dispatch to LLM
116
+
117
+ The `dispatch_to_llm` tool enables a brain + agents architecture. Instead of one LLM session doing everything, complex tasks get dispatched to background agents that run independently under the same 7 rules.
118
+
119
+ - Checks available RAM (requires 500MB+)
120
+ - Enforces max 2 concurrent dispatches
121
+ - Returns PID and log file path for monitoring
122
+ - Every dispatched agent runs under the same nervous system guardrails
123
+
124
+ ## EU AI Act Compliance
125
+
126
+ The Nervous System provides practical compliance tools for the EU AI Act. See the full compliance page at:
127
+
128
+ https://api.100levelup.com/family/eu-ai-act.html
129
+
130
+ ## Resources (5)
131
+
132
+ - `nervous-system://framework` - The complete framework
133
+ - `nervous-system://quick-start` - Quick start guide
134
+ - `nervous-system://rules` - The 7 core rules
135
+ - `nervous-system://templates` - Templates for handoffs, worklogs, preflight
136
+ - `nervous-system://drift-audit` - Configuration drift detection
137
+
138
+ ## Production Stats
139
+
140
+ From the live Palyan Family AI System deployment (Feb 28 - Mar 5, 2026):
141
+
142
+ - **58+** violations caught
143
+ - **29** edits blocked by preflight
144
+ - **13** unique files protected
145
+ - **0** rules bypassed
146
+ - **28** processes monitored
147
+ - **7** days continuous operation
148
+
149
+ ## Live Demo
150
+
151
+ Try it yourself (no login required):
152
+
153
+ - **[Interactive Demo](https://api.100levelup.com/family/arthur.html?guest=1)** - Talk to a governed LLM and try to break the rules
154
+ - **[Audit Dashboard](https://api.100levelup.com/family/audit.html)** - See real violation history with timeline
155
+ - **[System Status](https://api.100levelup.com/family/status.html)** - Live health checks
156
+ - **[API Documentation](https://api.100levelup.com/family/api-docs.html)** - Full tool and resource reference
157
+ - **[Case Study](https://api.100levelup.com/family/case-study.html)** - Production deployment data
158
+ - **[Plain English Rules](https://api.100levelup.com/family/rules-plain.html)** - For non-technical stakeholders
159
+ - **[Incident Response](https://api.100levelup.com/family/incident-response.html)** - Detection, containment, resolution
160
+ - **[EU AI Act Compliance](https://api.100levelup.com/family/eu-ai-act.html)** - Practical EU AI Act compliance tools
161
+
162
+ ## Philosophy
163
+
164
+ > "LLMs can't reliably self-enforce promises. Guardrails work via preflight.sh, violation logs, and catching drift. Build enforcement systems, don't make promises."
165
+
166
+ If a guardrail can be violated by the thing it guards, it is not a guardrail. It is a suggestion.
167
+
168
+ Every rule in the Nervous System is enforced by an external mechanism: a shell script, a timer, a separate monitoring process. The LLM cannot override, circumvent, or ignore them.
169
+
170
+ ## License
171
+
172
+ MIT
package/index.js CHANGED
@@ -5,11 +5,62 @@ const crypto = require('crypto');
5
5
  const { spawn } = require('child_process');
6
6
  const fs = require('fs');
7
7
 
8
+ const path = require('path');
9
+ const os = require('os');
10
+
11
+ // ============================================================
12
+ // PROJECT CONFIGURATION - Auto-discover or use config file
13
+ // ============================================================
14
+
15
+ function loadProjectConfig() {
16
+ const configPaths = [
17
+ process.env.NS_CONFIG_PATH,
18
+ path.join(process.cwd(), 'nervous-system.config.json'),
19
+ path.join(os.homedir(), '.nervous-system', 'config.json'),
20
+ path.join(__dirname, 'nervous-system.config.json'),
21
+ ].filter(Boolean);
22
+
23
+ for (const cp of configPaths) {
24
+ try {
25
+ const raw = fs.readFileSync(cp, 'utf8');
26
+ const cfg = JSON.parse(raw);
27
+ cfg._source = cp;
28
+ return cfg;
29
+ } catch (e) { continue; }
30
+ }
31
+
32
+ // Return defaults that work for any project
33
+ return {
34
+ _source: 'defaults',
35
+ project_root: process.cwd(),
36
+ data_dir: null,
37
+ logs_dir: null,
38
+ html_dir: null,
39
+ protected_files_list: null,
40
+ config_file: null,
41
+ roles_file: null,
42
+ docs_to_audit: [],
43
+ pm2_managed: false,
44
+ html_pages: [],
45
+ package_json: null,
46
+ github_repo: null,
47
+ };
48
+ }
49
+
50
+ const PROJECT = loadProjectConfig();
51
+
52
+ function projectPath(key) {
53
+ const val = PROJECT[key];
54
+ if (!val) return null;
55
+ if (path.isAbsolute(val)) return val;
56
+ return path.join(PROJECT.project_root || process.cwd(), val);
57
+ }
58
+
8
59
  const PORT = 3475;
9
60
 
10
61
  const KILL_SECRET = process.env.KILL_SECRET || 'ns-kill-2026';
11
- const AUDIT_CHAIN_FILE = '/root/family-data/audit-chain.json';
12
- const VIOLATIONS_LOG = '/root/family-logs/guardrail-violations.log';
62
+ const AUDIT_CHAIN_FILE = projectPath('data_dir') ? path.join(projectPath('data_dir'), 'audit-chain.json') : path.join(os.homedir(), '.nervous-system', 'audit-chain.json');
63
+ const VIOLATIONS_LOG = projectPath('logs_dir') ? path.join(projectPath('logs_dir'), 'guardrail-violations.log') : path.join(os.homedir(), '.nervous-system', 'guardrail-violations.log');
13
64
  const GENESIS_HASH = '0000000000000000000000000000000000000000000000000000000000000000';
14
65
  const activeDispatches = [];
15
66
  const MAX_CONCURRENT_DISPATCHES = 2;
@@ -108,7 +159,7 @@ function dispatchToLLM(task, maxTurns) {
108
159
  const freeMB = getFreeMB();
109
160
  if (freeMB < 500) return { dispatched: false, error: `Insufficient RAM: ${freeMB}MB free (need 500MB+)` };
110
161
  const ts = Date.now();
111
- const logFile = `/root/family-logs/dispatch-${ts}.log`;
162
+ const logFile = projectPath('logs_dir') ? `${projectPath('logs_dir')}/dispatch-${ts}.log` : path.join(os.homedir(), '.nervous-system', `dispatch-${ts}.log`);
112
163
  const turns = maxTurns || 15;
113
164
  try {
114
165
  const escaped = task.replace(/"/g, '\\"');
@@ -129,7 +180,7 @@ const MCP_VERSION = '2024-11-05';
129
180
  // Server info
130
181
  const SERVER_INFO = {
131
182
  name: 'nervous-system',
132
- version: '1.5.1'
183
+ version: '1.6.0'
133
184
  };
134
185
 
135
186
  // ============================================================
@@ -138,7 +189,7 @@ const SERVER_INFO = {
138
189
 
139
190
  const FRAMEWORK = {
140
191
  name: 'The Nervous System',
141
- version: '1.5.1',
192
+ version: '1.6.0',
142
193
  author: 'Arthur Palyan',
143
194
  tagline: 'Anthropic built the brain. Arthur built the nervous system that keeps it from hurting itself.',
144
195
  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.',
@@ -475,7 +526,7 @@ const NERVOUS_SYSTEM_INFO = {
475
526
  ]
476
527
  },
477
528
  origin_story: {
478
- context: 'Arthur Palyan runs a startup with 12 AI family members, each with distinct roles. The entire operation runs on a $12/month VPS with a $300/month LLM subscription.',
529
+ context: 'Arthur Palyan runs a startup with 12 AI family members, each with distinct roles. The entire operation runs on a $24/month VPS with a $200/month LLM subscription.',
479
530
  problem_discovered: 'After months of building, patterns emerged: LLMs would break working systems while trying to improve them. They would loop on debugging instead of dispatching. They would silently fail when sessions timed out. They would lose all context between sessions.',
480
531
  solution_built: 'Arthur built the nervous system - not by changing the LLM model, but by wrapping it in behavioral rules enforced through scripts, file checks, and prompt engineering. The LLM itself became the enforcement mechanism, trained to check before acting.',
481
532
  philosophy: 'The brain (LLM) is powerful but needs a nervous system to keep it from hurting itself. Just like a human nervous system sends pain signals before you touch a hot stove, this system sends BLOCKED/PROTECTED signals before the LLM edits a critical file.',
@@ -507,7 +558,7 @@ const NERVOUS_SYSTEM_INFO = {
507
558
  reflection_trigger: 'Every 4 messages',
508
559
  processes_managed: '22+ autonomous PM2 processes',
509
560
  family_members: 12,
510
- monthly_cost: '$352/month total infrastructure',
561
+ monthly_cost: 'Under $300/month total infrastructure',
511
562
  uptime: '24/7 autonomous operation',
512
563
  deployment: 'Single VPS, single LLM subscription'
513
564
  }
@@ -649,6 +700,21 @@ const TOOLS = [
649
700
  page: { type: 'string', description: "Specific page to check (e.g. 'gateway.html'), or 'all' for everything" }
650
701
  }
651
702
  }
703
+ },
704
+ // NEW: Pre-Publish Audit
705
+ {
706
+ name: 'pre_publish_audit',
707
+ annotations: { title: 'Pre-Publish Audit', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
708
+ description: 'Scans the Nervous System source code itself before publishing. Catches hardcoded secrets, personal data, non-portable paths, and internal naming that should not ship to clients. RUN THIS BEFORE EVERY npm publish.',
709
+ inputSchema: {
710
+ type: 'object',
711
+ properties: {
712
+ source_file: {
713
+ type: 'string',
714
+ description: 'Path to the NS source file to audit. Defaults to own index.js'
715
+ }
716
+ }
717
+ }
652
718
  }
653
719
  ];
654
720
 
@@ -686,7 +752,10 @@ function safeReadFile(filePath) {
686
752
  function auditRoles() {
687
753
  const drifts = [];
688
754
  let cleanChecks = 0;
689
- const rolesFile = '/root/family-data/family-roles.json';
755
+ const rolesFile = projectPath('roles_file');
756
+ if (!rolesFile) {
757
+ return { drifts: [], cleanChecks: 0, skipped: 'roles_file not configured' };
758
+ }
690
759
  const roles = safeReadJSON(rolesFile);
691
760
  if (!roles || !roles.members) {
692
761
  drifts.push({ type: 'missing_source', source: rolesFile, target: '', field: '', expected: 'valid JSON with members array', found: 'missing or invalid' });
@@ -699,7 +768,8 @@ function auditRoles() {
699
768
  }
700
769
 
701
770
  // Check family-status.json
702
- const statusFile = '/root/family-data/family-status.json';
771
+ const statusFile = projectPath('data_dir') ? path.join(projectPath('data_dir'), 'family-status.json') : null;
772
+ if (!statusFile) { return { drifts, cleanChecks }; }
703
773
  const status = safeReadJSON(statusFile);
704
774
  if (status && status.members) {
705
775
  for (const m of status.members) {
@@ -715,8 +785,8 @@ function auditRoles() {
715
785
  }
716
786
 
717
787
  // Check system-config.json
718
- const configFile = '/root/family-data/system-config.json';
719
- const config = safeReadJSON(configFile);
788
+ const configFile = projectPath('config_file');
789
+ const config = configFile ? safeReadJSON(configFile) : null;
720
790
  if (config && config.family_members) {
721
791
  for (const m of config.family_members) {
722
792
  const src = sourceRoles[m.id];
@@ -731,8 +801,8 @@ function auditRoles() {
731
801
  }
732
802
 
733
803
  // Check family-guide.json
734
- const guideFile = '/root/family-data/family-guide.json';
735
- const guide = safeReadJSON(guideFile);
804
+ const guideFile = projectPath('data_dir') ? path.join(projectPath('data_dir'), 'family-guide.json') : null;
805
+ const guide = guideFile ? safeReadJSON(guideFile) : null;
736
806
  if (guide && guide.members) {
737
807
  for (const m of guide.members) {
738
808
  const src = sourceRoles[m.id];
@@ -747,11 +817,12 @@ function auditRoles() {
747
817
  }
748
818
 
749
819
  // Check HTML files for role references
750
- const htmlFiles = [
751
- { path: '/root/family-home/index.html', name: 'index.html' },
752
- { path: '/root/family-home/explorer.html', name: 'explorer.html' },
753
- { path: '/root/family-home/meet.html', name: 'meet.html' }
754
- ];
820
+ const htmlDir = projectPath('html_dir');
821
+ const htmlFiles = htmlDir ? [
822
+ { path: path.join(htmlDir, 'index.html'), name: 'index.html' },
823
+ { path: path.join(htmlDir, 'explorer.html'), name: 'explorer.html' },
824
+ { path: path.join(htmlDir, 'meet.html'), name: 'meet.html' }
825
+ ] : [];
755
826
  for (const hf of htmlFiles) {
756
827
  const content = safeReadFile(hf.path);
757
828
  if (!content) continue;
@@ -761,7 +832,7 @@ function auditRoles() {
761
832
  }
762
833
 
763
834
  // Check mcp-ops-server.js
764
- const opsContent = safeReadFile('/root/mcp-ops-server.js');
835
+ const opsContent = projectPath('project_root') ? safeReadFile(path.join(projectPath('project_root') || process.cwd(), 'mcp-ops-server.js')) : null;
765
836
  if (opsContent) {
766
837
  for (const [id, src] of Object.entries(sourceRoles)) {
767
838
  if (opsContent.includes(`"${src.aka}"`) || opsContent.includes(`'${src.aka}'`)) {
@@ -776,7 +847,10 @@ function auditRoles() {
776
847
  function auditVersions() {
777
848
  const drifts = [];
778
849
  let cleanChecks = 0;
779
- const pkgFile = '/root/github-repos/mcp-nervous-system/package.json';
850
+ const pkgFile = projectPath('package_json');
851
+ if (!pkgFile) {
852
+ return { drifts: [], cleanChecks: 0, skipped: 'package_json not configured' };
853
+ }
780
854
  const pkg = safeReadJSON(pkgFile);
781
855
  const expectedVersion = pkg ? pkg.version : null;
782
856
  if (!expectedVersion) {
@@ -785,7 +859,8 @@ function auditVersions() {
785
859
  }
786
860
 
787
861
  // Check SERVER_INFO.version and FRAMEWORK.version in index.js
788
- const indexContent = safeReadFile('/root/github-repos/mcp-nervous-system/index.js');
862
+ const ghRepo = projectPath('github_repo');
863
+ const indexContent = ghRepo ? safeReadFile(path.join(ghRepo, 'index.js')) : null;
789
864
  if (indexContent) {
790
865
  const siMatch = indexContent.match(/SERVER_INFO\s*=\s*\{[^}]*version:\s*'([^']+)'/);
791
866
  if (siMatch) {
@@ -826,7 +901,7 @@ function auditVersions() {
826
901
  }
827
902
 
828
903
  // Check BUSINESS_BUILDER.md
829
- const bbContent = safeReadFile('/root/family-data/BUSINESS_BUILDER.md');
904
+ const bbContent = projectPath('data_dir') ? safeReadFile(path.join(projectPath('data_dir'), 'BUSINESS_BUILDER.md')) : null;
830
905
  if (bbContent) {
831
906
  const bbMatch = bbContent.match(/[Nn]ervous [Ss]ystem.*?v?(\d+\.\d+\.\d+)/);
832
907
  if (bbMatch && bbMatch[1] !== expectedVersion) {
@@ -835,7 +910,7 @@ function auditVersions() {
835
910
  }
836
911
 
837
912
  // Check gateway.html
838
- const gwContent = safeReadFile('/root/family-home/gateway.html');
913
+ const gwContent = projectPath('html_dir') ? safeReadFile(path.join(projectPath('html_dir'), 'gateway.html')) : null;
839
914
  if (gwContent) {
840
915
  const gwMatch = gwContent.match(/[Vv]ersion[:\s]*v?(\d+\.\d+\.\d+)/);
841
916
  if (gwMatch && gwMatch[1] !== expectedVersion) {
@@ -844,7 +919,7 @@ function auditVersions() {
844
919
  }
845
920
 
846
921
  // Check README.md
847
- const readmeContent = safeReadFile('/root/github-repos/mcp-nervous-system/README.md');
922
+ const readmeContent = ghRepo ? safeReadFile(path.join(ghRepo, 'README.md')) : null;
848
923
  if (readmeContent) {
849
924
  const rmMatch = readmeContent.match(/[Vv]ersion[:\s]*v?(\d+\.\d+\.\d+)/);
850
925
  if (rmMatch && rmMatch[1] !== expectedVersion) {
@@ -853,7 +928,7 @@ function auditVersions() {
853
928
  }
854
929
 
855
930
  // Check family-roles.json stats
856
- const roles = safeReadJSON('/root/family-data/family-roles.json');
931
+ const roles = projectPath('roles_file') ? safeReadJSON(projectPath('roles_file')) : null;
857
932
  if (roles && roles.stats) {
858
933
  if (roles.stats.ns_version && roles.stats.ns_version !== expectedVersion) {
859
934
  drifts.push({ type: 'version_mismatch', source: 'package.json', target: 'family-roles.json', field: 'stats.ns_version', expected: expectedVersion, found: roles.stats.ns_version });
@@ -874,10 +949,13 @@ function auditFiles() {
874
949
  let cleanChecks = 0;
875
950
 
876
951
  // Check UNTOUCHABLE_FILES.txt - verify each file exists
877
- const untouchableContent = safeReadFile('/root/family-data/UNTOUCHABLE_FILES.txt');
952
+ const untouchableFile = projectPath('protected_files_list');
953
+ const untouchableContent = untouchableFile ? safeReadFile(untouchableFile) : null;
878
954
  if (untouchableContent) {
879
955
  const lines = untouchableContent.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
880
- for (const filePath of lines) {
956
+ for (const rawLine of lines) {
957
+ const filePath = rawLine.split(/\s*[\(\#]/)[0].trim();
958
+ if (!filePath || !filePath.startsWith('/')) continue;
881
959
  if (fs.existsSync(filePath)) {
882
960
  cleanChecks++;
883
961
  } else {
@@ -887,10 +965,8 @@ function auditFiles() {
887
965
  }
888
966
 
889
967
  // Check LLM_STARTUP.md and BUSINESS_BUILDER.md for file references
890
- const docsToCheck = [
891
- { path: '/root/family-data/LLM_STARTUP.md', name: 'LLM_STARTUP.md' },
892
- { path: '/root/family-data/BUSINESS_BUILDER.md', name: 'BUSINESS_BUILDER.md' }
893
- ];
968
+ const docsToAudit = PROJECT.docs_to_audit || [];
969
+ const docsToCheck = docsToAudit.map(p => ({ path: p, name: path.basename(p) }));
894
970
 
895
971
  // Get PM2 running scripts
896
972
  let pm2Scripts = {};
@@ -906,7 +982,8 @@ function auditFiles() {
906
982
  const content = safeReadFile(doc.path);
907
983
  if (!content) continue;
908
984
  // Look for .js file references
909
- const jsRefs = content.match(/\/root\/[^\s)]+\.js/g) || [];
985
+ // Match .js files but exclude .json, .jsonl, .jsx
986
+ const jsRefs = (content.match(/\/[^\s)]+\.js\b/g) || []).filter(r => !r.match(/\.json[l]?$/));
910
987
  for (const ref of jsRefs) {
911
988
  if (fs.existsSync(ref)) {
912
989
  cleanChecks++;
@@ -928,7 +1005,8 @@ function auditFiles() {
928
1005
  }
929
1006
 
930
1007
  // Check system-config.json syntax_check_scripts
931
- const config = safeReadJSON('/root/family-data/system-config.json');
1008
+ const sysConfigFile = projectPath('config_file');
1009
+ const config = sysConfigFile ? safeReadJSON(sysConfigFile) : null;
932
1010
  if (config && config.syntax_check_scripts) {
933
1011
  for (const script of config.syntax_check_scripts) {
934
1012
  if (fs.existsSync(script)) {
@@ -955,10 +1033,10 @@ function auditProcesses() {
955
1033
  return { drifts, cleanChecks };
956
1034
  }
957
1035
 
958
- const config = safeReadJSON('/root/family-data/system-config.json');
1036
+ const procConfigFile = projectPath('config_file');
1037
+ const config = procConfigFile ? safeReadJSON(procConfigFile) : null;
959
1038
  if (!config || !config.processes) {
960
- // Try to compare against family-roles.json procs
961
- const roles = safeReadJSON('/root/family-data/family-roles.json');
1039
+ const roles = projectPath('roles_file') ? safeReadJSON(projectPath('roles_file')) : null;
962
1040
  if (roles && roles.members) {
963
1041
  const expectedProcs = [];
964
1042
  for (const m of roles.members) {
@@ -1007,18 +1085,19 @@ function auditWebsite() {
1007
1085
  let cleanChecks = 0;
1008
1086
 
1009
1087
  // Source of truth values
1010
- const pkgFile = '/root/github-repos/mcp-nervous-system/package.json';
1011
- const pkg = safeReadJSON(pkgFile);
1012
- const expectedVersion = pkg ? pkg.version : '1.5.0';
1088
+ const pkgFile = projectPath('package_json');
1089
+ const pkg = pkgFile ? safeReadJSON(pkgFile) : null;
1090
+ const expectedVersion = pkg ? pkg.version : SERVER_INFO.version;
1013
1091
  const actualToolCount = TOOLS.length;
1014
1092
  const actualResourceCount = RESOURCES.length;
1015
1093
 
1016
- const roles = safeReadJSON('/root/family-data/family-roles.json');
1094
+ const roles = projectPath('roles_file') ? safeReadJSON(projectPath('roles_file')) : null;
1017
1095
  const expectedMemberCount = roles && roles.stats ? roles.stats.member_count : 11;
1018
1096
  const expectedProcessCount = roles && roles.stats ? roles.stats.process_count : 28;
1019
1097
 
1020
1098
  // Count protected files (non-comment, non-blank lines starting with /)
1021
- const untouchableContent = safeReadFile('/root/family-data/UNTOUCHABLE_FILES.txt');
1099
+ const protListFile = projectPath('protected_files_list');
1100
+ const untouchableContent = protListFile ? safeReadFile(protListFile) : null;
1022
1101
  let protectedFileCount = 0;
1023
1102
  if (untouchableContent) {
1024
1103
  protectedFileCount = untouchableContent.split('\n').filter(l => l.trim() && !l.trim().startsWith('#') && l.trim().startsWith('/')).length;
@@ -1028,7 +1107,10 @@ function auditWebsite() {
1028
1107
  const roleNames = roles && roles.members ? roles.members.map(m => m.name) : [];
1029
1108
 
1030
1109
  // 1. Check all .html files in /root/family-home/
1031
- const familyHomeDir = '/root/family-home/';
1110
+ const familyHomeDir = projectPath('html_dir');
1111
+ if (!familyHomeDir) {
1112
+ return { drifts: [], cleanChecks: 0, skipped: 'html_dir not configured' };
1113
+ }
1032
1114
  let htmlFiles = [];
1033
1115
  try {
1034
1116
  htmlFiles = fs.readdirSync(familyHomeDir).filter(f => f.endsWith('.html')).map(f => familyHomeDir + f);
@@ -1091,7 +1173,8 @@ function auditWebsite() {
1091
1173
  }
1092
1174
 
1093
1175
  // 2. Check family-guide.json
1094
- const guide = safeReadJSON('/root/family-data/family-guide.json');
1176
+ const guideFile2 = projectPath('data_dir') ? path.join(projectPath('data_dir'), 'family-guide.json') : null;
1177
+ const guide = guideFile2 ? safeReadJSON(guideFile2) : null;
1095
1178
  if (guide) {
1096
1179
  const guideStr = JSON.stringify(guide);
1097
1180
  // Check version refs
@@ -1117,7 +1200,7 @@ function auditWebsite() {
1117
1200
  } else { cleanChecks++; }
1118
1201
 
1119
1202
  // 3. Check mcp-stripe-checkout.js for version refs
1120
- const checkoutContent = safeReadFile('/root/mcp-stripe-checkout.js');
1203
+ const checkoutContent = projectPath('project_root') ? safeReadFile(path.join(projectPath('project_root') || process.cwd(), 'mcp-stripe-checkout.js')) : null;
1121
1204
  if (checkoutContent) {
1122
1205
  const checkoutVersions = checkoutContent.match(/v(\d+\.\d+\.\d+)/g) || [];
1123
1206
  for (const cv of checkoutVersions) {
@@ -1130,7 +1213,8 @@ function auditWebsite() {
1130
1213
  }
1131
1214
 
1132
1215
  // 4. Check system-config.json for version/tool counts
1133
- const sysConfig = safeReadJSON('/root/family-data/system-config.json');
1216
+ const sysConfigFile2 = projectPath('config_file');
1217
+ const sysConfig = sysConfigFile2 ? safeReadJSON(sysConfigFile2) : null;
1134
1218
  if (sysConfig) {
1135
1219
  const scStr = JSON.stringify(sysConfig);
1136
1220
  const scVersions = scStr.match(/v(\d+\.\d+\.\d+)/g) || [];
@@ -1144,7 +1228,7 @@ function auditWebsite() {
1144
1228
  }
1145
1229
 
1146
1230
  // 5. Check FREE_TOOLS in mcp-api-middleware.js match actual tool names
1147
- const middlewareContent = safeReadFile('/root/mcp-api-middleware.js');
1231
+ const middlewareContent = projectPath('project_root') ? safeReadFile(path.join(projectPath('project_root') || process.cwd(), 'mcp-api-middleware.js')) : null;
1148
1232
  if (middlewareContent) {
1149
1233
  const freeToolsMatch = middlewareContent.match(/'nervous-system':\s*\[([^\]]+)\]/);
1150
1234
  if (freeToolsMatch) {
@@ -1164,7 +1248,7 @@ function auditWebsite() {
1164
1248
  }
1165
1249
 
1166
1250
  // 6. Check sitemap.xml has all public pages
1167
- const sitemapContent = safeReadFile('/root/family-home/sitemap.xml');
1251
+ const sitemapContent = familyHomeDir ? safeReadFile(path.join(familyHomeDir, 'sitemap.xml')) : null;
1168
1252
  if (sitemapContent && htmlFiles.length > 0) {
1169
1253
  const publicPages = htmlFiles.filter(f => {
1170
1254
  const name = f.split('/').pop();
@@ -1226,17 +1310,21 @@ function runSecurityAudit() {
1226
1310
  let checksPassed = 0;
1227
1311
 
1228
1312
  // 1. Scan HTML files for hardcoded passwords/secrets
1229
- const htmlDir = '/root/family-home/';
1313
+ const htmlDir = projectPath('html_dir');
1314
+ if (!htmlDir) {
1315
+ return { status: 'skipped', vulnerability_count: 0, checks_passed: 0, vulnerabilities: [], skipped: 'html_dir not configured' };
1316
+ }
1230
1317
  const secretPatterns = [
1231
- /Liarzhek1\$/g,
1232
- /Levelsofself1\$/g,
1233
- /\d{10}:AA[A-Za-z0-9_-]{30,}/g,
1234
- /sk-ant-[a-zA-Z0-9_-]+/g,
1235
- /npm_[A-Za-z0-9]{20,}/g,
1236
- /ghp_[A-Za-z0-9]{20,}/g,
1237
- /BOT_TOKEN\s*[:=]\s*['"][^'"]+['"]/gi,
1238
- /PAPA_FULL\s*[:=]\s*['"][^'"]+['"]/gi,
1239
- /PAPA_READ\s*[:=]\s*['"][^'"]+['"]/gi
1318
+ /\d{10}:AA[A-Za-z0-9_-]{30,}/g, // Telegram bot tokens
1319
+ /sk-ant-[a-zA-Z0-9_-]+/g, // Anthropic API keys
1320
+ /sk_live_[a-zA-Z0-9]+/g, // Stripe live keys
1321
+ /sk_test_[a-zA-Z0-9]+/g, // Stripe test keys
1322
+ /npm_[A-Za-z0-9]{20,}/g, // npm tokens
1323
+ /ghp_[A-Za-z0-9]{20,}/g, // GitHub PATs
1324
+ /BOT_TOKEN\s*[:=]\s*['"][^'"]+['"]/gi, // Generic bot tokens
1325
+ /password\s*[:=]\s*['"][^'"]{8,}['"]/gi, // Hardcoded passwords
1326
+ /api[_-]?key\s*[:=]\s*['"][A-Za-z0-9_\-]{20,}['"]/gi, // API keys
1327
+ /secret\s*[:=]\s*['"][A-Za-z0-9_\-]{16,}['"]/gi, // Secrets
1240
1328
  ];
1241
1329
  try {
1242
1330
  const htmlFiles = fs.readdirSync(htmlDir).filter(f => f.endsWith('.html'));
@@ -1244,11 +1332,24 @@ function runSecurityAudit() {
1244
1332
  const content = safeReadFile(htmlDir + hf);
1245
1333
  if (!content) continue;
1246
1334
  let fileClean = true;
1335
+ const contentLines = content.split('\n');
1247
1336
  for (const pat of secretPatterns) {
1248
1337
  pat.lastIndex = 0;
1249
- const matches = content.match(pat);
1250
- if (matches && matches.length > 0) {
1251
- vulnerabilities.push({ type: 'hardcoded_secret', file: hf, pattern: pat.source, count: matches.length });
1338
+ let realMatches = 0;
1339
+ for (const line of contentLines) {
1340
+ // Skip lines that are defining detection patterns (not actual secrets)
1341
+ if (line.trim().match(/^\s*\/.*\/[gim]*,?\s*$/) ||
1342
+ line.includes('SENS_PAT') ||
1343
+ line.includes('secretPatterns') ||
1344
+ line.includes('leakPatterns') ||
1345
+ line.includes('dangerPatterns') ||
1346
+ line.includes('redact')) continue;
1347
+ pat.lastIndex = 0;
1348
+ const m = line.match(pat);
1349
+ if (m) realMatches += m.length;
1350
+ }
1351
+ if (realMatches > 0) {
1352
+ vulnerabilities.push({ type: 'hardcoded_secret', file: hf, pattern: pat.source, count: realMatches });
1252
1353
  fileClean = false;
1253
1354
  }
1254
1355
  }
@@ -1259,7 +1360,7 @@ function runSecurityAudit() {
1259
1360
  }
1260
1361
 
1261
1362
  // 2. Check auth endpoints use server-side validation
1262
- const serverContent = safeReadFile('/root/family-home/server.js');
1363
+ const serverContent = htmlDir ? safeReadFile(path.join(htmlDir, 'server.js')) : null;
1263
1364
  if (serverContent) {
1264
1365
  if (serverContent.includes('getSessionFromReq') || serverContent.includes('getAccessLevel')) {
1265
1366
  checksPassed++;
@@ -1291,7 +1392,8 @@ function runSecurityAudit() {
1291
1392
  }
1292
1393
 
1293
1394
  // 5. Check bridge rate limiting
1294
- if (serverContent && serverContent.includes('rate') || fs.existsSync('/root/rate-limit.js') || fs.existsSync('/root/bridge-ratelimit.js')) {
1395
+ const projRoot = projectPath('project_root') || process.cwd();
1396
+ if (serverContent && serverContent.includes('rate') || fs.existsSync(path.join(projRoot, 'rate-limit.js')) || fs.existsSync(path.join(projRoot, 'bridge-ratelimit.js'))) {
1295
1397
  checksPassed++;
1296
1398
  } else {
1297
1399
  vulnerabilities.push({ type: 'missing_rate_limit', file: 'bridge', detail: 'No rate limiting found for bridge' });
@@ -1315,7 +1417,8 @@ function runSecurityAudit() {
1315
1417
 
1316
1418
  // 7. Check api-credentials.json permissions
1317
1419
  try {
1318
- const credFile = '/root/family-data/api-credentials.json';
1420
+ const credFile = projectPath('data_dir') ? path.join(projectPath('data_dir'), 'api-credentials.json') : null;
1421
+ if (!credFile) { checksPassed++; }
1319
1422
  if (fs.existsSync(credFile)) {
1320
1423
  const stats = fs.statSync(credFile);
1321
1424
  const mode = (stats.mode & 0o777).toString(8);
@@ -1367,10 +1470,14 @@ function runSecurityAudit() {
1367
1470
 
1368
1471
  function runAutoPropagators() {
1369
1472
  const results = [];
1473
+ const workersDir = projectPath('project_root') ? path.join(projectPath('project_root') || process.cwd(), 'family-workers') : null;
1474
+ if (!workersDir || !fs.existsSync(workersDir)) {
1475
+ return { timestamp: new Date().toISOString(), propagators_run: 0, results: [], skipped: 'family-workers directory not found' };
1476
+ }
1370
1477
  const scripts = [
1371
- { name: 'role', path: '/root/family-workers/role-propagator.js' },
1372
- { name: 'version', path: '/root/family-workers/version-propagator.js' },
1373
- { name: 'content', path: '/root/family-workers/content-propagator.js' }
1478
+ { name: 'role', path: path.join(workersDir, 'role-propagator.js') },
1479
+ { name: 'version', path: path.join(workersDir, 'version-propagator.js') },
1480
+ { name: 'content', path: path.join(workersDir, 'content-propagator.js') }
1374
1481
  ];
1375
1482
  for (const script of scripts) {
1376
1483
  try {
@@ -1393,7 +1500,10 @@ function runAutoPropagators() {
1393
1500
  // ============================================================
1394
1501
 
1395
1502
  function runPageHealth(page) {
1396
- const FAMILY_HOME = '/root/family-home';
1503
+ const FAMILY_HOME = projectPath('html_dir');
1504
+ if (!FAMILY_HOME) {
1505
+ return { status: 'skipped', pages_checked: 0, issue_count: 0, issues: [], skipped: 'html_dir not configured' };
1506
+ }
1397
1507
  const issues = [];
1398
1508
 
1399
1509
  let htmlFiles;
@@ -1547,6 +1657,111 @@ function runPageHealth(page) {
1547
1657
  };
1548
1658
  }
1549
1659
 
1660
+
1661
+ // ============================================================
1662
+ // PRE-PUBLISH AUDIT ENGINE
1663
+ // ============================================================
1664
+
1665
+ function runPrePublishAudit(sourceFile) {
1666
+ const findings = [];
1667
+ const file = sourceFile || __filename;
1668
+ let content;
1669
+ try {
1670
+ content = fs.readFileSync(file, 'utf8');
1671
+ } catch (e) {
1672
+ return { status: 'error', error: 'Cannot read file: ' + e.message };
1673
+ }
1674
+ const lines = content.split('\n');
1675
+
1676
+ // 1. Check for hardcoded absolute paths (non-portable)
1677
+ lines.forEach((line, idx) => {
1678
+ if (line.trim().startsWith('//')) return;
1679
+ if (line.includes('description:') || line.includes('context:')) return;
1680
+ if (line.includes('description,') || line.includes("description'")) return;
1681
+
1682
+ if (line.match(/['"\`]\/root\//)) {
1683
+ findings.push({
1684
+ type: 'hardcoded_path',
1685
+ line: idx + 1,
1686
+ preview: line.trim().substring(0, 100),
1687
+ fix: 'Use projectPath() or configurable path'
1688
+ });
1689
+ }
1690
+ if (line.match(/['"\`]\/home\//)) {
1691
+ findings.push({
1692
+ type: 'hardcoded_path',
1693
+ line: idx + 1,
1694
+ preview: line.trim().substring(0, 100),
1695
+ fix: 'Use projectPath() or os.homedir()'
1696
+ });
1697
+ }
1698
+ });
1699
+
1700
+ // 2. Check for personal data that should not ship
1701
+ const personalPatterns = [
1702
+ { name: 'email_address', pat: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g },
1703
+ { name: 'phone_number', pat: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g },
1704
+ { name: 'ip_address', pat: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g },
1705
+ ];
1706
+ lines.forEach((line, idx) => {
1707
+ if (line.trim().startsWith('//') || line.includes('description')) return;
1708
+ if (line.includes('regex') || line.includes('pattern') || line.includes('pat:')) return;
1709
+ for (const pp of personalPatterns) {
1710
+ pp.pat.lastIndex = 0;
1711
+ if (pp.pat.test(line)) {
1712
+ findings.push({
1713
+ type: 'personal_data',
1714
+ subtype: pp.name,
1715
+ line: idx + 1,
1716
+ preview: line.trim().substring(0, 100),
1717
+ });
1718
+ }
1719
+ }
1720
+ });
1721
+
1722
+ // 3. Check for internal naming that should be generic
1723
+ const internalTerms = [
1724
+ 'family-data', 'family-home', 'family-logs', 'family-roles',
1725
+ 'family-guide', 'family-status', 'family-workers',
1726
+ 'PAPA_FULL', 'PAPA_READ', 'ARTHUR_CHAT_ID',
1727
+ ];
1728
+ lines.forEach((line, idx) => {
1729
+ if (line.trim().startsWith('//')) return;
1730
+ if (line.includes('description:') || line.includes('context:') || line.includes('tagline:')) return;
1731
+ if (line.includes('origin_story')) return;
1732
+ for (const term of internalTerms) {
1733
+ if (line.toLowerCase().includes(term.toLowerCase()) &&
1734
+ !line.includes('// ')) {
1735
+ findings.push({
1736
+ type: 'internal_naming',
1737
+ term: term,
1738
+ line: idx + 1,
1739
+ preview: line.trim().substring(0, 100),
1740
+ });
1741
+ }
1742
+ }
1743
+ });
1744
+
1745
+ // 4. GATE: Block publish if critical findings
1746
+ const critical = findings.filter(f =>
1747
+ f.type === 'personal_data' ||
1748
+ (f.type === 'hardcoded_path' && !f.preview.includes('description'))
1749
+ );
1750
+
1751
+ return {
1752
+ status: findings.length === 0 ? 'ready_to_publish' :
1753
+ critical.length > 0 ? 'BLOCKED_critical_findings' : 'warnings_only',
1754
+ total_findings: findings.length,
1755
+ critical_count: critical.length,
1756
+ findings: findings,
1757
+ recommendation: critical.length > 0 ?
1758
+ 'DO NOT PUBLISH. Fix critical findings first.' :
1759
+ findings.length > 0 ?
1760
+ 'Review warnings before publishing. None are blockers.' :
1761
+ 'Clean. Safe to publish.'
1762
+ };
1763
+ }
1764
+
1550
1765
  // ============================================================
1551
1766
  // Handle tool calls
1552
1767
  // ============================================================
@@ -1672,6 +1887,10 @@ function handleToolCall(name, args) {
1672
1887
  return runPageHealth(args.page || 'all');
1673
1888
  }
1674
1889
 
1890
+ case 'pre_publish_audit': {
1891
+ return runPrePublishAudit(args.source_file);
1892
+ }
1893
+
1675
1894
  default:
1676
1895
  return { error: 'Unknown tool' };
1677
1896
  }
@@ -1793,7 +2012,7 @@ const server = http.createServer((req, res) => {
1793
2012
  // Health check
1794
2013
  if (req.method === 'GET' && url.pathname === '/health') {
1795
2014
  res.writeHead(200, { 'Content-Type': 'application/json' });
1796
- res.end(JSON.stringify({ status: 'ok', service: 'nervous-system-mcp', version: '1.5.0', protocol: MCP_VERSION }));
2015
+ res.end(JSON.stringify({ status: 'ok', service: 'nervous-system-mcp', version: '1.6.0', protocol: MCP_VERSION }));
1797
2016
  return;
1798
2017
  }
1799
2018
 
@@ -1910,7 +2129,7 @@ const server = http.createServer((req, res) => {
1910
2129
  res.writeHead(200, { 'Content-Type': 'application/json' });
1911
2130
  res.end(JSON.stringify({
1912
2131
  name: 'The Nervous System MCP Server',
1913
- version: '1.5.1',
2132
+ version: '1.6.0',
1914
2133
  protocol: MCP_VERSION,
1915
2134
  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.',
1916
2135
  endpoints: {
@@ -1932,8 +2151,8 @@ const server = http.createServer((req, res) => {
1932
2151
  migrateExistingViolations();
1933
2152
 
1934
2153
  server.listen(PORT, '127.0.0.1', () => {
1935
- console.error(`[MCP Server] Nervous System v1.5.1 running on port ${PORT}`);
2154
+ console.error(`[MCP Server] Nervous System v1.6.0 running on port ${PORT}`);
1936
2155
  console.error(`[MCP Server] SSE: /sse | HTTP: /mcp | Health: /health | Kill: POST /kill | Audit: GET /audit/verify | Dispatches: GET /dispatches`);
1937
2156
  console.error(`[MCP Server] Protocol: ${MCP_VERSION}`);
1938
- console.error(`[MCP Server] Tools: ${TOOLS.length} (including kill switch, audit chain, dispatch, drift audit, page health)`);
2157
+ console.error(`[MCP Server] Tools: ${TOOLS.length} (including kill switch, audit chain, dispatch, drift audit, page health, pre-publish audit)`);
1939
2158
  });
@@ -0,0 +1,10 @@
1
+ {
2
+ "project_root": ".",
3
+ "data_dir": "./data",
4
+ "logs_dir": "./logs",
5
+ "html_dir": "./public",
6
+ "protected_files_list": "./PROTECTED_FILES.txt",
7
+ "config_file": "./system-config.json",
8
+ "pm2_managed": false,
9
+ "docs_to_audit": []
10
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-nervous-system",
3
- "version": "1.5.1",
4
- "description": "The Nervous System - LLM Behavioral Enforcement Framework. 7 mechanically enforced rules, 16 tools including kill switch, audit chain, dispatch, drift audit, security audit, page health, and session close. MCP server for Claude Desktop, Claude Code, and any MCP-compatible client.",
3
+ "version": "1.6.0",
4
+ "description": "The Nervous System - LLM Behavioral Enforcement Framework. 7 mechanically enforced rules, 17 tools including kill switch, audit chain, dispatch, drift audit, security audit, page health, pre-publish 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"
@@ -39,4 +39,4 @@
39
39
  "dependencies": {
40
40
  "@modelcontextprotocol/sdk": "^1.0.0"
41
41
  }
42
- }
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "mcp-nervous-system",
3
+ "version": "1.5.1",
4
+ "description": "The Nervous System - LLM Behavioral Enforcement Framework. 7 mechanically enforced rules, 16 tools including kill switch, audit chain, dispatch, drift audit, security audit, page health, and session close. MCP server for Claude Desktop, Claude Code, and any MCP-compatible client.",
5
+ "main": "server.js",
6
+ "bin": {
7
+ "mcp-nervous-system": "./stdio.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node server.js"
11
+ },
12
+ "keywords": [
13
+ "mcp",
14
+ "model-context-protocol",
15
+ "llm",
16
+ "ai-safety",
17
+ "ai-governance",
18
+ "behavioral-enforcement",
19
+ "guardrails",
20
+ "claude",
21
+ "anthropic",
22
+ "nervous-system",
23
+ "kill-switch",
24
+ "audit-trail"
25
+ ],
26
+ "author": "Arthur Palyan <palyanai@gmail.com>",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/levelsofself/mcp-nervous-system"
31
+ },
32
+ "homepage": "https://api.100levelup.com/family/gateway.html",
33
+ "bugs": {
34
+ "url": "https://github.com/levelsofself/mcp-nervous-system/issues"
35
+ },
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.0.0"
41
+ }
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "mcp-nervous-system",
3
+ "version": "1.5.0",
4
+ "description": "The Nervous System - LLM Behavioral Enforcement Framework. 7 mechanically enforced rules, 16 tools including kill switch, audit chain, dispatch, drift audit, security audit, page health, and session close. MCP server for Claude Desktop, Claude Code, and any MCP-compatible client.",
5
+ "main": "server.js",
6
+ "bin": {
7
+ "mcp-nervous-system": "./stdio.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node server.js"
11
+ },
12
+ "keywords": [
13
+ "mcp",
14
+ "model-context-protocol",
15
+ "llm",
16
+ "ai-safety",
17
+ "ai-governance",
18
+ "behavioral-enforcement",
19
+ "guardrails",
20
+ "claude",
21
+ "anthropic",
22
+ "nervous-system",
23
+ "kill-switch",
24
+ "audit-trail"
25
+ ],
26
+ "author": "Arthur Palyan <palyanai@gmail.com>",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/levelsofself/mcp-nervous-system"
31
+ },
32
+ "homepage": "https://api.100levelup.com/family/gateway.html",
33
+ "bugs": {
34
+ "url": "https://github.com/levelsofself/mcp-nervous-system/issues"
35
+ },
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.0.0"
41
+ }
42
+ }