@privacyscrubber/mcp-server 2.4.2 → 2.4.4
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 +1 -1
- package/index.js +2 -2
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/pii-masking-run.js +15 -10
- package/ps-license-manager.cjs +11 -261
- package/ps-license-manager.js +11 -261
- package/ps-pii-engine.cjs +8 -3611
- package/ps-pii-engine.js +8 -3611
- package/scrubber-core.cjs +8 -1801
- package/server.json +3 -3
|
@@ -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
|
@@ -482,7 +482,7 @@ Returns a visual dashboard showing your current tier, session request count, act
|
|
|
482
482
|
* **Response Example (Free Tier):**
|
|
483
483
|
```
|
|
484
484
|
╔══════════════════════════════════════════════════╗
|
|
485
|
-
║ PrivacyScrubber MCP Server v2.4.
|
|
485
|
+
║ PrivacyScrubber MCP Server v2.4.4 ║
|
|
486
486
|
╠══════════════════════════════════════════════════╣
|
|
487
487
|
║ 🔓 Tier: FREE ║
|
|
488
488
|
║ 📊 Session requests: 5 ║
|
package/index.js
CHANGED
|
@@ -352,7 +352,7 @@ export class StreamingDetokenizer {
|
|
|
352
352
|
const tailLen = 160;
|
|
353
353
|
const tail = this.pendingBuffer.length > tailLen ? this.pendingBuffer.slice(-tailLen) : this.pendingBuffer;
|
|
354
354
|
const offset = this.pendingBuffer.length > tailLen ? this.pendingBuffer.length - tailLen : 0;
|
|
355
|
-
const m = tail.match(/(?:(?:\[|【|[|〔|「|『|<|\{\{?|__?|%5[Bb]|\\u005[Bb]|&(?:amp;)?(?:#0*91;|#[xX]0*5[Bb];|lbrack;))[A-Za-z0-9_
|
|
355
|
+
const m = tail.match(/(?:(?:\[|【|[|〔|「|『|<|\{\{?|__?|%5[Bb]|\\u005[Bb]|&(?:amp;)?(?:#0*91;|#[xX]0*5[Bb];|lbrack;))[A-Za-z0-9_\-\u0410-\u044F\u0401\u0451 *`~.\\]{0,128}(?:%5?|&(?:a(?:m(?:p(?:;)?)?)?)?(?:#(?:0*9?3?|[xX]0*5?[Dd]?)?|r(?:b(?:r(?:a(?:c(?:k)?)?)?)?)?)?)?|%[0-9A-Fa-f]?|\\(?:u(?:0(?:0(?:5)?)?)?)?|&(?:a(?:m(?:p(?:;)?)?)?)?(?:#(?:0*9?1?|[xX]0*5?)?|l(?:b(?:r(?:a(?:c(?:k)?)?)?)?)?)?)$/);
|
|
356
356
|
|
|
357
357
|
if (m) {
|
|
358
358
|
const candidate = m[0];
|
|
@@ -814,7 +814,7 @@ function buildPsr2Receipt(tokenMap, timestamp = new Date().toISOString()) {
|
|
|
814
814
|
const labels = (tokenMap && typeof tokenMap === 'object' ? Object.keys(tokenMap) : []).slice().sort();
|
|
815
815
|
const counts = {};
|
|
816
816
|
for (const t of labels) {
|
|
817
|
-
const m = t.match(
|
|
817
|
+
const m = String(t).trim().match(/^(?:\[|<|\{\{|__)([A-Z0-9_]+)_\d+(?:\]|>|\}\}|__)$/);
|
|
818
818
|
const type = m ? m[1] : 'CUSTOM';
|
|
819
819
|
counts[type] = (counts[type] || 0) + 1;
|
|
820
820
|
}
|
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.
|
|
4
|
+
"version": "2.4.4",
|
|
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.4.
|
|
3
|
+
"version": "2.4.4",
|
|
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",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"start": "node index.js",
|
|
37
|
-
"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.');\"",
|
|
37
|
+
"build": "node -e \"const fs=require('fs'); if(fs.existsSync('../scripts/build-mcp.js')) require('child_process').execSync('node ../scripts/build-mcp.js --keep-obfuscated', {stdio:'inherit'}); else console.log('MCP assets are pre-built.');\"",
|
|
38
38
|
"test": "node test-mcp.js && node test-mcp-deep.js && node test-mcp-streaming.js",
|
|
39
39
|
"test:stream": "node test-mcp-streaming.js",
|
|
40
|
-
"prepublishOnly": "npm test"
|
|
40
|
+
"prepublishOnly": "npm run build && node -e \"const fs=require('fs'); for(const f of ['ps-license-manager.js','ps-license-manager.cjs','ps-pii-engine.js','ps-pii-engine.cjs','scrubber-core.cjs']){const c=fs.readFileSync(f,'utf8'); if(c.includes('OFFLINE_KEY_SALT')||c.includes('computeV2Signature')||!c.includes('_0x')){console.error('FATAL: Unobfuscated '+f+' detected!'); process.exit(1);}}\" && npm test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
package/pii-masking-run.js
CHANGED
|
@@ -27,7 +27,7 @@ const cmdIndex = args.indexOf("--");
|
|
|
27
27
|
|
|
28
28
|
if (args.includes("--version") || args.includes("-v")) {
|
|
29
29
|
const pkgPath = path.resolve(__dirname, './package.json');
|
|
30
|
-
let version = '2.4.
|
|
30
|
+
let version = '2.4.4';
|
|
31
31
|
try { version = JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version; } catch (_) {}
|
|
32
32
|
console.log(`ps-guard / pii-masking-run v${version} (PrivacyScrubber MCP)`);
|
|
33
33
|
process.exit(0);
|
|
@@ -250,18 +250,23 @@ function performSanitization(text) {
|
|
|
250
250
|
if (!license.isPro && result.tokenMap) {
|
|
251
251
|
// Free tier: check for secrets and warn
|
|
252
252
|
let hasSecrets = false;
|
|
253
|
+
const SECRET_TOKEN_RE = /^\[(?:SECRET|API_TOKEN|AWS_KEY|JWT_TOKEN|STRIPE_KEY|OPENAI_KEY|DB_URI)_\d+\]$/;
|
|
253
254
|
Object.entries(result.tokenMap).forEach(([token, original]) => {
|
|
254
|
-
let isSecret =
|
|
255
|
-
|
|
256
|
-
detector
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
255
|
+
let isSecret = SECRET_TOKEN_RE.test(token);
|
|
256
|
+
if (!isSecret) {
|
|
257
|
+
for (const detector of DEVOPS_SECRETS_DETECTOR) {
|
|
258
|
+
detector.regex.lastIndex = 0;
|
|
259
|
+
if (detector.regex.test(original)) {
|
|
260
|
+
isSecret = true;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
if (isSecret) {
|
|
263
266
|
hasSecrets = true;
|
|
264
267
|
result.scrubbedText = result.scrubbedText.replaceAll(token, '[SECRET_BLOCKED_UPGRADE_TO_PRO]');
|
|
268
|
+
delete result.tokenMap[token];
|
|
269
|
+
delete localSessionMap[token];
|
|
265
270
|
}
|
|
266
271
|
});
|
|
267
272
|
|
|
@@ -280,7 +285,7 @@ function buildCisoAuditTelemetry(currentTokenMap = {}) {
|
|
|
280
285
|
const keys = Array.isArray(currentTokenMap) ? currentTokenMap : Object.keys(currentTokenMap);
|
|
281
286
|
for (const t of keys) {
|
|
282
287
|
const tokenStr = typeof t === 'string' ? t : (t.token || t.mask || '');
|
|
283
|
-
const match = tokenStr.match(/\[([A-
|
|
288
|
+
const match = tokenStr.match(/\[([A-Z0-9_]+)_\d+\]/);
|
|
284
289
|
const baseType = match ? match[1] : (tokenStr.replace(/\[|\]/g, '').replace(/_[0-9]+$/, '') || 'CUSTOM');
|
|
285
290
|
entities[baseType] = (entities[baseType] || 0) + 1;
|
|
286
291
|
totalCount++;
|
|
@@ -289,7 +294,7 @@ function buildCisoAuditTelemetry(currentTokenMap = {}) {
|
|
|
289
294
|
const types = Object.keys(entities);
|
|
290
295
|
let riskLevel = 'LOW EXPOSURE';
|
|
291
296
|
if (totalCount > 0) {
|
|
292
|
-
const hasHighRisk = types.some(t => ['ID', 'SSN', 'CREDIT_CARD', 'PASSPORT', 'BANK', 'API_KEY', 'SECRET', 'PASSWORD', 'MRN', 'KEY'].includes(t));
|
|
297
|
+
const hasHighRisk = types.some(t => ['ID', 'SSN', 'CREDIT_CARD', 'PASSPORT', 'BANK', 'API_KEY', 'SECRET', 'PASSWORD', 'MRN', 'KEY', 'AWS_KEY', 'STRIPE_KEY', 'JWT_TOKEN', 'API_TOKEN'].includes(t));
|
|
293
298
|
if (hasHighRisk || types.length >= 3 || totalCount >= 10) {
|
|
294
299
|
riskLevel = 'CRITICAL (HIGH EXPOSURE)';
|
|
295
300
|
} else if (types.length >= 2 || totalCount >= 3) {
|
|
@@ -399,7 +404,7 @@ function buildPsr2Receipt(tokenMap, timestamp = new Date().toISOString()) {
|
|
|
399
404
|
const labels = (tokenMap && typeof tokenMap === 'object' ? Object.keys(tokenMap) : []).slice().sort();
|
|
400
405
|
const counts = {};
|
|
401
406
|
for (const t of labels) {
|
|
402
|
-
const m = t.match(
|
|
407
|
+
const m = String(t).trim().match(/^(?:\[|<|\{\{|__)([A-Z0-9_]+)_\d+(?:\]|>|\}\}|__)$/);
|
|
403
408
|
const type = m ? m[1] : 'CUSTOM';
|
|
404
409
|
counts[type] = (counts[type] || 0) + 1;
|
|
405
410
|
}
|