@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/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
- console.log('🛡️ PrivacyScrubber MCP Server 1-Click Installer');
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
- let claudeConfigPath = '';
16
- if (process.platform === 'darwin') {
17
- claudeConfigPath = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
18
- } else if (process.platform === 'win32') {
19
- claudeConfigPath = path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
20
- } else {
21
- // Linux fallback, though Claude Desktop is not officially supported on Linux yet, we check common paths
22
- claudeConfigPath = path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');
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
- // 1. Configure Claude Desktop
26
- let installedClaude = false;
27
- try {
28
- let configDir = path.dirname(claudeConfigPath);
29
- if (!fs.existsSync(configDir)) {
30
- fs.mkdirSync(configDir, { recursive: true });
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
- let config = {};
34
- if (fs.existsSync(claudeConfigPath)) {
35
- const rawData = fs.readFileSync(claudeConfigPath, 'utf8');
36
- if (rawData.trim() !== '') {
37
- config = JSON.parse(rawData);
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 (!config.mcpServers) {
42
- config.mcpServers = {};
171
+ if (typeof configObj !== 'object' || configObj === null || Array.isArray(configObj)) {
172
+ configObj = {};
43
173
  }
44
174
 
45
- config.mcpServers['privacyscrubber'] = mcpConfig;
175
+ if (!configObj.mcpServers || typeof configObj.mcpServers !== 'object' || Array.isArray(configObj.mcpServers)) {
176
+ configObj.mcpServers = {};
177
+ }
46
178
 
47
- fs.writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2), 'utf8');
48
- console.log(`✅ [Claude Desktop] Successfully injected PrivacyScrubber MCP into:`);
49
- console.log(` ${claudeConfigPath}`);
50
- installedClaude = true;
51
- } catch (e) {
52
- console.log(`⚠️ [Claude Desktop] Could not configure automatically: ${e.message}`);
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
- console.log('\n---');
56
- console.log('\n🔵 [Cursor / Windsurf Users]');
57
- console.log('To install PrivacyScrubber in Cursor or Windsurf:');
58
- console.log('1. Open Settings -> Features -> MCP Servers (or type "MCP" in settings).');
59
- console.log('2. Click "+ Add New MCP Server".');
60
- console.log('3. Set Name: privacyscrubber');
61
- console.log('4. Set Type: command');
62
- console.log('5. Set Command: npx -y @privacyscrubber/mcp-server');
63
- console.log('\n🚀 Installation complete! Restart your IDE or Claude Desktop to start redacting PII locally.');
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.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.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": "echo 'No build step required for vanilla JS'",
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": "MIT",
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",
@@ -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.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);