fullcourtdefense-cli 1.2.0 → 1.3.0
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/dist/commands/discover.d.ts +8 -0
- package/dist/commands/discover.js +63 -75
- package/dist/commands/discoverPaths.d.ts +18 -0
- package/dist/commands/discoverPaths.js +194 -0
- package/dist/commands/knownMcpServers.d.ts +8 -0
- package/dist/commands/knownMcpServers.js +68 -0
- package/dist/commands/mcpGateway.d.ts +2 -0
- package/dist/commands/mcpGateway.js +20 -81
- package/dist/index.js +3 -1
- package/package.json +1 -1
|
@@ -23,3 +23,11 @@ export interface DesktopDiscoveryHost {
|
|
|
23
23
|
probeMode: 'config' | 'deep';
|
|
24
24
|
}
|
|
25
25
|
export declare function discoverCommand(args: DiscoverArgs, config: BotGuardConfig): Promise<void>;
|
|
26
|
+
/** Upload-only discover — used after install-mcp-gateway --upload. */
|
|
27
|
+
export declare function runDiscoverUpload(args: {
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
apiUrl?: string;
|
|
30
|
+
userEmail?: string;
|
|
31
|
+
silent?: boolean;
|
|
32
|
+
connectorName?: string;
|
|
33
|
+
}, config: BotGuardConfig): Promise<void>;
|
|
@@ -34,12 +34,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.discoverCommand = discoverCommand;
|
|
37
|
+
exports.runDiscoverUpload = runDiscoverUpload;
|
|
37
38
|
const crypto = __importStar(require("crypto"));
|
|
38
39
|
const fs = __importStar(require("fs"));
|
|
39
40
|
const os = __importStar(require("os"));
|
|
40
|
-
const path = __importStar(require("path"));
|
|
41
41
|
const discoverProxy_1 = require("./discoverProxy");
|
|
42
42
|
const discoverSchedule_1 = require("./discoverSchedule");
|
|
43
|
+
const discoverPaths_1 = require("./discoverPaths");
|
|
44
|
+
const knownMcpServers_1 = require("./knownMcpServers");
|
|
43
45
|
const DEFAULT_API_URL = 'https://api.fullcourtdefense.ai';
|
|
44
46
|
const SECRET_VALUE = /sk-[A-Za-z0-9_-]{12,}|ghp_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{20,}/;
|
|
45
47
|
const SECRET_KEY = /key|token|secret|password|credential|api[_-]?key/i;
|
|
@@ -58,68 +60,9 @@ function buildHostMetadata(userEmail, probeMode = 'config') {
|
|
|
58
60
|
probeMode,
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
|
-
/** Where MCP client configs live
|
|
63
|
+
/** Where MCP client configs live — see discoverPaths.ts for the canonical list. */
|
|
62
64
|
function candidateConfigPaths(cwd, extra) {
|
|
63
|
-
|
|
64
|
-
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
65
|
-
const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
|
|
66
|
-
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
67
|
-
const list = [
|
|
68
|
-
// Cursor
|
|
69
|
-
{ path: path.join(home, '.cursor', 'mcp.json'), source: 'Cursor (global)' },
|
|
70
|
-
{ path: path.join(cwd, '.cursor', 'mcp.json'), source: 'Cursor (project)' },
|
|
71
|
-
// Cursor managed (enterprise)
|
|
72
|
-
{ path: path.join(appData, 'Cursor', 'managed-mcp.json'), source: 'Cursor (managed, win)' },
|
|
73
|
-
{ path: path.join(home, 'Library', 'Application Support', 'Cursor', 'managed-mcp.json'), source: 'Cursor (managed, mac)' },
|
|
74
|
-
{ path: '/etc/cursor/managed-mcp.json', source: 'Cursor (managed, linux)' },
|
|
75
|
-
// Claude Desktop
|
|
76
|
-
{ path: path.join(appData, 'Claude', 'claude_desktop_config.json'), source: 'Claude Desktop (win)' },
|
|
77
|
-
{ path: path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'), source: 'Claude Desktop (mac)' },
|
|
78
|
-
{ path: path.join(xdg, 'Claude', 'claude_desktop_config.json'), source: 'Claude Desktop (linux)' },
|
|
79
|
-
// Claude Code
|
|
80
|
-
{ path: path.join(home, '.claude.json'), source: 'Claude Code' },
|
|
81
|
-
{ path: path.join(home, '.claude', 'settings.json'), source: 'Claude Code (global)' },
|
|
82
|
-
{ path: path.join(cwd, '.claude', 'settings.json'), source: 'Claude Code (project)' },
|
|
83
|
-
{ path: path.join(cwd, '.mcp.json'), source: 'Claude Code (.mcp.json project)' },
|
|
84
|
-
// Gemini CLI
|
|
85
|
-
{ path: path.join(home, '.gemini', 'settings.json'), source: 'Gemini CLI' },
|
|
86
|
-
{ path: path.join(xdg, 'gemini', 'settings.json'), source: 'Gemini CLI (xdg)' },
|
|
87
|
-
// OpenAI Codex
|
|
88
|
-
{ path: path.join(home, '.codex', 'mcp.json'), source: 'Codex' },
|
|
89
|
-
{ path: path.join(xdg, 'codex', 'mcp.json'), source: 'Codex (xdg)' },
|
|
90
|
-
// VS Code (project + user settings)
|
|
91
|
-
{ path: path.join(cwd, '.vscode', 'mcp.json'), source: 'VS Code (project)' },
|
|
92
|
-
{ path: path.join(appData, 'Code', 'User', 'settings.json'), source: 'VS Code (user, win)' },
|
|
93
|
-
{ path: path.join(home, 'Library', 'Application Support', 'Code', 'User', 'settings.json'), source: 'VS Code (user, mac)' },
|
|
94
|
-
{ path: path.join(xdg, 'Code', 'User', 'settings.json'), source: 'VS Code (user, linux)' },
|
|
95
|
-
// Windsurf
|
|
96
|
-
{ path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), source: 'Windsurf' },
|
|
97
|
-
// Kiro / generic
|
|
98
|
-
{ path: path.join(home, '.kiro', 'mcp.json'), source: 'Kiro' },
|
|
99
|
-
// Generic / repo-root
|
|
100
|
-
{ path: path.join(cwd, '.mcp.json'), source: 'Repo (.mcp.json)' },
|
|
101
|
-
{ path: path.join(cwd, 'mcp.json'), source: 'Repo (mcp.json)' },
|
|
102
|
-
];
|
|
103
|
-
if (process.platform === 'win32') {
|
|
104
|
-
list.push({ path: path.join(process.env.ProgramData || 'C:\\ProgramData', 'Cursor', 'managed-mcp.json'), source: 'Cursor (managed, program data)' });
|
|
105
|
-
const packagesDir = path.join(localAppData, 'Packages');
|
|
106
|
-
try {
|
|
107
|
-
for (const packageName of fs.readdirSync(packagesDir)) {
|
|
108
|
-
if (!/^Claude_/i.test(packageName))
|
|
109
|
-
continue;
|
|
110
|
-
list.push({
|
|
111
|
-
path: path.join(packagesDir, packageName, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json'),
|
|
112
|
-
source: 'Claude Desktop (win MSIX)',
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Missing Packages directory is normal on non-Store installs.
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (extra)
|
|
121
|
-
list.push({ path: path.resolve(extra), source: 'Custom path' });
|
|
122
|
-
return list;
|
|
65
|
+
return (0, discoverPaths_1.discoverScanTargets)(cwd, extra);
|
|
123
66
|
}
|
|
124
67
|
/** MCP servers can be declared under several keys across clients. */
|
|
125
68
|
function extractServerMap(parsed) {
|
|
@@ -353,6 +296,16 @@ function applyProxyClassification(servers, scanned, cwd) {
|
|
|
353
296
|
if (server.proxyStatus === 'direct' && server.riskLevel !== 'low') {
|
|
354
297
|
server.warnings.push('Direct MCP — not proxied through Full Court Defense gateway.');
|
|
355
298
|
}
|
|
299
|
+
const info = (0, knownMcpServers_1.knownMcpServerInfo)(server.serverName, server.command);
|
|
300
|
+
if (info) {
|
|
301
|
+
server.serverKind = info.category;
|
|
302
|
+
server.serverDescription = info.description;
|
|
303
|
+
}
|
|
304
|
+
if (server.proxyStatus === 'direct') {
|
|
305
|
+
const hint = (0, knownMcpServers_1.directServerFixHint)(server.serverName);
|
|
306
|
+
if (hint && !server.warnings.includes(hint))
|
|
307
|
+
server.warnings.push(hint);
|
|
308
|
+
}
|
|
356
309
|
}
|
|
357
310
|
return (0, discoverProxy_1.buildClientCoverage)(scanned, servers, cwd);
|
|
358
311
|
}
|
|
@@ -383,6 +336,8 @@ async function upload(servers, host, clientCoverage, apiUrl, apiKey, connectorNa
|
|
|
383
336
|
probeMode: s.probeMode,
|
|
384
337
|
proxyStatus: s.proxyStatus,
|
|
385
338
|
wrapsServerName: s.wrapsServerName,
|
|
339
|
+
serverKind: s.serverKind,
|
|
340
|
+
serverDescription: s.serverDescription,
|
|
386
341
|
tools: s.tools,
|
|
387
342
|
})),
|
|
388
343
|
};
|
|
@@ -427,10 +382,10 @@ async function discoverCommand(args, config) {
|
|
|
427
382
|
const scanned = [];
|
|
428
383
|
let found = [];
|
|
429
384
|
for (const c of candidates) {
|
|
430
|
-
if (!fs.existsSync(c.path))
|
|
431
|
-
continue;
|
|
432
385
|
scanned.push({ path: c.path, source: c.source });
|
|
433
|
-
|
|
386
|
+
if (fs.existsSync(c.path)) {
|
|
387
|
+
found.push(...parseConfigFile(c.path, c.source));
|
|
388
|
+
}
|
|
434
389
|
}
|
|
435
390
|
found = dedupe(found);
|
|
436
391
|
const clientCoverage = applyProxyClassification(found, scanned, cwd);
|
|
@@ -455,7 +410,7 @@ async function discoverCommand(args, config) {
|
|
|
455
410
|
const apiUrl = args.apiUrl || config.apiUrl || DEFAULT_API_URL;
|
|
456
411
|
if (!apiKey)
|
|
457
412
|
process.exit(1);
|
|
458
|
-
if (
|
|
413
|
+
if (scanned.length === 0)
|
|
459
414
|
return;
|
|
460
415
|
await upload(found, host, clientCoverage, apiUrl, apiKey, args.connectorName);
|
|
461
416
|
}
|
|
@@ -469,16 +424,12 @@ async function discoverCommand(args, config) {
|
|
|
469
424
|
console.log(`${COLOR.gray}Looked in Cursor, Claude Desktop/Code, VS Code, Windsurf, Codex, Gemini CLI, and repo-root configs.${COLOR.reset}`);
|
|
470
425
|
return;
|
|
471
426
|
}
|
|
472
|
-
console.log(`${COLOR.bold}
|
|
473
|
-
for (const s of scanned)
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
if (found.length === 0) {
|
|
477
|
-
console.log(`${COLOR.yellow}Config files exist but declare no MCP servers.${COLOR.reset}`);
|
|
478
|
-
return;
|
|
427
|
+
console.log(`${COLOR.bold}AI clients checked (${scanned.length}):${COLOR.reset}`);
|
|
428
|
+
for (const s of scanned) {
|
|
429
|
+
const exists = fs.existsSync(s.path);
|
|
430
|
+
console.log(` ${COLOR.dim}•${COLOR.reset} ${s.source} → ${s.path}${exists ? '' : ` ${COLOR.yellow}(not created yet)${COLOR.reset}`}`);
|
|
479
431
|
}
|
|
480
|
-
|
|
481
|
-
found.sort((a, b) => order[a.riskLevel] - order[b.riskLevel]);
|
|
432
|
+
console.log('');
|
|
482
433
|
if (clientCoverage.length > 0) {
|
|
483
434
|
console.log(`${COLOR.bold}Client coverage:${COLOR.reset}`);
|
|
484
435
|
for (const row of clientCoverage) {
|
|
@@ -487,15 +438,40 @@ async function discoverCommand(args, config) {
|
|
|
487
438
|
? (row.cursorHooksInstalled ? `${COLOR.green}hooks ✓ (${row.cursorHookEvents.join(', ')})${COLOR.reset}` : `${COLOR.yellow}no hooks${COLOR.reset}`)
|
|
488
439
|
: '';
|
|
489
440
|
console.log(` ${COLOR.dim}•${COLOR.reset} ${row.client} — ${row.mcpServerCount} MCP · ${gateway}${hooks ? ` · ${hooks}` : ''}`);
|
|
441
|
+
if (row.clientKey === 'claude_desktop' && !row.mcpGatewayInstalled && row.mcpServerCount === 0) {
|
|
442
|
+
console.log(` ${COLOR.gray}Install: fullcourtdefense install-claude-desktop-mcp-gateway --shield-id ... --mcp-command npm --mcp-args "run mcp"${COLOR.reset}`);
|
|
443
|
+
}
|
|
490
444
|
}
|
|
491
445
|
console.log('');
|
|
492
446
|
}
|
|
447
|
+
if (found.length === 0) {
|
|
448
|
+
console.log(`${COLOR.yellow}No MCP servers configured in these clients yet.${COLOR.reset}`);
|
|
449
|
+
console.log(`${COLOR.gray}Install gateway everywhere: fullcourtdefense install-mcp-gateway --clients all --shield-id ... --shield-key ... --mcp-command npm --mcp-args "run mcp"${COLOR.reset}`);
|
|
450
|
+
if (args.upload === 'true') {
|
|
451
|
+
const apiKey = args.apiKey || config.apiKey || process.env.FULLCOURTDEFENSE_API_KEY;
|
|
452
|
+
const apiUrl = args.apiUrl || config.apiUrl || DEFAULT_API_URL;
|
|
453
|
+
if (!apiKey) {
|
|
454
|
+
console.error(`\n${COLOR.red}--upload requires an API key.${COLOR.reset}`);
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
console.log('');
|
|
458
|
+
await upload(found, host, clientCoverage, apiUrl, apiKey, args.connectorName);
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
console.log(`${COLOR.gray}Run with --upload to push client coverage into AI Inventory.${COLOR.reset}`);
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const order = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
466
|
+
found.sort((a, b) => order[a.riskLevel] - order[b.riskLevel]);
|
|
493
467
|
console.log(`${COLOR.bold}${found.length} MCP server(s) discovered:${COLOR.reset}\n`);
|
|
494
468
|
for (const s of found) {
|
|
495
469
|
const col = riskColor(s.riskLevel);
|
|
496
470
|
const target = s.command ? `${s.command} ${(s.args || []).join(' ')}`.trim() : (s.url || 'unknown');
|
|
497
471
|
const proxyCol = s.proxyStatus === 'direct' ? COLOR.red : s.proxyStatus === 'proxied' || s.proxyStatus === 'fcd_gateway' ? COLOR.green : COLOR.yellow;
|
|
498
472
|
console.log(` ${col}${COLOR.bold}[${s.riskLevel.toUpperCase()}]${COLOR.reset} ${COLOR.bold}${s.serverName}${COLOR.reset} ${COLOR.gray}(${s.transport}${s.probeMode === 'deep' ? ', deep' : ''})${COLOR.reset} ${proxyCol}${proxyStatusLabel(s.proxyStatus)}${COLOR.reset}`);
|
|
473
|
+
if (s.serverDescription)
|
|
474
|
+
console.log(` ${COLOR.gray}about:${COLOR.reset} ${s.serverDescription}`);
|
|
499
475
|
console.log(` ${COLOR.gray}target:${COLOR.reset} ${target}`);
|
|
500
476
|
if (s.riskTags.length)
|
|
501
477
|
console.log(` ${COLOR.gray}tags:${COLOR.reset} ${s.riskTags.join(', ')}`);
|
|
@@ -527,3 +503,15 @@ async function discoverCommand(args, config) {
|
|
|
527
503
|
console.log(`${COLOR.gray}Add --schedule daily for automatic daily upload (MDM/fleet).${COLOR.reset}`);
|
|
528
504
|
}
|
|
529
505
|
}
|
|
506
|
+
/** Upload-only discover — used after install-mcp-gateway --upload. */
|
|
507
|
+
async function runDiscoverUpload(args, config) {
|
|
508
|
+
await discoverCommand({
|
|
509
|
+
type: 'mcp',
|
|
510
|
+
upload: 'true',
|
|
511
|
+
silent: args.silent ? 'true' : undefined,
|
|
512
|
+
apiKey: args.apiKey,
|
|
513
|
+
apiUrl: args.apiUrl,
|
|
514
|
+
userEmail: args.userEmail,
|
|
515
|
+
connectorName: args.connectorName,
|
|
516
|
+
}, config);
|
|
517
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ConfigPathCandidate {
|
|
2
|
+
path: string;
|
|
3
|
+
source: string;
|
|
4
|
+
clientKey: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ClaudeDesktopConfigCandidate {
|
|
7
|
+
file: string;
|
|
8
|
+
source: string;
|
|
9
|
+
exists: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function claudeDesktopMsixCandidates(): ClaudeDesktopConfigCandidate[];
|
|
12
|
+
export declare function claudeDesktopConfigCandidates(): ClaudeDesktopConfigCandidate[];
|
|
13
|
+
export declare function claudeDesktopLikelyInstalled(): boolean;
|
|
14
|
+
export declare function claudeDesktopInstallTargets(configPath?: string): ClaudeDesktopConfigCandidate[];
|
|
15
|
+
/** All MCP client config locations discover / install should check. */
|
|
16
|
+
export declare function candidateConfigPaths(cwd: string, extra?: string): ConfigPathCandidate[];
|
|
17
|
+
/** Include config paths for apps that exist on disk even when the MCP file is not created yet. */
|
|
18
|
+
export declare function discoverScanTargets(cwd: string, extra?: string): ConfigPathCandidate[];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.claudeDesktopMsixCandidates = claudeDesktopMsixCandidates;
|
|
37
|
+
exports.claudeDesktopConfigCandidates = claudeDesktopConfigCandidates;
|
|
38
|
+
exports.claudeDesktopLikelyInstalled = claudeDesktopLikelyInstalled;
|
|
39
|
+
exports.claudeDesktopInstallTargets = claudeDesktopInstallTargets;
|
|
40
|
+
exports.candidateConfigPaths = candidateConfigPaths;
|
|
41
|
+
exports.discoverScanTargets = discoverScanTargets;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
function claudeDesktopDocumentedPath() {
|
|
46
|
+
const home = os.homedir();
|
|
47
|
+
if (process.platform === 'win32') {
|
|
48
|
+
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
49
|
+
return path.join(appData, 'Claude', 'claude_desktop_config.json');
|
|
50
|
+
}
|
|
51
|
+
if (process.platform === 'darwin') {
|
|
52
|
+
return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
53
|
+
}
|
|
54
|
+
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
55
|
+
return path.join(xdg, 'Claude', 'claude_desktop_config.json');
|
|
56
|
+
}
|
|
57
|
+
function claudeDesktopMsixCandidates() {
|
|
58
|
+
if (process.platform !== 'win32')
|
|
59
|
+
return [];
|
|
60
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
61
|
+
const packagesDir = path.join(localAppData, 'Packages');
|
|
62
|
+
try {
|
|
63
|
+
return fs.readdirSync(packagesDir)
|
|
64
|
+
.filter(name => /^Claude_/i.test(name))
|
|
65
|
+
.map(name => ({
|
|
66
|
+
file: path.join(packagesDir, name, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json'),
|
|
67
|
+
source: 'Claude Desktop (win MSIX)',
|
|
68
|
+
exists: fs.existsSync(path.join(packagesDir, name, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json')),
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function claudeDesktopConfigCandidates() {
|
|
76
|
+
const documented = claudeDesktopDocumentedPath();
|
|
77
|
+
const normal = {
|
|
78
|
+
file: documented,
|
|
79
|
+
source: process.platform === 'win32'
|
|
80
|
+
? 'Claude Desktop (win)'
|
|
81
|
+
: process.platform === 'darwin'
|
|
82
|
+
? 'Claude Desktop (mac)'
|
|
83
|
+
: 'Claude Desktop (linux)',
|
|
84
|
+
exists: fs.existsSync(documented),
|
|
85
|
+
};
|
|
86
|
+
const seen = new Set();
|
|
87
|
+
return [...claudeDesktopMsixCandidates(), normal].filter(candidate => {
|
|
88
|
+
const key = path.resolve(candidate.file).toLowerCase();
|
|
89
|
+
if (seen.has(key))
|
|
90
|
+
return false;
|
|
91
|
+
seen.add(key);
|
|
92
|
+
return true;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function claudeDesktopLikelyInstalled() {
|
|
96
|
+
if (claudeDesktopConfigCandidates().some(candidate => candidate.exists))
|
|
97
|
+
return true;
|
|
98
|
+
if (claudeDesktopMsixCandidates().length > 0)
|
|
99
|
+
return true;
|
|
100
|
+
if (process.platform === 'darwin' && fs.existsSync('/Applications/Claude.app'))
|
|
101
|
+
return true;
|
|
102
|
+
if (process.platform === 'win32') {
|
|
103
|
+
const pf = process.env.ProgramFiles || 'C:\\Program Files';
|
|
104
|
+
if (fs.existsSync(path.join(pf, 'Claude', 'Claude.exe')))
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return fs.existsSync(path.dirname(claudeDesktopDocumentedPath()));
|
|
108
|
+
}
|
|
109
|
+
function claudeDesktopInstallTargets(configPath) {
|
|
110
|
+
if (configPath) {
|
|
111
|
+
const file = path.resolve(configPath);
|
|
112
|
+
return [{ file, source: 'Claude Desktop (custom)', exists: fs.existsSync(file) }];
|
|
113
|
+
}
|
|
114
|
+
const candidates = claudeDesktopConfigCandidates();
|
|
115
|
+
const existing = candidates.filter(candidate => candidate.exists);
|
|
116
|
+
if (existing.length > 0)
|
|
117
|
+
return existing;
|
|
118
|
+
const msix = candidates.find(candidate => candidate.source.includes('MSIX'));
|
|
119
|
+
if (msix)
|
|
120
|
+
return [msix];
|
|
121
|
+
return [candidates[candidates.length - 1] || {
|
|
122
|
+
file: claudeDesktopDocumentedPath(),
|
|
123
|
+
source: 'Claude Desktop',
|
|
124
|
+
exists: false,
|
|
125
|
+
}];
|
|
126
|
+
}
|
|
127
|
+
/** All MCP client config locations discover / install should check. */
|
|
128
|
+
function candidateConfigPaths(cwd, extra) {
|
|
129
|
+
const home = os.homedir();
|
|
130
|
+
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
131
|
+
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
132
|
+
const list = [
|
|
133
|
+
{ path: path.join(home, '.cursor', 'mcp.json'), source: 'Cursor (global)', clientKey: 'cursor' },
|
|
134
|
+
{ path: path.join(cwd, '.cursor', 'mcp.json'), source: 'Cursor (project)', clientKey: 'cursor' },
|
|
135
|
+
{ path: path.join(appData, 'Cursor', 'managed-mcp.json'), source: 'Cursor (managed, win)', clientKey: 'cursor' },
|
|
136
|
+
{ path: path.join(home, 'Library', 'Application Support', 'Cursor', 'managed-mcp.json'), source: 'Cursor (managed, mac)', clientKey: 'cursor' },
|
|
137
|
+
{ path: '/etc/cursor/managed-mcp.json', source: 'Cursor (managed, linux)', clientKey: 'cursor' },
|
|
138
|
+
{ path: path.join(home, '.claude.json'), source: 'Claude Code', clientKey: 'claude_code' },
|
|
139
|
+
{ path: path.join(home, '.claude', 'settings.json'), source: 'Claude Code (global)', clientKey: 'claude_code' },
|
|
140
|
+
{ path: path.join(cwd, '.claude', 'settings.json'), source: 'Claude Code (project)', clientKey: 'claude_code' },
|
|
141
|
+
{ path: path.join(cwd, '.mcp.json'), source: 'Claude Code (.mcp.json project)', clientKey: 'claude_code' },
|
|
142
|
+
{ path: path.join(home, '.gemini', 'settings.json'), source: 'Gemini CLI', clientKey: 'gemini_cli' },
|
|
143
|
+
{ path: path.join(xdg, 'gemini', 'settings.json'), source: 'Gemini CLI (xdg)', clientKey: 'gemini_cli' },
|
|
144
|
+
{ path: path.join(home, '.codex', 'mcp.json'), source: 'Codex', clientKey: 'codex' },
|
|
145
|
+
{ path: path.join(xdg, 'codex', 'mcp.json'), source: 'Codex (xdg)', clientKey: 'codex' },
|
|
146
|
+
{ path: path.join(cwd, '.vscode', 'mcp.json'), source: 'VS Code (project)', clientKey: 'vscode' },
|
|
147
|
+
{ path: path.join(appData, 'Code', 'User', 'settings.json'), source: 'VS Code (user, win)', clientKey: 'vscode' },
|
|
148
|
+
{ path: path.join(home, 'Library', 'Application Support', 'Code', 'User', 'settings.json'), source: 'VS Code (user, mac)', clientKey: 'vscode' },
|
|
149
|
+
{ path: path.join(xdg, 'Code', 'User', 'settings.json'), source: 'VS Code (user, linux)', clientKey: 'vscode' },
|
|
150
|
+
{ path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), source: 'Windsurf', clientKey: 'windsurf' },
|
|
151
|
+
{ path: path.join(home, '.kiro', 'mcp.json'), source: 'Kiro', clientKey: 'kiro' },
|
|
152
|
+
{ path: path.join(cwd, '.mcp.json'), source: 'Repo (.mcp.json)', clientKey: 'other' },
|
|
153
|
+
{ path: path.join(cwd, 'mcp.json'), source: 'Repo (mcp.json)', clientKey: 'other' },
|
|
154
|
+
];
|
|
155
|
+
for (const candidate of claudeDesktopConfigCandidates()) {
|
|
156
|
+
list.push({ path: candidate.file, source: candidate.source, clientKey: 'claude_desktop' });
|
|
157
|
+
}
|
|
158
|
+
if (process.platform === 'win32') {
|
|
159
|
+
list.push({
|
|
160
|
+
path: path.join(process.env.ProgramData || 'C:\\ProgramData', 'Cursor', 'managed-mcp.json'),
|
|
161
|
+
source: 'Cursor (managed, program data)',
|
|
162
|
+
clientKey: 'cursor',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (extra) {
|
|
166
|
+
list.push({ path: path.resolve(extra), source: 'Custom path', clientKey: 'other' });
|
|
167
|
+
}
|
|
168
|
+
const seen = new Set();
|
|
169
|
+
return list.filter(item => {
|
|
170
|
+
const key = path.resolve(item.path).toLowerCase();
|
|
171
|
+
if (seen.has(key))
|
|
172
|
+
return false;
|
|
173
|
+
seen.add(key);
|
|
174
|
+
return true;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/** Include config paths for apps that exist on disk even when the MCP file is not created yet. */
|
|
178
|
+
function discoverScanTargets(cwd, extra) {
|
|
179
|
+
const candidates = candidateConfigPaths(cwd, extra);
|
|
180
|
+
const existing = candidates.filter(item => fs.existsSync(item.path));
|
|
181
|
+
const implicit = [];
|
|
182
|
+
if (claudeDesktopLikelyInstalled()) {
|
|
183
|
+
for (const target of claudeDesktopInstallTargets()) {
|
|
184
|
+
if (!existing.some(item => path.resolve(item.path).toLowerCase() === path.resolve(target.file).toLowerCase())) {
|
|
185
|
+
implicit.push({ path: target.file, source: target.source, clientKey: 'claude_desktop' });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const cursorGlobal = path.join(os.homedir(), '.cursor', 'mcp.json');
|
|
190
|
+
if (fs.existsSync(path.join(os.homedir(), '.cursor')) && !existing.some(item => path.resolve(item.path).toLowerCase() === cursorGlobal.toLowerCase())) {
|
|
191
|
+
implicit.push({ path: cursorGlobal, source: 'Cursor (global)', clientKey: 'cursor' });
|
|
192
|
+
}
|
|
193
|
+
return [...existing, ...implicit];
|
|
194
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface KnownMcpServerInfo {
|
|
2
|
+
title: string;
|
|
3
|
+
description: string;
|
|
4
|
+
category: 'browser' | 'filesystem' | 'database' | 'gateway' | 'other';
|
|
5
|
+
recommendGateway: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function knownMcpServerInfo(serverName: string, command?: string): KnownMcpServerInfo | undefined;
|
|
8
|
+
export declare function directServerFixHint(serverName: string): string | undefined;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.knownMcpServerInfo = knownMcpServerInfo;
|
|
4
|
+
exports.directServerFixHint = directServerFixHint;
|
|
5
|
+
const KNOWN = [
|
|
6
|
+
{
|
|
7
|
+
match: /playwright/i,
|
|
8
|
+
info: {
|
|
9
|
+
title: 'Playwright',
|
|
10
|
+
description: 'Browser automation — AI can open sites, click, and act as the logged-in user.',
|
|
11
|
+
category: 'browser',
|
|
12
|
+
recommendGateway: true,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
match: /puppeteer|selenium|browser/i,
|
|
17
|
+
info: {
|
|
18
|
+
title: 'Browser automation',
|
|
19
|
+
description: 'Controls a web browser on the developer machine.',
|
|
20
|
+
category: 'browser',
|
|
21
|
+
recommendGateway: true,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
match: /filesystem|server-filesystem/i,
|
|
26
|
+
info: {
|
|
27
|
+
title: 'Filesystem',
|
|
28
|
+
description: 'Reads and writes local files — high exfiltration risk.',
|
|
29
|
+
category: 'filesystem',
|
|
30
|
+
recommendGateway: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
match: /postgres|mysql|sqlite|database|supabase/i,
|
|
35
|
+
info: {
|
|
36
|
+
title: 'Database',
|
|
37
|
+
description: 'Queries databases directly from the AI client.',
|
|
38
|
+
category: 'database',
|
|
39
|
+
recommendGateway: true,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
match: /agentguard-gateway|fullcourtdefense-gateway|fcd-gateway/i,
|
|
44
|
+
info: {
|
|
45
|
+
title: 'FCD MCP gateway',
|
|
46
|
+
description: 'Full Court Defense proxy — tool calls checked by Shield and Action Policies.',
|
|
47
|
+
category: 'gateway',
|
|
48
|
+
recommendGateway: false,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
function knownMcpServerInfo(serverName, command) {
|
|
53
|
+
const hay = `${serverName} ${command || ''}`;
|
|
54
|
+
for (const entry of KNOWN) {
|
|
55
|
+
if (entry.match.test(hay))
|
|
56
|
+
return entry.info;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
function directServerFixHint(serverName) {
|
|
61
|
+
const info = knownMcpServerInfo(serverName);
|
|
62
|
+
if (!info?.recommendGateway)
|
|
63
|
+
return undefined;
|
|
64
|
+
if (/playwright/i.test(serverName)) {
|
|
65
|
+
return 'Remove Playwright from mcp.json or route it through: fullcourtdefense install-mcp-gateway --clients all --mcp-command ...';
|
|
66
|
+
}
|
|
67
|
+
return 'Route through FCD gateway: fullcourtdefense install-mcp-gateway --clients all --mcp-command npm --mcp-args "run mcp"';
|
|
68
|
+
}
|
|
@@ -34,6 +34,8 @@ export interface InstallMcpGatewayArgs extends McpGatewayArgs {
|
|
|
34
34
|
cursorProject?: string;
|
|
35
35
|
claudeCodeScope?: string;
|
|
36
36
|
serverName?: string;
|
|
37
|
+
upload?: string;
|
|
38
|
+
apiKey?: string;
|
|
37
39
|
}
|
|
38
40
|
export declare function mcpGatewayCommand(args: McpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
39
41
|
export declare function installCursorMcpGatewayCommand(args: InstallCursorMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
@@ -42,6 +42,8 @@ const fs = __importStar(require("fs"));
|
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const child_process_1 = require("child_process");
|
|
45
|
+
const discoverPaths_1 = require("./discoverPaths");
|
|
46
|
+
const discover_1 = require("./discover");
|
|
45
47
|
const DEFAULT_API_URL = 'https://api.fullcourtdefense.ai';
|
|
46
48
|
const MANAGED_SERVER_NAME = 'agentguard-gateway';
|
|
47
49
|
function parseArgsList(value) {
|
|
@@ -672,83 +674,6 @@ function writeMcpServerConfig(file, serverName, nodeExe, commandArgs) {
|
|
|
672
674
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
673
675
|
fs.writeFileSync(file, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
674
676
|
}
|
|
675
|
-
function dedupePaths(candidates) {
|
|
676
|
-
const seen = new Set();
|
|
677
|
-
return candidates.filter(candidate => {
|
|
678
|
-
const key = path.resolve(candidate.file).toLowerCase();
|
|
679
|
-
if (seen.has(key))
|
|
680
|
-
return false;
|
|
681
|
-
seen.add(key);
|
|
682
|
-
return true;
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
function claudeDesktopMcpPath() {
|
|
686
|
-
const home = os.homedir();
|
|
687
|
-
if (process.platform === 'win32') {
|
|
688
|
-
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
689
|
-
return path.join(appData, 'Claude', 'claude_desktop_config.json');
|
|
690
|
-
}
|
|
691
|
-
if (process.platform === 'darwin') {
|
|
692
|
-
return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
693
|
-
}
|
|
694
|
-
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
695
|
-
return path.join(xdg, 'Claude', 'claude_desktop_config.json');
|
|
696
|
-
}
|
|
697
|
-
function claudeDesktopMsixCandidates() {
|
|
698
|
-
if (process.platform !== 'win32')
|
|
699
|
-
return [];
|
|
700
|
-
const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
701
|
-
const packagesDir = path.join(localAppData, 'Packages');
|
|
702
|
-
let packageNames = [];
|
|
703
|
-
try {
|
|
704
|
-
packageNames = fs.readdirSync(packagesDir)
|
|
705
|
-
.filter(name => /^Claude_/i.test(name));
|
|
706
|
-
}
|
|
707
|
-
catch {
|
|
708
|
-
return [];
|
|
709
|
-
}
|
|
710
|
-
return packageNames.map(name => {
|
|
711
|
-
const file = path.join(packagesDir, name, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
712
|
-
return {
|
|
713
|
-
file,
|
|
714
|
-
source: 'Claude Desktop (Microsoft Store/MSIX)',
|
|
715
|
-
exists: fs.existsSync(file),
|
|
716
|
-
};
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
function claudeDesktopMcpCandidates() {
|
|
720
|
-
const documented = claudeDesktopMcpPath();
|
|
721
|
-
const normal = {
|
|
722
|
-
file: documented,
|
|
723
|
-
source: process.platform === 'win32'
|
|
724
|
-
? 'Claude Desktop (Windows documented AppData)'
|
|
725
|
-
: process.platform === 'darwin'
|
|
726
|
-
? 'Claude Desktop (macOS)'
|
|
727
|
-
: 'Claude Desktop (Linux/XDG)',
|
|
728
|
-
exists: fs.existsSync(documented),
|
|
729
|
-
};
|
|
730
|
-
// Store/MSIX Claude Desktop reads a virtualized AppData path. Prefer it when present.
|
|
731
|
-
return dedupePaths([
|
|
732
|
-
...claudeDesktopMsixCandidates(),
|
|
733
|
-
normal,
|
|
734
|
-
]);
|
|
735
|
-
}
|
|
736
|
-
function claudeDesktopInstallTargets(configPath) {
|
|
737
|
-
if (configPath) {
|
|
738
|
-
const file = path.resolve(configPath);
|
|
739
|
-
return [{ file, source: 'Claude Desktop (custom config path)', exists: fs.existsSync(file) }];
|
|
740
|
-
}
|
|
741
|
-
const candidates = claudeDesktopMcpCandidates();
|
|
742
|
-
const existing = candidates.filter(candidate => candidate.exists);
|
|
743
|
-
if (existing.length > 0)
|
|
744
|
-
return existing;
|
|
745
|
-
const msix = candidates.find(candidate => candidate.source.includes('MSIX'));
|
|
746
|
-
return [msix || candidates[candidates.length - 1] || {
|
|
747
|
-
file: claudeDesktopMcpPath(),
|
|
748
|
-
source: 'Claude Desktop',
|
|
749
|
-
exists: false,
|
|
750
|
-
}];
|
|
751
|
-
}
|
|
752
677
|
function normalizeClaudeCodeScope(scope) {
|
|
753
678
|
if (scope === 'project' || scope === 'user' || scope === 'local')
|
|
754
679
|
return scope;
|
|
@@ -791,9 +716,8 @@ function detectInstallClients(cursorProject) {
|
|
|
791
716
|
clients.push('cursor');
|
|
792
717
|
if (commandAvailable('claude'))
|
|
793
718
|
clients.push('claude-code');
|
|
794
|
-
if (
|
|
719
|
+
if ((0, discoverPaths_1.claudeDesktopLikelyInstalled)())
|
|
795
720
|
clients.push('claude-desktop');
|
|
796
|
-
}
|
|
797
721
|
return clients;
|
|
798
722
|
}
|
|
799
723
|
function clientInstallArgs(args) {
|
|
@@ -841,7 +765,9 @@ async function installCursorMcpGatewayCommand(args, config) {
|
|
|
841
765
|
async function installMcpGatewayCommand(args, config) {
|
|
842
766
|
const cursorProject = args.cursorProject === 'true';
|
|
843
767
|
const selection = parseClientList(args.clients);
|
|
844
|
-
const clients = selection === 'auto'
|
|
768
|
+
const clients = selection === 'auto'
|
|
769
|
+
? detectInstallClients(cursorProject)
|
|
770
|
+
: selection;
|
|
845
771
|
if (clients.length === 0) {
|
|
846
772
|
throw new Error('No supported MCP clients detected. Re-run with --clients all or a comma list such as cursor,claude-desktop.');
|
|
847
773
|
}
|
|
@@ -876,6 +802,19 @@ async function installMcpGatewayCommand(args, config) {
|
|
|
876
802
|
console.log(`Runtime identity: ${detectedDeveloperName(args)}`);
|
|
877
803
|
console.log('Restart/reload each client to use the protected tools.');
|
|
878
804
|
console.log('Smoke test prompt: "Use lookup_customer to look up customer C-1001."');
|
|
805
|
+
if (args.upload === 'true') {
|
|
806
|
+
console.log('');
|
|
807
|
+
console.log('Uploading desktop inventory to AI Inventory…');
|
|
808
|
+
await (0, discover_1.runDiscoverUpload)({
|
|
809
|
+
apiKey: args.apiKey || config.apiKey,
|
|
810
|
+
apiUrl: args.apiUrl,
|
|
811
|
+
userEmail: args.developerName,
|
|
812
|
+
silent: false,
|
|
813
|
+
}, config);
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
console.log('Run with --upload to refresh AI Inventory / Desktop MCP coverage in the app.');
|
|
817
|
+
}
|
|
879
818
|
}
|
|
880
819
|
async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
881
820
|
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
@@ -940,7 +879,7 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
940
879
|
agentNamePrefix: 'claude-desktop',
|
|
941
880
|
userObjective: 'Developer requested this action from Claude Desktop.',
|
|
942
881
|
});
|
|
943
|
-
const targets = claudeDesktopInstallTargets(args.configPath);
|
|
882
|
+
const targets = (0, discoverPaths_1.claudeDesktopInstallTargets)(args.configPath);
|
|
944
883
|
const nodeExe = process.execPath;
|
|
945
884
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
946
885
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
package/dist/index.js
CHANGED
|
@@ -208,7 +208,7 @@ function printHelp() {
|
|
|
208
208
|
$ fullcourtdefense install-cursor-hook --project true # this repo only
|
|
209
209
|
$ fullcourtdefense uninstall-cursor-hook
|
|
210
210
|
$ fullcourtdefense install-mcp-gateway --clients auto --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
|
|
211
|
-
$ fullcourtdefense install-mcp-gateway --clients all --mcp-command
|
|
211
|
+
$ fullcourtdefense install-mcp-gateway --clients all --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp" --upload --api-key <key>
|
|
212
212
|
$ fullcourtdefense install-cursor-mcp-gateway --project true --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
|
|
213
213
|
$ fullcourtdefense install-claude-code-mcp-gateway --scope local --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
|
|
214
214
|
$ fullcourtdefense install-claude-code-mcp-gateway --scope project --shield-id <id> --mcp-command npm --mcp-args "run mcp"
|
|
@@ -406,6 +406,8 @@ async function main() {
|
|
|
406
406
|
approvalPollMs: flags['approval-poll-ms'],
|
|
407
407
|
scanResponse: flags['scan-response'],
|
|
408
408
|
failClosed: flags['fail-closed'],
|
|
409
|
+
upload: flags.upload,
|
|
410
|
+
apiKey: flags['api-key'],
|
|
409
411
|
};
|
|
410
412
|
await (0, mcpGateway_1.installMcpGatewayCommand)(args, config);
|
|
411
413
|
break;
|