@privacyscrubber/mcp-server 2.2.7 → 2.3.2

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.
@@ -0,0 +1,78 @@
1
+ {
2
+ "serverInfo": {
3
+ "name": "privacyscrubber/pii-masking-mcp",
4
+ "version": "1.6.9"
5
+ },
6
+ "tools": [
7
+ {
8
+ "name": "sanitize_text",
9
+ "description": "Locally scrubs PII, secrets, and credentials (like API keys, passwords, emails, phones, names) from code, logs, or text. Replaces them with safe placeholders (e.g., [EMAIL_1], [API_KEY_1]). Keep your data secure before passing it to any LLM.",
10
+ "inputSchema": {
11
+ "type": "object",
12
+ "properties": {
13
+ "text": {
14
+ "type": "string",
15
+ "description": "The raw text, code, or logs to sanitize."
16
+ },
17
+ "profile": {
18
+ "type": "string",
19
+ "description": "The detection profile to use. Available: 'General' (Free), or PRO profiles: 'Dev' (Engineering/Code), 'Medical', 'Pharma', 'Legal', 'Compliance', 'CCPA', 'Finance', 'Bizops', 'Sales', 'WealthMgmt', 'Insurance', 'Accounting', 'HR', 'Security', 'Marketing', 'Support', 'RealEstate', 'Agents', 'Academic', 'Creative', 'Tech', 'Personal'. Defaults to 'General'."
20
+ }
21
+ },
22
+ "required": ["text"]
23
+ }
24
+ },
25
+ {
26
+ "name": "reveal_text",
27
+ "description": "Replaces masked tokens (e.g., [EMAIL_1], [API_KEY_1]) in the LLM's response back with the original private data from the local volatile RAM-only session map.",
28
+ "inputSchema": {
29
+ "type": "object",
30
+ "properties": {
31
+ "text": {
32
+ "type": "string",
33
+ "description": "The AI generated response containing placeholders to restore."
34
+ }
35
+ },
36
+ "required": ["text"]
37
+ }
38
+ },
39
+ {
40
+ "name": "sanitize_file",
41
+ "description": "Reads a local file, sanitizes its contents using the selected profile, and outputs the safe version for AI analysis. Securely keeps original identifiers in memory.",
42
+ "inputSchema": {
43
+ "type": "object",
44
+ "properties": {
45
+ "file_path": {
46
+ "type": "string",
47
+ "description": "Absolute path to the file to sanitize."
48
+ },
49
+ "profile": {
50
+ "type": "string",
51
+ "description": "The detection profile to use. Available: 'General' (Free), or PRO profiles: 'Dev' (Engineering/Code), 'Medical', 'Pharma', 'Legal', 'Compliance', 'CCPA', 'Finance', 'Bizops', 'Sales', 'WealthMgmt', 'Insurance', 'Accounting', 'HR', 'Security', 'Marketing', 'Support', 'RealEstate', 'Agents', 'Academic', 'Creative', 'Tech', 'Personal'. Defaults to 'General'."
52
+ }
53
+ },
54
+ "required": ["file_path"]
55
+ }
56
+ },
57
+ {
58
+ "name": "create_default_config",
59
+ "description": "Creates a default 'privacyscrubber.json' configuration file in the active workspace root directory if one does not exist. Includes template structures for custom regex rules and exclusion bypass patterns.",
60
+ "inputSchema": {
61
+ "type": "object",
62
+ "properties": {},
63
+ "required": []
64
+ }
65
+ },
66
+ {
67
+ "name": "check_status",
68
+ "description": "Returns the current PrivacyScrubber MCP tier, session usage, available profiles, and PRO upgrade instructions. Call this to see your license status or get setup help.",
69
+ "inputSchema": {
70
+ "type": "object",
71
+ "properties": {},
72
+ "required": []
73
+ }
74
+ }
75
+ ],
76
+ "resources": [],
77
+ "prompts": []
78
+ }
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ # PrivacyScrubber MCP Server License Agreement
2
+
3
+ **Copyright (c) 2026 Ilya Sibiryakov (BrandMeWeb). All Rights Reserved.**
4
+
5
+ ### 1. Grant of License — Community / Development Tier
6
+ Subject to the terms and conditions of this Agreement, BrandMeWeb grants you a personal, worldwide, non-exclusive, non-transferable license to install and run the PrivacyScrubber MCP Server (`@privacyscrubber/mcp-server`) in local IDE environments (such as Cursor, Windsurf, Claude Desktop, and CLI tools) solely for personal evaluation, development, and standard local data sanitization under published Free Tier quotas.
7
+
8
+ ### 2. Commercial, Multi-User & Enterprise Tier
9
+ Use of this software within commercial organizations, production automated pipelines, CI/CD gates, high-volume batch processing, multi-seat corporate teams, or the invocation of the 30 Specialized Industry Profiles (HIPAA PHI, Financial PAN/IBAN, Legal Privilege, DevSecOps Cloud Secrets, Automotive, Telecom, Biotech) requires a valid commercial license key:
10
+ - **PrivacyScrubber PRO**: Individual professional use ($15/mo or $110 Lifetime).
11
+ - **PrivacyScrubber TEAMS**: Team and small enterprise seats ($99/mo flat).
12
+ - **PrivacyScrubber Enterprise / Air-Gapped**: Dedicated corporate VPC clusters, Nitro Enclaves, and custom profile calibration ($12,000/yr).
13
+
14
+ Licensing and key activation: https://privacyscrubber.com/pricing
15
+
16
+ ### 3. Restrictions on Reverse-Engineering & Redistribution
17
+ Except as expressly authorized herein, you may not:
18
+ - Decompile, disassemble, reverse-engineer, or attempt to reconstruct the underlying pattern databases, regular expression taxonomies, or proprietary tokenization finite-state machines.
19
+ - Extract, harvest, or scrape the 30 specialized industry entity taxonomies to train competing machine learning models or build competing redaction products.
20
+ - Distribute, sublicense, lease, or commercially exploit standalone copies of the engine without an active Enterprise OEM agreement.
21
+
22
+ ### 4. Disclaimer of Warranties & Limitation of Liability
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -10,6 +10,7 @@
10
10
  [![OSF DOI](https://img.shields.io/badge/OSF%20DOI-10.17605%2FOSF.IO%2F5BYJF-blue.svg)](https://osf.io/5byjf/)
11
11
  [![SSRN](https://img.shields.io/badge/SSRN-7335581-darkred.svg)](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=7335581)
12
12
  [![IETF Specification](https://img.shields.io/badge/IETF-ZTDS--Protocol-0284c7.svg)](https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/)
13
+ [![ZTDS Verified](https://ztds.ai/badge/privacyscrubber-sdk.svg)](https://ztds.ai/registry/#privacyscrubber-mcp)
13
14
  [![smithery badge](https://smithery.ai/badge/privacyscrubber/pii-masking-mcp)](https://smithery.ai/servers/privacyscrubber/pii-masking-mcp)
14
15
  [![Cursor Directory](https://img.shields.io/badge/Cursor%20Directory-Verified%20Plugin-000000.svg)](https://cursor.directory/plugins/privacyscrubber-mcp)
15
16
  [![Glama.ai](https://glama.ai/mcp/servers/moxno/privacyscrubber-mcp/badge)](https://glama.ai/mcp/servers/moxno/privacyscrubber-mcp)
@@ -17,13 +18,21 @@
17
18
  [![There's An AI For That](https://img.shields.io/badge/There's_An_AI_For_That-Live-06b6d4.svg)](https://theresanaiforthat.com/ai/privacy-scrubber/)
18
19
  [![Security: 100% Local](https://img.shields.io/badge/Security-100%25%20Local-emerald)](https://privacyscrubber.com)
19
20
  [![Parity: 100% Core Match](https://img.shields.io/badge/Parity-100%25%20Core%20Match-blueviolet)](https://privacyscrubber.com)
20
- [![Patent Pending](https://img.shields.io/badge/Patent-Pending%20(ILPO%20331905)-06b6d4.svg)](https://privacyscrubber.com/patents/)
21
+ [![Patent Pending](https://img.shields.io/badge/Patent-Pending%20(US%20%26%20Intl)-06b6d4.svg)](https://privacyscrubber.com/patents/)
22
+ [![Developer SDK](https://img.shields.io/badge/Developer%20SDK-$299%2Fmo%20Flat-06b6d4.svg)](https://privacyscrubber.com/sdk/?utm_source=mcp_readme)
23
+ [![TEAMS Plan](https://img.shields.io/badge/TEAMS-$99%2Fmo%20Flat-3b82f6.svg)](https://privacyscrubber.com/pricing?tier=teams&utm_source=mcp_readme)
21
24
  [![GitHub Stars](https://img.shields.io/github/stars/moxno/privacyscrubber-mcp?style=social)](https://github.com/moxno/privacyscrubber-mcp)
22
25
 
23
26
  **CISO-Approved Zero-Trust PII & Secrets Redaction MCP Server for Cursor, Windsurf, and Claude Desktop.**
24
27
  Locally scrubs PII, secrets, credentials, and custom regex rules from files and text contexts before they reach remote LLM providers to prevent API leaks and ensure HIPAA/SOC 2 compliance at the developer endpoint.
25
28
 
26
- > ⭐ **Support Zero-Trust Open Source:** If PrivacyScrubber protects your API keys and code from leaks, please **[Star this repository](https://github.com/moxno/privacyscrubber-mcp)** or run `gh repo star moxno/privacyscrubber-mcp` in your terminal!
29
+ > **Support Zero-Trust Open Source:** If PrivacyScrubber protects your API keys, source code, and developer credentials from model leaks, please **[Star this repository on GitHub](https://github.com/moxno/privacyscrubber-mcp)** or execute `gh repo star moxno/privacyscrubber-mcp`.
30
+
31
+ > **Direct Commercial Licensing & Production Infrastructure:**
32
+ > Looking to automate zero-trust PII redaction inside backend microservices, ETL pipelines, or LangChain/LlamaIndex agents?
33
+ > - **Developer SDK ($299/mo flat or $2,990/yr):** Unlimited internal backend nodes, zero network egress, <1ms in-memory latency. **[Subscribe to Developer SDK](https://privacyscrubber.com/pricing?tier=sdk&utm_source=mcp_readme)** | **[Technical Specs](https://privacyscrubber.com/sdk/?utm_source=mcp_readme)**
34
+ > - **TEAMS ($99/mo flat):** Unlimited team seats across your entire domain with cryptographic P2P session handoff. **[Deploy TEAMS Plan](https://privacyscrubber.com/pricing?tier=teams&utm_source=mcp_readme)**
35
+ > - **PRO ($15/mo or $110 Lifetime):** Single-seat unlimited throughput across all 30 vertical profiles. **[Get PRO License](https://privacyscrubber.com/pricing?tier=pro&utm_source=mcp_readme)**
27
36
 
28
37
  ---
29
38
 
@@ -55,7 +64,18 @@ Run the server directly without local installation:
55
64
  npx -y @privacyscrubber/mcp-server
56
65
  ```
57
66
 
58
- ### 3. Programmatic Node.js / TypeScript SDK (Lightweight Presidio Alternative)
67
+ ### 3. Instant Run with UVX (Python / PyPI)
68
+ Run the server directly inside Python agent workflows (CrewAI, LangChain, AutoGen, LlamaIndex):
69
+ ```bash
70
+ uvx privacyscrubber-mcp
71
+ ```
72
+ Or via pip:
73
+ ```bash
74
+ pip install privacyscrubber-mcp
75
+ privacyscrubber-mcp
76
+ ```
77
+
78
+ ### 4. Programmatic Node.js / TypeScript SDK (Lightweight Presidio Alternative)
59
79
  Need direct, in-memory zero-trust PII sanitization in your backend microservice, Next.js app, or RAG vector pipeline rather than an MCP server? Use our official zero-dependency SDK:
60
80
 
61
81
  ```bash
@@ -175,10 +195,10 @@ const engine = new PrivacyScrubberEngine({ defaultProfile: 'Dev' });
175
195
  const guardedTools = createGuardedTools(engine);
176
196
  ```
177
197
 
178
- > 🏢 **Enterprise & Production Licensing:**
179
- > - **Self-Serve Developer SDK ($299/mo flat or $2,990/yr):** Unlimited internal backend nodes, microservices, and RAG pipelines. [Developer SDK Specs & Pricing](https://privacyscrubber.com/sdk/?utm_source=mcp_readme)
180
- > - **PrivacyScrubber TEAMS ($99/mo flat):** Unlimited team seats, centralized policy enforcement, encrypted session handoff. [Deploy TEAMS](https://privacyscrubber.com/teams?utm_source=mcp_readme)
181
- > - **Enterprise Air-Gapped License:** On-premise source code distribution, zero-network custom models. [Contact Enterprise](https://privacyscrubber.com/enterprise?utm_source=mcp_readme)
198
+ > **Enterprise & Production Licensing:**
199
+ > - **Self-Serve Developer SDK ($299/mo flat or $2,990/yr):** Unlimited internal backend nodes, microservices, and RAG pipelines. **[Subscribe to Developer SDK](https://privacyscrubber.com/pricing?tier=sdk&utm_source=mcp_readme)** | **[Developer SDK Specs](https://privacyscrubber.com/sdk/?utm_source=mcp_readme)**
200
+ > - **PrivacyScrubber TEAMS ($99/mo flat):** Unlimited team seats, centralized policy enforcement, encrypted session handoff. **[Deploy TEAMS Plan](https://privacyscrubber.com/pricing?tier=teams&utm_source=mcp_readme)**
201
+ > - **Enterprise Air-Gapped License:** On-premise source code distribution, zero-network custom models. **[Contact Enterprise](https://privacyscrubber.com/enterprise?utm_source=mcp_readme)**
182
202
 
183
203
  #### 🛡️ Architecture & Security Deep-Dive (Zero-Trust vs Cloud DLP)
184
204
 
@@ -194,7 +214,7 @@ When AI IDEs (Cursor, Claude Desktop, Windsurf) connect to model providers, deve
194
214
  - **IETF Specification**: [draft-sibiryakov-ztds-protocol-01](https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/) — Revision 01 · 2026-09-23 · 13 pages
195
215
  - **CERN / Zenodo Foundation**: [DOI 10.5281/zenodo.22058770](https://doi.org/10.5281/zenodo.22058770)
196
216
  - **Center for Open Science (OSF)**: [DOI 10.17605/OSF.IO/5BYJF](https://doi.org/10.17605/OSF.IO/5BYJF)
197
- - **Patent Pending**: Israel Patent Office Application `IL 331905` (WIPO DAS Code: `B17B`)
217
+ - **Patent Pending**: U.S. & International Patents Pending (Paris Convention / PCT Priority)
198
218
 
199
219
  ---
200
220
 
@@ -375,7 +395,7 @@ Returns a visual dashboard showing your current tier, session request count, act
375
395
  * **Response Example (Free Tier):**
376
396
  ```
377
397
  ╔══════════════════════════════════════════════════╗
378
- ║ PrivacyScrubber MCP Server v2.2.4 ║
398
+ ║ PrivacyScrubber MCP Server v2.2.8 ║
379
399
  ╠══════════════════════════════════════════════════╣
380
400
  ║ 🔓 Tier: FREE ║
381
401
  ║ 📊 Session requests: 5 ║
@@ -426,19 +446,23 @@ Looking for real-time protection directly inside your web browser?
426
446
 
427
447
  By default, the server runs under the **Free Tier** (restricted to 15,000 characters per request and the basic `General` PII profile). To unlock 30 specialized engineering, medical, legal, and financial PII profiles, as well as team-wide custom rules, you can purchase a commercial license.
428
448
 
429
- ### Feature Comparison
449
+ ### Commercial Plan & Feature Comparison
430
450
 
431
- | Feature | Free Tier | PRO Tier | TEAMS Tier |
432
- | :--- | :--- | :--- | :--- |
433
- | **Volatile Tokenization** | ✅ Yes | ✅ Yes | ✅ Yes |
434
- | **Standard PII Masking** | ✅ Yes | ✅ Yes | ✅ Yes |
435
- | **Max Character Length** | 15,000 chars | ♾️ Unlimited | ♾️ Unlimited |
436
- | **Industry Profiles** | General Only | 30 Profiles | 30 Profiles |
437
- | **Custom Regex Rules** | ❌ Locked | ♾️ Unlimited | ♾️ Unlimited |
438
- | **Team Rules Sync (GPO)** | ❌ No | ❌ No | ✅ Yes (Shared Link) |
439
- | **Licensing Cost** | $0 | **$110 Lifetime** | **$99/mo Flat Rate** |
451
+ | Feature | Community Free | PRO Tier | TEAMS Tier | Developer SDK |
452
+ | :--- | :--- | :--- | :--- | :--- |
453
+ | **Execution Architecture** | Local Stdio RAM | Local Stdio RAM | Local Stdio RAM | In-Process Node/WASM |
454
+ | **Volatile Tokenization** | Yes (RAM-only) | Yes (RAM-only) | Yes (RAM-only) | Yes (RAM-only) |
455
+ | **Standard PII Masking** | Yes | Yes | Yes | Yes |
456
+ | **Max Character Throughput** | 15,000 chars / call | Unlimited | Unlimited | Unlimited |
457
+ | **30 Industry Profiles** | General Only (5k trial) | All 30 Profiles | All 30 Profiles | All 30 Profiles |
458
+ | **Custom Regex Rules** | Locked | Unlimited | Unlimited | Unlimited |
459
+ | **Team Rules Sync (GPO)** | No | No | Yes (Shared Link) | Yes (Configurable) |
460
+ | **Microservice / RAG Export** | No | No | No | Yes (`@privacyscrubber/sdk`) |
461
+ | **Subprocessor Liability** | 0 (Client-side) | 0 (Client-side) | 0 (Client-side) | 0 (In-Process Node/WASM) |
462
+ | **Licensing Cost** | $0 | **$15/mo or $110 Lifetime** | **$99/mo Flat Rate** | **$299/mo or $2,990/yr** |
463
+ | **Direct Activation** | Default included | [Acquire PRO](https://privacyscrubber.com/pricing?tier=pro&utm_source=mcp_readme) | [Deploy TEAMS](https://privacyscrubber.com/pricing?tier=teams&utm_source=mcp_readme) | [Subscribe SDK](https://privacyscrubber.com/pricing?tier=sdk&utm_source=mcp_readme) |
440
464
 
441
- 👉 **[Acquire a PRO / TEAMS License Key at privacyscrubber.com/pricing](https://privacyscrubber.com/pricing?utm_source=npm&utm_medium=readme&utm_campaign=mcp_server)**
465
+ 👉 **[Acquire a Commercial License Key at privacyscrubber.com/pricing](https://privacyscrubber.com/pricing?utm_source=npm&utm_medium=readme&utm_campaign=mcp_server)**
442
466
 
443
467
  ---
444
468
 
@@ -551,15 +575,15 @@ MIT © [Ilya Sibiryakov](https://privacyscrubber.com) (BrandMeWeb)
551
575
 
552
576
  ---
553
577
 
554
- ## ⚖️ Intellectual Property & Virtual Patent Marking
578
+ ## ⚖️ Intellectual Property & Patent Disclosures
555
579
 
556
580
  The Zero-Trust Data Sanitization (ZTDS) architecture, in-memory deterministic tokenization, cryptographic session handoff, and stdio execution methods implemented in this package are proprietary technology of Ilya Sibiryakov (BrandMeWeb) and are protected under **Patent Pending** status:
557
581
 
558
- - **Patent Office:** State of Israel Ministry of Justice, Patent Office (ILPO)
559
- - **Application Number:** `331905` (Tracking ID: `94221`)
560
- - **Filing / Priority Date:** September 14, 2026 (Paris Convention Art. 4 & 35 U.S.C. § 119 Priority)
582
+ - **Legal Status:** U.S. & International Patents Pending
583
+ - **Priority Framework:** Paris Convention Art. 4 & 35 U.S.C. § 119 Priority (locked through September 14, 2027)
584
+ - **Filing / Priority Date:** September 14, 2026
561
585
  - **Official Title:** *SYSTEM AND METHOD FOR CLIENT-SIDE ZERO-TRUST DATA SANITIZATION AND CRYPTOGRAPHIC SESSION HANDOFF IN ARTIFICIAL INTELLIGENCE WORKFLOWS*
562
- - **Virtual Patent Marking:** [privacyscrubber.com/patents/](https://privacyscrubber.com/patents/) in accordance with 35 U.S.C. § 287(a).
586
+ - **Patent Disclosures Hub:** [privacyscrubber.com/patents/](https://privacyscrubber.com/patents/) (formal verification via counsel NDA).
563
587
 
564
588
  ### 🌐 Internet Engineering Task Force (IETF) Specification
565
589
  - **Specification Title:** *The Zero-Trust Data Sanitization (ZTDS) Protocol for Frontier Artificial Intelligence Ingestion*
package/index.js CHANGED
@@ -67,7 +67,7 @@ PrivacyScrubberCore.init();
67
67
  const agentSessions = new Map();
68
68
  const MAX_CONCURRENT_SESSIONS = 200;
69
69
 
70
- function getSessionMap(sessionId = 'default') {
70
+ function getSession(sessionId = 'default') {
71
71
  const sid = (sessionId || 'default').toString().trim() || 'default';
72
72
  let session = agentSessions.get(sid);
73
73
  const now = Date.now();
@@ -76,15 +76,24 @@ function getSessionMap(sessionId = 'default') {
76
76
  const oldestKey = agentSessions.keys().next().value;
77
77
  agentSessions.delete(oldestKey);
78
78
  }
79
- session = { map: {}, createdAt: now, lastActive: now };
79
+ session = { map: {}, ignoreList: new Set(), createdAt: now, lastActive: now };
80
80
  agentSessions.set(sid, session);
81
81
  }
82
82
  session.lastActive = now;
83
- return session.map;
83
+ return session;
84
+ }
85
+
86
+ function getSessionMap(sessionId = 'default') {
87
+ return getSession(sessionId).map;
88
+ }
89
+
90
+ function getSessionIgnoreList(sessionId = 'default') {
91
+ return getSession(sessionId).ignoreList;
84
92
  }
85
93
 
86
94
  // Global default sessionMap maintains 100% backward compatibility
87
95
  const sessionMap = getSessionMap('default');
96
+ const sessionIgnoreList = getSessionIgnoreList('default');
88
97
 
89
98
  function reverseTokens(text, map) {
90
99
  if (!text || typeof text !== 'string' || !map || Object.keys(map).length === 0) return text;
@@ -101,16 +110,15 @@ function reverseTokens(text, map) {
101
110
  return result;
102
111
  }
103
112
 
104
- // Volatile in-memory false positive ignore list (values excluded from future scrubs)
105
- const sessionIgnoreList = new Set();
106
-
107
113
  // ANSI terminal color helpers
108
114
  const colors = {
109
115
  yellowBold: '\x1b[1;33m',
110
116
  yellow: '\x1b[33m',
117
+ cyanBold: '\x1b[1;36m',
111
118
  cyan: '\x1b[36m',
112
119
  greenBold: '\x1b[1;32m',
113
120
  redBold: '\x1b[1;31m',
121
+ dim: '\x1b[2m',
114
122
  reset: '\x1b[0m'
115
123
  };
116
124
 
@@ -160,31 +168,35 @@ function getUsageFilePath() {
160
168
  return path.resolve(homeDir, '.privacyscrubber-usage.json');
161
169
  }
162
170
 
171
+ let cachedDailyUsage = null;
172
+
163
173
  function getDailyUsage() {
164
- const file = getUsageFilePath();
165
174
  const today = new Date().toISOString().split('T')[0];
166
- if (!fs.existsSync(file)) return 0;
175
+ if (cachedDailyUsage && cachedDailyUsage.date === today) {
176
+ return cachedDailyUsage.count;
177
+ }
178
+ const file = getUsageFilePath();
179
+ if (!fs.existsSync(file)) {
180
+ cachedDailyUsage = { date: today, count: 0 };
181
+ return 0;
182
+ }
167
183
  try {
168
184
  const data = JSON.parse(fs.readFileSync(file, 'utf8'));
169
185
  if (data.date === today) {
170
- return data.count || 0;
186
+ cachedDailyUsage = { date: today, count: data.count || 0 };
187
+ return cachedDailyUsage.count;
171
188
  }
172
189
  } catch (e) {}
190
+ cachedDailyUsage = { date: today, count: 0 };
173
191
  return 0;
174
192
  }
175
193
 
176
194
  function incrementDailyUsage() {
177
- const file = getUsageFilePath();
178
195
  const today = new Date().toISOString().split('T')[0];
179
- let count = 0;
180
- if (fs.existsSync(file)) {
181
- try {
182
- const data = JSON.parse(fs.readFileSync(file, 'utf8'));
183
- if (data.date === today) count = data.count || 0;
184
- } catch (e) {}
185
- }
186
- count++;
196
+ const count = getDailyUsage() + 1;
197
+ cachedDailyUsage = { date: today, count };
187
198
  try {
199
+ const file = getUsageFilePath();
188
200
  fs.writeFileSync(file, JSON.stringify({ date: today, count }), 'utf8');
189
201
  } catch (e) {}
190
202
  return count;
@@ -250,33 +262,79 @@ function buildCisoAuditTelemetry(currentTokenMap = {}) {
250
262
  frameworksSet.add('GDPR (Art. 4)');
251
263
  frameworksSet.add('CCPA/CPRA');
252
264
  }
253
- if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT')) {
265
+ if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT') || types.includes('NATIONAL_ID')) {
254
266
  frameworksSet.add('SOC 2 Type II');
255
267
  frameworksSet.add('ISO 27001 (A.8.11)');
256
268
  }
257
- if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD'].includes(t))) {
269
+ if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD', 'STRIPE_KEY', 'TAX_ID', 'EIN', 'W2', '1099', 'SALARY', 'WAGE'].includes(t))) {
258
270
  frameworksSet.add('PCI DSS v4.0');
271
+ frameworksSet.add('IRC § 7216 & GLBA');
259
272
  }
260
- if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT'].includes(t))) {
273
+ if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT', 'DIAGNOSIS', 'NPI', 'DEA'].includes(t))) {
261
274
  frameworksSet.add('HIPAA §164.514');
262
275
  }
263
- if (types.some(t => ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'KEY'].includes(t))) {
276
+ if (types.some(t => ['CASE_NUMBER', 'DOCKET', 'PRIVILEGED', 'LEGAL', 'ATTORNEY', 'COURT', 'CONTRACT', 'NDA'].includes(t))) {
277
+ frameworksSet.add('FRE 502 (Privilege)');
278
+ }
279
+ if (types.some(t => ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'KEY', 'AWS_KEY', 'JWT_TOKEN', 'API_TOKEN', 'CREDENTIAL'].includes(t))) {
264
280
  frameworksSet.add('NIST SP 800-53');
265
281
  }
282
+ if (totalCount > 0) {
283
+ frameworksSet.add('EU AI Act (Art. 50)');
284
+ }
285
+
286
+ const ipHoldings = {
287
+ standard: 'IETF draft-sibiryakov-ztds-protocol-00',
288
+ standardUrl: 'https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/',
289
+ patent: 'IL 331905 (Tracking: 94221)',
290
+ wipoDas: 'B17B',
291
+ trademark: 'ZTDS™ Reg. #182655957 (ILPO Cl 9 & 42)'
292
+ };
293
+
294
+ const statutoryCitations = [
295
+ {
296
+ statute: 'EU AI Act (Art. 50)',
297
+ scope: 'AI Transparency & Data Minimization',
298
+ rule: '0-byte external network transmission; prevents unauthorized ingestion into frontier models.'
299
+ },
300
+ {
301
+ statute: 'GDPR (Art. 4, 25, 28, 32)',
302
+ scope: 'Pseudonymization & Processor Liability',
303
+ rule: 'Volatile client-side tokenization eliminates GDPR Article 28 data processor liability.'
304
+ },
305
+ {
306
+ statute: 'HIPAA 45 CFR § 164.514(b)',
307
+ scope: 'Safe Harbor De-Identification',
308
+ rule: 'Deterministic removal of all 18 PHI identifiers prior to AI analysis.'
309
+ },
310
+ {
311
+ statute: 'IRC § 7216 & IRS Pub. 1075',
312
+ scope: 'Tax Return Information Confidentiality',
313
+ rule: 'Strict criminal penalty safeguard against unauthorized disclosure of tax records to cloud LLMs.'
314
+ },
315
+ {
316
+ statute: 'FRE 502',
317
+ scope: 'Attorney-Client Privilege Preservation',
318
+ rule: 'Automated in-memory redaction prevents inadvertent waiver of legal privilege in AI workflows.'
319
+ }
320
+ ];
266
321
 
267
322
  return {
268
323
  totalCount,
269
324
  entities,
270
325
  types,
271
326
  riskLevel,
272
- frameworksList: Array.from(frameworksSet)
327
+ frameworksList: Array.from(frameworksSet),
328
+ statutoryCitations,
329
+ ipHoldings,
330
+ multimodalEgress: '0.00 Bytes (Solid Blackout & Burn-in Verified)'
273
331
  };
274
332
  }
275
333
 
276
334
  function formatAuditReceipt(telemetry, compact = false, wasTruncated = false, charLimit = 15000) {
277
335
  const isCompact = compact === true || process.env.PRIVACYSCRUBBER_COMPACT_RECEIPT === '1' || process.env.PRIVACYSCRUBBER_COMPACT_RECEIPT === 'true';
278
336
  const truncationLine = wasTruncated
279
- ? `> * ⚠️ **Free Tier Limit:** Input truncated to ${charLimit.toLocaleString()} chars. Upgrade to PRO ($15/mo or $110 Lifetime) or Developer SDK ($299/mo): [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)\n`
337
+ ? `> * **Notice:** Input truncated to ${charLimit.toLocaleString()} chars (Free Tier Limit). Upgrade to PRO ($15/mo or $110 Lifetime), TEAMS ($99/mo) or Developer SDK ($299/mo): [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)\n`
280
338
  : '';
281
339
 
282
340
  if (isCompact) {
@@ -657,6 +715,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
657
715
  token: {
658
716
  type: "string",
659
717
  description: "The token to mark as false positive (e.g., '[NAME_1]', '[EMAIL_2]')."
718
+ },
719
+ session_id: {
720
+ type: "string",
721
+ description: "Optional session ID for multi-agent isolation. Defaults to 'default'."
660
722
  }
661
723
  },
662
724
  required: ["token"]
@@ -1151,10 +1213,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1151
1213
  if (name === "sanitize_text" || name === "scrub_text") {
1152
1214
  const { text, profile = "General", ignore_list, compact = false, session_id = "default" } = args || {};
1153
1215
  const currentSession = getSessionMap(session_id);
1216
+ const currentIgnoreList = getSessionIgnoreList(session_id);
1154
1217
 
1155
1218
  // Merge per-call ignore_list into persistent sessionIgnoreList
1156
1219
  if (Array.isArray(ignore_list)) {
1157
- ignore_list.forEach(v => { if (typeof v === 'string' && v.trim()) sessionIgnoreList.add(v.trim()); });
1220
+ ignore_list.forEach(v => { if (typeof v === 'string' && v.trim()) currentIgnoreList.add(v.trim()); });
1158
1221
  }
1159
1222
  if (text === undefined || text === null) {
1160
1223
  return {
@@ -1195,12 +1258,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1195
1258
 
1196
1259
  const charLimit = (isAdvanced && !isAutoElevated && !license.isPro) ? 5000 : 15000;
1197
1260
  if (isAdvanced && !license.isPro) {
1198
- mcpLog(`${colors.yellowBold}⚠️ [PrivacyScrubber] Profile '${targetProfile}' active on Free Tier (5,000 char limit).${colors.reset}\n`);
1261
+ mcpLog(`${colors.yellowBold}[PrivacyScrubber] Profile '${targetProfile}' active on Free Tier (5,000 char trial quota).${colors.reset}\n${colors.cyan}[Upgrade] Commercial TEAMS ($99/mo flat) & Developer SDK ($299/mo): https://privacyscrubber.com/pricing${colors.reset}\n`);
1199
1262
  }
1200
1263
 
1201
1264
  const { processedText, wasTruncated } = truncateIfFree(text, license.isPro, charLimit);
1202
1265
 
1203
- const { scrubbedText, newTokens } = performSanitization(processedText, finalProfile, sessionIgnoreList, currentSession);
1266
+ const { scrubbedText, newTokens } = performSanitization(processedText, finalProfile, currentIgnoreList, currentSession);
1204
1267
 
1205
1268
  const telemetry = buildCisoAuditTelemetry(newTokens);
1206
1269
  const receiptMd = formatAuditReceipt(telemetry, compact, wasTruncated, charLimit);
@@ -1254,7 +1317,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1254
1317
  }
1255
1318
 
1256
1319
  if (name === "mark_false_positive") {
1257
- const { token } = args || {};
1320
+ const { token, session_id = "default" } = args || {};
1258
1321
  if (!token || typeof token !== 'string') {
1259
1322
  return {
1260
1323
  isError: true,
@@ -1262,25 +1325,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1262
1325
  };
1263
1326
  }
1264
1327
 
1265
- const original = sessionMap[token];
1328
+ const currentSession = getSessionMap(session_id);
1329
+ const currentIgnoreList = getSessionIgnoreList(session_id);
1330
+
1331
+ const original = currentSession[token];
1266
1332
  if (!original) {
1267
1333
  return {
1268
1334
  isError: true,
1269
- content: [{ type: "text", text: `Error: Token '${token}' not found in current session map. Available tokens: ${Object.keys(sessionMap).join(', ') || '(empty session)'}` }]
1335
+ content: [{ type: "text", text: `Error: Token '${token}' not found in current session map. Available tokens: ${Object.keys(currentSession).join(', ') || '(empty session)'}` }]
1270
1336
  };
1271
1337
  }
1272
1338
 
1273
1339
  // Add the original plaintext to the ignore list
1274
- sessionIgnoreList.add(original);
1340
+ currentIgnoreList.add(original);
1275
1341
  // Remove the token from the session map
1276
- delete sessionMap[token];
1342
+ delete currentSession[token];
1277
1343
 
1278
1344
  mcpLog(`${colors.yellow}🔖 [PrivacyScrubber] False Positive: '${token}' → '${original}' will be excluded from future scrubs.${colors.reset}\n`);
1279
1345
 
1280
1346
  return {
1281
1347
  content: [{
1282
1348
  type: "text",
1283
- text: `✅ Marked '${token}' as false positive.\n\n**Restored value:** ${original}\n**Session ignore list size:** ${sessionIgnoreList.size}\n\nThis value will be excluded from all future \`sanitize_text\` calls in this session.`
1349
+ text: `✅ Marked '${token}' as false positive.\n\n**Restored value:** ${original}\n**Session ignore list size:** ${currentIgnoreList.size}\n\nThis value will be excluded from all future \`sanitize_text\` calls in this session.`
1284
1350
  }]
1285
1351
  };
1286
1352
  }
@@ -1361,7 +1427,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1361
1427
 
1362
1428
  const charLimit = (isAdvanced && !license.isPro) ? 5000 : 15000;
1363
1429
  if (isAdvanced && !license.isPro) {
1364
- mcpLog(`${colors.yellowBold}⚠️ [PrivacyScrubber] Profile '${targetProfile}' active on Free Tier (5,000 char limit).${colors.reset}\n`);
1430
+ mcpLog(`${colors.yellowBold}[PrivacyScrubber] Profile '${targetProfile}' active on Free Tier (5,000 char trial quota).${colors.reset}\n${colors.cyan}[Upgrade] Commercial TEAMS ($99/mo flat) & Developer SDK ($299/mo): https://privacyscrubber.com/pricing${colors.reset}\n`);
1365
1431
  }
1366
1432
 
1367
1433
  const { processedText: processedContent, wasTruncated } = truncateIfFree(content, license.isPro, charLimit);
@@ -1514,7 +1580,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1514
1580
 
1515
1581
  const charLimit = (isAdvanced && !isAutoElevated && !license.isPro) ? 5000 : 15000;
1516
1582
  if (isAdvanced && !license.isPro) {
1517
- mcpLog(`${colors.yellowBold}⚠️ [PrivacyScrubber] Profile '${targetProfile}' active on Free Tier (5,000 char limit).${colors.reset}\n`);
1583
+ mcpLog(`${colors.yellowBold}[PrivacyScrubber] Profile '${targetProfile}' active on Free Tier (5,000 char trial quota).${colors.reset}\n${colors.cyan}[Upgrade] Commercial TEAMS ($99/mo flat) & Developer SDK ($299/mo): https://privacyscrubber.com/pricing${colors.reset}\n`);
1518
1584
  }
1519
1585
 
1520
1586
  const { processedText: processedContent2, wasTruncated } = truncateIfFree(content, license.isPro, charLimit);
@@ -1958,6 +2024,7 @@ ${telemetry.frameworksList.map(f => `- **${f}**`).join('\n')}
1958
2024
  }
1959
2025
 
1960
2026
  const targetSession = getSessionMap(session_id);
2027
+ const targetIgnoreList = getSessionIgnoreList(session_id);
1961
2028
  // Transparent in-memory unmasking of any token placeholders before local execution
1962
2029
  const rawExecutableCommand = reverseTokens(command, targetSession);
1963
2030
 
@@ -1987,9 +2054,9 @@ ${telemetry.frameworksList.map(f => `- **${f}**`).join('\n')}
1987
2054
  const { processedText: cleanStdout, wasTruncated: truncOut } = truncateIfFree(stdout, license.isPro, charLimit);
1988
2055
  const { processedText: cleanStderr, wasTruncated: truncErr } = truncateIfFree(stderr, license.isPro, charLimit);
1989
2056
 
1990
- const resCommand = performSanitization(command, targetProfile, sessionIgnoreList, targetSession);
1991
- const resStdout = performSanitization(cleanStdout, targetProfile, sessionIgnoreList, targetSession);
1992
- const resStderr = performSanitization(cleanStderr, targetProfile, sessionIgnoreList, targetSession);
2057
+ const resCommand = performSanitization(command, targetProfile, targetIgnoreList, targetSession);
2058
+ const resStdout = performSanitization(cleanStdout, targetProfile, targetIgnoreList, targetSession);
2059
+ const resStderr = performSanitization(cleanStderr, targetProfile, targetIgnoreList, targetSession);
1993
2060
 
1994
2061
  const allNewTokens = { ...resCommand.newTokens, ...resStdout.newTokens, ...resStderr.newTokens };
1995
2062
  const tokenCount = Object.keys(allNewTokens).length;
@@ -2051,7 +2118,8 @@ ${telemetry.frameworksList.map(f => `- **${f}**`).join('\n')}
2051
2118
 
2052
2119
  const { processedText, wasTruncated: wasCharTruncated } = truncateIfFree(capped, license.isPro, charLimit);
2053
2120
  const currentSession = getSessionMap(session_id);
2054
- const { scrubbedText, newTokens } = performSanitization(processedText, targetProfile, sessionIgnoreList, currentSession);
2121
+ const currentIgnoreList = getSessionIgnoreList(session_id);
2122
+ const { scrubbedText, newTokens } = performSanitization(processedText, targetProfile, currentIgnoreList, currentSession);
2055
2123
 
2056
2124
  const telemetry = buildCisoAuditTelemetry(newTokens);
2057
2125
  const receiptMd = formatAuditReceipt(telemetry, false, (wasLineTruncated || wasCharTruncated), charLimit);
@@ -2735,7 +2803,7 @@ function performSanitization(text, profile, ignoreList = null, customSessionMap
2735
2803
 
2736
2804
  function truncateIfFree(text, isPro, charLimit = 15000) {
2737
2805
  if (!isPro && text.length > charLimit) {
2738
- mcpLog(`${colors.yellowBold}⚠️ [PrivacyScrubber] Input truncated to ${charLimit.toLocaleString()} characters (Free Tier Limit).${colors.reset}\n${colors.cyan}👉 Set PRIVACYSCRUBBER_KEY to your PRO license key for unlimited size: https://privacyscrubber.com/pricing${colors.reset}\n`);
2806
+ mcpLog(`${colors.yellowBold}[PrivacyScrubber] Input truncated to ${charLimit.toLocaleString()} characters (Free Tier Limit).${colors.reset}\n${colors.cyan}[Upgrade] Set PRIVACYSCRUBBER_KEY to your PRO/TEAMS/SDK license key for unlimited throughput: https://privacyscrubber.com/pricing${colors.reset}\n`);
2739
2807
  const upsellNotice = `\n\n[PrivacyScrubber Free Tier: Payload truncated to ${charLimit.toLocaleString()} chars. Upgrade to PRO ($15/mo or $110 Lifetime) or Developer SDK ($299/mo) for unlimited payload processing: https://privacyscrubber.com/pricing]`;
2740
2808
  return { processedText: text.substring(0, charLimit), wasTruncated: true, upsellNotice, charLimit };
2741
2809
  }
@@ -2745,10 +2813,20 @@ function truncateIfFree(text, isPro, charLimit = 15000) {
2745
2813
  // Start the server transport
2746
2814
  const transport = new StdioServerTransport();
2747
2815
  server.connect(transport).then(() => {
2748
- mcpLog(`${colors.greenBold}✅ PrivacyScrubber ZTDS MCP Server v${MCP_VERSION} started successfully.${colors.reset}\n`);
2749
- mcpLog(`${colors.cyan}📦 Need programmatic in-code redaction? Try: npm install @privacyscrubber/sdk${colors.reset}\n`);
2750
- mcpLog(`${colors.yellowBold}⭐ Star us on GitHub: https://github.com/moxno/privacyscrubber-mcp${colors.reset}\n`);
2751
- mcpLog(`${colors.cyan}👉 Developer SDK & Enterprise: https://privacyscrubber.com/sdk/${colors.reset}\n`);
2816
+ const license = checkLicenseStatus();
2817
+ const tierName = (license.type || license.tier || 'PRO').toString().toUpperCase();
2818
+ const licenseNotice = license.isPro
2819
+ ? `${colors.greenBold}[License] ${tierName} Active (Unlimited Nodes & Profiles)${colors.reset}`
2820
+ : `${colors.yellowBold}[Tier] Community Free (15,000 chars universal / 5,000 chars vertical quota)${colors.reset}`;
2821
+
2822
+ mcpLog(`\n${colors.cyanBold}======================================================================${colors.reset}\n`);
2823
+ mcpLog(`${colors.greenBold}[PrivacyScrubber MCP v${MCP_VERSION}] Zero-Trust Data Sanitization Active${colors.reset}\n`);
2824
+ mcpLog(`${colors.cyan}[Architecture] 100% Local In-Memory RAM | 0 Bytes Network Egress${colors.reset}\n`);
2825
+ mcpLog(`${licenseNotice}\n`);
2826
+ mcpLog(`${colors.dim}[Developer SDK] In-code headless redaction: npm install @privacyscrubber/sdk${colors.reset}\n`);
2827
+ mcpLog(`${colors.dim}[TEAMS & Enterprise] Multi-seat governance & Air-gap: https://privacyscrubber.com/pricing${colors.reset}\n`);
2828
+ mcpLog(`${colors.yellow}[GitHub] Public Server Repository: https://github.com/moxno/privacyscrubber-mcp${colors.reset}\n`);
2829
+ mcpLog(`${colors.cyanBold}======================================================================${colors.reset}\n\n`);
2752
2830
  }).catch((error) => {
2753
2831
  console.error("Failed to connect MCP server transport:", error);
2754
2832
  process.exit(1);
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.3",
3
3
  "name": "privacyscrubber/pii-masking-mcp",
4
- "version": "2.2.7",
4
+ "version": "2.3.1",
5
5
  "description": "CISO-Approved Zero-Trust PII & Secrets Redaction MCP Server for Cursor, Windsurf, and Claude Desktop. Prevent API leaks and comply with HIPAA/SOC 2 locally.",
6
6
  "author": {
7
7
  "name": "PrivacyScrubber",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privacyscrubber/mcp-server",
3
- "version": "2.2.7",
3
+ "version": "2.3.2",
4
4
  "mcpName": "io.github.moxno/privacyscrubber-mcp",
5
5
  "description": "CISO-Approved Zero-Trust PII & Secrets Redaction MCP Server for Cursor, Windsurf, and Claude Desktop. Prevent API leaks and comply with HIPAA/SOC 2 locally.",
6
6
  "main": "index.js",
@@ -35,9 +35,9 @@
35
35
  },
36
36
  "scripts": {
37
37
  "start": "node index.js",
38
- "build": "node ../scripts/build-mcp.js",
38
+ "build": "node -e \"const fs=require('fs'); if(fs.existsSync('../scripts/build-mcp.js')) require('child_process').execSync('node ../scripts/build-mcp.js', {stdio:'inherit'}); else console.log('MCP assets are pre-built.');\"",
39
39
  "test": "node test-mcp.js && node test-mcp-deep.js",
40
- "prepublishOnly": "npm run build && npm test"
40
+ "prepublishOnly": "npm test"
41
41
  },
42
42
  "dependencies": {
43
43
  "@modelcontextprotocol/sdk": "^1.29.0",