@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.
- package/.well-known/mcp/server-card.json +78 -0
- package/LICENSE +23 -0
- package/README.md +49 -25
- package/index.js +122 -44
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/pii-masking-run.js +53 -6
- package/ps-license-manager.cjs +1 -1
- package/ps-license-manager.js +1 -1
- package/ps-pii-engine.cjs +1 -1
- package/ps-pii-engine.js +1 -1
- package/scrubber-core.cjs +1 -1
- package/server.json +2 -2
- package/smithery.yaml +4 -1
|
@@ -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
|
[](https://osf.io/5byjf/)
|
|
11
11
|
[](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=7335581)
|
|
12
12
|
[](https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/)
|
|
13
|
+
[](https://ztds.ai/registry/#privacyscrubber-mcp)
|
|
13
14
|
[](https://smithery.ai/servers/privacyscrubber/pii-masking-mcp)
|
|
14
15
|
[](https://cursor.directory/plugins/privacyscrubber-mcp)
|
|
15
16
|
[](https://glama.ai/mcp/servers/moxno/privacyscrubber-mcp)
|
|
@@ -17,13 +18,21 @@
|
|
|
17
18
|
[](https://theresanaiforthat.com/ai/privacy-scrubber/)
|
|
18
19
|
[](https://privacyscrubber.com)
|
|
19
20
|
[](https://privacyscrubber.com)
|
|
20
|
-
[-06b6d4.svg)](https://privacyscrubber.com/patents/)
|
|
22
|
+
[](https://privacyscrubber.com/sdk/?utm_source=mcp_readme)
|
|
23
|
+
[](https://privacyscrubber.com/pricing?tier=teams&utm_source=mcp_readme)
|
|
21
24
|
[](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
|
-
>
|
|
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.
|
|
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
|
-
>
|
|
179
|
-
> - **Self-Serve Developer SDK ($299/mo flat or $2,990/yr):** Unlimited internal backend nodes, microservices, and RAG pipelines. [Developer SDK
|
|
180
|
-
> - **PrivacyScrubber TEAMS ($99/mo flat):** Unlimited team seats, centralized policy enforcement, encrypted session handoff. [Deploy TEAMS](https://privacyscrubber.com/teams
|
|
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**:
|
|
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.
|
|
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
|
|
432
|
-
| :--- | :--- | :--- | :--- |
|
|
433
|
-
| **
|
|
434
|
-
| **
|
|
435
|
-
| **
|
|
436
|
-
| **
|
|
437
|
-
| **
|
|
438
|
-
| **
|
|
439
|
-
| **
|
|
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
|
|
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 &
|
|
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
|
-
- **
|
|
559
|
-
- **
|
|
560
|
-
- **Filing / Priority Date:** September 14, 2026
|
|
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
|
-
- **
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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 => ['
|
|
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
|
-
? `> *
|
|
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())
|
|
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}
|
|
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,
|
|
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
|
|
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(
|
|
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
|
-
|
|
1340
|
+
currentIgnoreList.add(original);
|
|
1275
1341
|
// Remove the token from the session map
|
|
1276
|
-
delete
|
|
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:** ${
|
|
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}
|
|
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}
|
|
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,
|
|
1991
|
-
const resStdout = performSanitization(cleanStdout, targetProfile,
|
|
1992
|
-
const resStderr = performSanitization(cleanStderr, targetProfile,
|
|
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
|
|
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}
|
|
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
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
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.
|
|
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
|
|
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
|
|
40
|
+
"prepublishOnly": "npm test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.29.0",
|