@privacyscrubber/mcp-server 2.2.2 → 2.2.5
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/LICENSE.md +23 -0
- package/README.md +59 -7
- package/index.js +837 -24
- package/install.js +291 -41
- package/manifest.json +1 -1
- package/package.json +10 -5
- package/pii-masking-run.js +1 -1
- package/ps-license-manager.cjs +11 -257
- package/ps-license-manager.js +11 -257
- package/ps-pii-engine.cjs +8 -2774
- package/ps-pii-engine.js +8 -2774
- package/scrubber-core.cjs +8 -1753
- package/server.json +2 -2
- package/.well-known/mcp/server-card.json +0 -78
- package/LICENSE +0 -21
package/install.js
CHANGED
|
@@ -1,63 +1,313 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* PrivacyScrubber MCP Server Universal Auto-Configurator & Installer
|
|
5
|
+
* Supports: Claude Desktop, Cursor (Global & Workspace), Windsurf, Cline / Roo-Code
|
|
6
|
+
*
|
|
7
|
+
* Safely merges 'privacyscrubber' into mcpServers without corrupting or deleting existing tools.
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
import fs from 'fs';
|
|
4
11
|
import path from 'path';
|
|
5
12
|
import os from 'os';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
6
14
|
|
|
7
|
-
|
|
8
|
-
console.log('================================================\n');
|
|
9
|
-
|
|
10
|
-
const mcpConfig = {
|
|
15
|
+
const MCP_CONFIG_SNIPPET = {
|
|
11
16
|
command: "npx",
|
|
12
17
|
args: ["-y", "@privacyscrubber/mcp-server"]
|
|
13
18
|
};
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
export function getSupportedTargets(customWorkspaceDir = null) {
|
|
21
|
+
const home = os.homedir();
|
|
22
|
+
const platform = process.platform;
|
|
23
|
+
const targets = [];
|
|
24
|
+
|
|
25
|
+
// 1. Claude Desktop
|
|
26
|
+
let claudePath = '';
|
|
27
|
+
if (platform === 'darwin') {
|
|
28
|
+
claudePath = path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
29
|
+
} else if (platform === 'win32') {
|
|
30
|
+
claudePath = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
|
|
31
|
+
} else {
|
|
32
|
+
claudePath = path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
|
|
33
|
+
}
|
|
34
|
+
targets.push({
|
|
35
|
+
id: 'claude',
|
|
36
|
+
name: 'Claude Desktop',
|
|
37
|
+
path: claudePath,
|
|
38
|
+
autoCreate: true
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 2. Cursor Global (~/.cursor/mcp.json)
|
|
42
|
+
const cursorGlobalHome = path.join(home, '.cursor', 'mcp.json');
|
|
43
|
+
targets.push({
|
|
44
|
+
id: 'cursor',
|
|
45
|
+
name: 'Cursor (Global ~/.cursor/mcp.json)',
|
|
46
|
+
path: cursorGlobalHome,
|
|
47
|
+
autoCreate: true
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 3. Cursor Global Storage (Application Support / AppData)
|
|
51
|
+
let cursorStoragePath = '';
|
|
52
|
+
if (platform === 'darwin') {
|
|
53
|
+
cursorStoragePath = path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');
|
|
54
|
+
} else if (platform === 'win32') {
|
|
55
|
+
cursorStoragePath = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');
|
|
56
|
+
} else {
|
|
57
|
+
cursorStoragePath = path.join(home, '.config', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');
|
|
58
|
+
}
|
|
59
|
+
targets.push({
|
|
60
|
+
id: 'cursor-storage',
|
|
61
|
+
name: 'Cursor (App Storage)',
|
|
62
|
+
path: cursorStoragePath,
|
|
63
|
+
autoCreate: false
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 4. Cursor Workspace (if .cursor dir or project exists in cwd)
|
|
67
|
+
const cwd = customWorkspaceDir || process.cwd();
|
|
68
|
+
const cursorWorkspacePath = path.join(cwd, '.cursor', 'mcp.json');
|
|
69
|
+
targets.push({
|
|
70
|
+
id: 'cursor-workspace',
|
|
71
|
+
name: `Cursor (Workspace: ${path.basename(cwd)}/.cursor/mcp.json)`,
|
|
72
|
+
path: cursorWorkspacePath,
|
|
73
|
+
autoCreate: fs.existsSync(path.join(cwd, '.cursor'))
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// 5. Windsurf (~/.codeium/windsurf/mcp_config.json)
|
|
77
|
+
const windsurfPath = path.join(
|
|
78
|
+
platform === 'win32' ? (process.env.USERPROFILE || home) : home,
|
|
79
|
+
'.codeium',
|
|
80
|
+
'windsurf',
|
|
81
|
+
'mcp_config.json'
|
|
82
|
+
);
|
|
83
|
+
targets.push({
|
|
84
|
+
id: 'windsurf',
|
|
85
|
+
name: 'Windsurf (Codeium)',
|
|
86
|
+
path: windsurfPath,
|
|
87
|
+
autoCreate: false
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 6. Cline (VS Code Extension)
|
|
91
|
+
let clinePath = '';
|
|
92
|
+
if (platform === 'darwin') {
|
|
93
|
+
clinePath = path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
94
|
+
} else if (platform === 'win32') {
|
|
95
|
+
clinePath = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
96
|
+
} else {
|
|
97
|
+
clinePath = path.join(home, '.config', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
98
|
+
}
|
|
99
|
+
targets.push({
|
|
100
|
+
id: 'cline',
|
|
101
|
+
name: 'Cline (VS Code)',
|
|
102
|
+
path: clinePath,
|
|
103
|
+
autoCreate: false
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 7. Roo-Code (VS Code Extension)
|
|
107
|
+
let rooPath = '';
|
|
108
|
+
if (platform === 'darwin') {
|
|
109
|
+
rooPath = path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'cline_mcp_settings.json');
|
|
110
|
+
} else if (platform === 'win32') {
|
|
111
|
+
rooPath = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'cline_mcp_settings.json');
|
|
112
|
+
} else {
|
|
113
|
+
rooPath = path.join(home, '.config', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'cline_mcp_settings.json');
|
|
114
|
+
}
|
|
115
|
+
targets.push({
|
|
116
|
+
id: 'roo',
|
|
117
|
+
name: 'Roo-Code (VS Code)',
|
|
118
|
+
path: rooPath,
|
|
119
|
+
autoCreate: false
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return targets;
|
|
23
123
|
}
|
|
24
124
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
125
|
+
export function parseArgs(rawArgs = process.argv.slice(2)) {
|
|
126
|
+
const options = {
|
|
127
|
+
dryRun: false,
|
|
128
|
+
remove: false,
|
|
129
|
+
all: false,
|
|
130
|
+
targets: [],
|
|
131
|
+
help: false
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
for (const arg of rawArgs) {
|
|
135
|
+
if (arg === '--dry-run') options.dryRun = true;
|
|
136
|
+
else if (arg === '--remove' || arg === '--uninstall') options.remove = true;
|
|
137
|
+
else if (arg === '--all') options.all = true;
|
|
138
|
+
else if (arg === '--claude') options.targets.push('claude');
|
|
139
|
+
else if (arg === '--cursor') options.targets.push('cursor', 'cursor-storage', 'cursor-workspace');
|
|
140
|
+
else if (arg === '--windsurf') options.targets.push('windsurf');
|
|
141
|
+
else if (arg === '--cline') options.targets.push('cline', 'roo');
|
|
142
|
+
else if (arg === '--workspace') options.targets.push('cursor-workspace');
|
|
143
|
+
else if (arg === '--help' || arg === '-h') options.help = true;
|
|
31
144
|
}
|
|
32
145
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
146
|
+
return options;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function safeMergeMcpConfig(filePath, snippet = MCP_CONFIG_SNIPPET, remove = false) {
|
|
150
|
+
let existingContent = '';
|
|
151
|
+
let configObj = {};
|
|
152
|
+
const fileExisted = fs.existsSync(filePath);
|
|
153
|
+
|
|
154
|
+
if (fileExisted) {
|
|
155
|
+
existingContent = fs.readFileSync(filePath, 'utf8');
|
|
156
|
+
if (existingContent.trim()) {
|
|
157
|
+
try {
|
|
158
|
+
configObj = JSON.parse(existingContent);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
// Backup corrupt file before modifying
|
|
161
|
+
const bakPath = `${filePath}.bak.${Date.now()}`;
|
|
162
|
+
fs.writeFileSync(bakPath, existingContent, 'utf8');
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
error: `Existing JSON was invalid (${e.message}). Created backup: ${bakPath}`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
38
168
|
}
|
|
39
169
|
}
|
|
40
170
|
|
|
41
|
-
if (
|
|
42
|
-
|
|
171
|
+
if (typeof configObj !== 'object' || configObj === null || Array.isArray(configObj)) {
|
|
172
|
+
configObj = {};
|
|
43
173
|
}
|
|
44
174
|
|
|
45
|
-
|
|
175
|
+
if (!configObj.mcpServers || typeof configObj.mcpServers !== 'object' || Array.isArray(configObj.mcpServers)) {
|
|
176
|
+
configObj.mcpServers = {};
|
|
177
|
+
}
|
|
46
178
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
179
|
+
if (remove) {
|
|
180
|
+
if (configObj.mcpServers['privacyscrubber']) {
|
|
181
|
+
delete configObj.mcpServers['privacyscrubber'];
|
|
182
|
+
} else {
|
|
183
|
+
return { success: true, modified: false, message: 'privacyscrubber was not present' };
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
configObj.mcpServers['privacyscrubber'] = snippet;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const updatedJson = JSON.stringify(configObj, null, 2) + '\n';
|
|
190
|
+
return {
|
|
191
|
+
success: true,
|
|
192
|
+
modified: true,
|
|
193
|
+
fileExisted,
|
|
194
|
+
updatedJson,
|
|
195
|
+
serverCount: Object.keys(configObj.mcpServers).length
|
|
196
|
+
};
|
|
53
197
|
}
|
|
54
198
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
console.log(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
199
|
+
export async function runInstaller(rawArgs = process.argv.slice(2)) {
|
|
200
|
+
const options = parseArgs(rawArgs);
|
|
201
|
+
|
|
202
|
+
if (options.help) {
|
|
203
|
+
console.log(`
|
|
204
|
+
PrivacyScrubber MCP Auto-Configurator
|
|
205
|
+
Usage:
|
|
206
|
+
npx @privacyscrubber/mcp-server init [options]
|
|
207
|
+
npx @privacyscrubber/mcp-server-install [options]
|
|
208
|
+
|
|
209
|
+
Options:
|
|
210
|
+
--all Configure all detected and standard client paths
|
|
211
|
+
--cursor Configure Cursor only (~/.cursor/mcp.json)
|
|
212
|
+
--claude Configure Claude Desktop only
|
|
213
|
+
--windsurf Configure Windsurf only
|
|
214
|
+
--cline Configure Cline / Roo-Code only
|
|
215
|
+
--workspace Configure current project .cursor/mcp.json
|
|
216
|
+
--dry-run Preview configuration changes without writing files
|
|
217
|
+
--remove Remove PrivacyScrubber from all target client configurations
|
|
218
|
+
-h, --help Display this help guide
|
|
219
|
+
`);
|
|
220
|
+
return { configured: 0 };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('--------------------------------------------------');
|
|
224
|
+
console.log('PrivacyScrubber MCP Universal Auto-Configurator');
|
|
225
|
+
console.log('Zero-Trust Data Sanitization (ZTDS) for AI IDEs');
|
|
226
|
+
console.log('--------------------------------------------------\n');
|
|
227
|
+
|
|
228
|
+
if (options.dryRun) {
|
|
229
|
+
console.log('[DRY-RUN MODE] No files will be modified on disk.\n');
|
|
230
|
+
}
|
|
231
|
+
if (options.remove) {
|
|
232
|
+
console.log('[UNINSTALL MODE] Removing PrivacyScrubber MCP server from configs.\n');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const targets = getSupportedTargets();
|
|
236
|
+
let configuredCount = 0;
|
|
237
|
+
let detectedClients = 0;
|
|
238
|
+
|
|
239
|
+
for (const target of targets) {
|
|
240
|
+
const isFiltered = options.targets.length > 0 && !options.targets.includes(target.id);
|
|
241
|
+
if (isFiltered) continue;
|
|
242
|
+
|
|
243
|
+
const fileExists = fs.existsSync(target.path);
|
|
244
|
+
const parentDirExists = fs.existsSync(path.dirname(target.path));
|
|
245
|
+
const shouldProcess = fileExists || target.autoCreate || (options.all && parentDirExists);
|
|
246
|
+
|
|
247
|
+
if (!shouldProcess && !options.targets.includes(target.id)) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
detectedClients++;
|
|
252
|
+
console.log(`[TARGET] ${target.name}`);
|
|
253
|
+
console.log(` Path: ${target.path}`);
|
|
254
|
+
|
|
255
|
+
const result = safeMergeMcpConfig(target.path, MCP_CONFIG_SNIPPET, options.remove);
|
|
256
|
+
|
|
257
|
+
if (!result.success) {
|
|
258
|
+
console.log(` [ERROR]: ${result.error}\n`);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (result.modified === false) {
|
|
263
|
+
console.log(` [SKIP]: ${result.message}\n`);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!options.dryRun) {
|
|
268
|
+
try {
|
|
269
|
+
const dir = path.dirname(target.path);
|
|
270
|
+
if (!fs.existsSync(dir)) {
|
|
271
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
272
|
+
}
|
|
273
|
+
fs.writeFileSync(target.path, result.updatedJson, 'utf8');
|
|
274
|
+
console.log(` [OK]: Successfully ${options.remove ? 'removed from' : 'written to'} config (${result.serverCount} total MCP servers).`);
|
|
275
|
+
configuredCount++;
|
|
276
|
+
} catch (err) {
|
|
277
|
+
console.log(` [ERROR]: Failed to write file: ${err.message}`);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
console.log(` [DRY-RUN OK]: Would write valid configuration (${result.serverCount} servers).`);
|
|
281
|
+
configuredCount++;
|
|
282
|
+
}
|
|
283
|
+
console.log('');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log('--------------------------------------------------');
|
|
287
|
+
if (configuredCount > 0) {
|
|
288
|
+
console.log(`[SUMMARY] ${options.remove ? 'Cleaned' : 'Configured'} ${configuredCount} AI client(s) successfully.`);
|
|
289
|
+
if (!options.remove) {
|
|
290
|
+
console.log('Next step: Restart your AI IDE or Claude Desktop to activate PrivacyScrubber.');
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
console.log('[SUMMARY] No active client configurations were modified.');
|
|
294
|
+
console.log('Tip: Run with --all or specify client directly (--cursor, --claude, --windsurf).');
|
|
295
|
+
}
|
|
296
|
+
console.log('--------------------------------------------------\n');
|
|
297
|
+
|
|
298
|
+
return { configured: configuredCount, detected: detectedClients };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Auto-run when executed as entry script
|
|
302
|
+
const isMain = process.argv[1] && (
|
|
303
|
+
path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url)) ||
|
|
304
|
+
process.argv[1].endsWith('install.js') ||
|
|
305
|
+
process.argv[1].endsWith('privacyscrubber-mcp-install')
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
if (isMain) {
|
|
309
|
+
runInstaller().catch(err => {
|
|
310
|
+
console.error('Fatal installer error:', err);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
});
|
|
313
|
+
}
|
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.
|
|
4
|
+
"version": "2.2.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.2.
|
|
3
|
+
"version": "2.2.5",
|
|
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",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"bin": {
|
|
28
28
|
"mcp-server": "index.js",
|
|
29
29
|
"privacyscrubber-mcp": "index.js",
|
|
30
|
+
"ps-mcp": "index.js",
|
|
30
31
|
"pii-masking-run": "pii-masking-run.js",
|
|
31
32
|
"ps-guard": "pii-masking-run.js",
|
|
32
33
|
"agentic-guard": "pii-masking-run.js",
|
|
@@ -34,8 +35,9 @@
|
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
36
37
|
"start": "node index.js",
|
|
37
|
-
"build": "
|
|
38
|
-
"test": "node test-mcp.js && node test-mcp-deep.js"
|
|
38
|
+
"build": "node ../scripts/build-mcp.js",
|
|
39
|
+
"test": "node test-mcp.js && node test-mcp-deep.js",
|
|
40
|
+
"prepublishOnly": "npm run build && npm test"
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|
|
41
43
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -64,10 +66,13 @@
|
|
|
64
66
|
"hipaa",
|
|
65
67
|
"soc2",
|
|
66
68
|
"grc",
|
|
67
|
-
"ciso"
|
|
69
|
+
"ciso",
|
|
70
|
+
"presidio",
|
|
71
|
+
"presidio-alternative",
|
|
72
|
+
"langchain"
|
|
68
73
|
],
|
|
69
74
|
"author": "Ilya Sibiryakov (BrandMeWeb)",
|
|
70
|
-
"license": "
|
|
75
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
71
76
|
"homepage": "https://privacyscrubber.com/pii-mcp/?utm_source=npm&utm_medium=readme&utm_campaign=mcp_server",
|
|
72
77
|
"repository": {
|
|
73
78
|
"type": "git",
|
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.2.
|
|
30
|
+
let version = '2.2.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);
|