fullcourtdefense-cli 1.14.10 → 1.14.12
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.js +12 -0
- package/dist/commands/mcpGateway.d.ts +16 -0
- package/dist/commands/mcpGateway.js +173 -9
- package/dist/version.json +1 -1
- package/package.json +2 -1
|
@@ -43,6 +43,7 @@ const child_process_1 = require("child_process");
|
|
|
43
43
|
const config_1 = require("../config");
|
|
44
44
|
const discoverProxy_1 = require("./discoverProxy");
|
|
45
45
|
const discoverSchedule_1 = require("./discoverSchedule");
|
|
46
|
+
const mcpGateway_1 = require("./mcpGateway");
|
|
46
47
|
const discoverPaths_1 = require("./discoverPaths");
|
|
47
48
|
const knownMcpServers_1 = require("./knownMcpServers");
|
|
48
49
|
const discoverAgentFiles_1 = require("./discoverAgentFiles");
|
|
@@ -770,6 +771,17 @@ async function discoverCommand(args, config) {
|
|
|
770
771
|
let clientCoverage = [];
|
|
771
772
|
if (runMcp) {
|
|
772
773
|
const candidates = candidateConfigPaths(cwd, args.extraPath);
|
|
774
|
+
// Self-heal BEFORE parsing: configs written by old CLI versions can carry a stale
|
|
775
|
+
// generic `--agent-name` that breaks dashboard policy matching. Fix them in place so
|
|
776
|
+
// every discovery run converges the fleet to the per-server identity contract.
|
|
777
|
+
const healedIdentities = (0, mcpGateway_1.healGatewayAgentIdentities)(candidates.map(c => c.path).filter(p => fs.existsSync(p)));
|
|
778
|
+
if (healedIdentities.length > 0 && !silent) {
|
|
779
|
+
console.log(`${COLOR.yellow}Healed ${healedIdentities.length} stale gateway identit${healedIdentities.length === 1 ? 'y' : 'ies'} (old CLI defaults):${COLOR.reset}`);
|
|
780
|
+
for (const heal of healedIdentities) {
|
|
781
|
+
console.log(` ${COLOR.dim}•${COLOR.reset} ${heal.server}: ${heal.from} → ${heal.to} (${heal.file})`);
|
|
782
|
+
}
|
|
783
|
+
console.log(`${COLOR.gray}Restart the affected AI clients to pick up the corrected identity.${COLOR.reset}\n`);
|
|
784
|
+
}
|
|
773
785
|
for (const c of candidates) {
|
|
774
786
|
scanned.push({ path: c.path, source: c.source });
|
|
775
787
|
if (fs.existsSync(c.path)) {
|
|
@@ -63,6 +63,22 @@ export interface ProtectAllArgs extends McpGatewayArgs {
|
|
|
63
63
|
config?: string;
|
|
64
64
|
clients?: string;
|
|
65
65
|
}
|
|
66
|
+
export interface GatewayIdentityHealResult {
|
|
67
|
+
file: string;
|
|
68
|
+
server: string;
|
|
69
|
+
from: string;
|
|
70
|
+
to: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* SELF-HEAL for the policy-identity contract. Scans MCP client config files (JSON and Codex
|
|
74
|
+
* TOML) for FCD-gateway-wrapped servers whose baked-in `--agent-name` is a stale generic
|
|
75
|
+
* `<developer>-<client>` default, and rewrites it to `<developer>-<server>` so dashboard
|
|
76
|
+
* policies targeting the MCP server (`agentName contains <server>`) match at runtime.
|
|
77
|
+
*
|
|
78
|
+
* Runs automatically from `discover` and `protect-all`, so a customer machine with configs
|
|
79
|
+
* written by an old CLI version converges to the correct identity without manual edits.
|
|
80
|
+
*/
|
|
81
|
+
export declare function healGatewayAgentIdentities(files: string[], dryRun?: boolean): GatewayIdentityHealResult[];
|
|
66
82
|
export declare function protectAllCommand(args: ProtectAllArgs, config: BotGuardConfig): Promise<void>;
|
|
67
83
|
export declare function unprotectAllCommand(args: ProtectAllArgs, config: BotGuardConfig): Promise<void>;
|
|
68
84
|
export declare function installClaudeCodeMcpGatewayCommand(args: InstallClaudeCodeMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
@@ -37,6 +37,7 @@ exports.ALL_GATEWAY_CLIENTS = void 0;
|
|
|
37
37
|
exports.mcpGatewayCommand = mcpGatewayCommand;
|
|
38
38
|
exports.installCursorMcpGatewayCommand = installCursorMcpGatewayCommand;
|
|
39
39
|
exports.installMcpGatewayCommand = installMcpGatewayCommand;
|
|
40
|
+
exports.healGatewayAgentIdentities = healGatewayAgentIdentities;
|
|
40
41
|
exports.protectAllCommand = protectAllCommand;
|
|
41
42
|
exports.unprotectAllCommand = unprotectAllCommand;
|
|
42
43
|
exports.installClaudeCodeMcpGatewayCommand = installClaudeCodeMcpGatewayCommand;
|
|
@@ -967,7 +968,7 @@ function clientInstallArgs(args) {
|
|
|
967
968
|
};
|
|
968
969
|
}
|
|
969
970
|
async function installCursorMcpGatewayCommand(args, config) {
|
|
970
|
-
const
|
|
971
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
971
972
|
agentClient: 'cursor',
|
|
972
973
|
agentNamePrefix: 'cursor',
|
|
973
974
|
userObjective: 'Developer requested this action from Cursor.',
|
|
@@ -976,6 +977,7 @@ async function installCursorMcpGatewayCommand(args, config) {
|
|
|
976
977
|
const file = cursorMcpPath(projectScope);
|
|
977
978
|
const nodeExe = process.execPath;
|
|
978
979
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
980
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
979
981
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
980
982
|
if (!downstreamCommand)
|
|
981
983
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
@@ -1141,6 +1143,147 @@ function backupFile(file) {
|
|
|
1141
1143
|
function perServerAgentName(developerName, _agentClient, serverName) {
|
|
1142
1144
|
return `${safeIdentityPart(developerName)}-${safeIdentityPart(serverName)}`;
|
|
1143
1145
|
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Generic `<developer>-<client>` identities that pre-1.14.11 per-server installs baked into
|
|
1148
|
+
* configs. These are the ONLY values self-heal is allowed to rewrite — a custom --agent-name
|
|
1149
|
+
* an admin chose on purpose is never touched.
|
|
1150
|
+
*/
|
|
1151
|
+
const GENERIC_CLIENT_AGENT_SUFFIXES = ['cursor', 'claude-code', 'claude-desktop', 'codex', 'gemini', 'gemini-cli', 'windsurf', 'vscode'];
|
|
1152
|
+
/**
|
|
1153
|
+
* Rewrite a wrapped entry's `--agent-name` to the per-server contract (`<developer>-<server>`)
|
|
1154
|
+
* when it still carries a known-buggy generic client default. Mutates `args` in place.
|
|
1155
|
+
*/
|
|
1156
|
+
function healAgentNameInArgs(args, serverName) {
|
|
1157
|
+
const nameIdx = args.indexOf('--agent-name');
|
|
1158
|
+
if (nameIdx === -1 || nameIdx + 1 >= args.length)
|
|
1159
|
+
return { changed: false };
|
|
1160
|
+
const current = String(args[nameIdx + 1]);
|
|
1161
|
+
const devIdx = args.indexOf('--developer-name');
|
|
1162
|
+
const developerName = devIdx !== -1 && args[devIdx + 1] ? String(args[devIdx + 1]) : machineScopedUser();
|
|
1163
|
+
const devPart = safeIdentityPart(developerName);
|
|
1164
|
+
const expected = `${devPart}-${safeIdentityPart(serverName)}`;
|
|
1165
|
+
if (current === expected)
|
|
1166
|
+
return { changed: false };
|
|
1167
|
+
const isGenericDefault = GENERIC_CLIENT_AGENT_SUFFIXES.some(suffix => current === `${devPart}-${suffix}`);
|
|
1168
|
+
if (!isGenericDefault)
|
|
1169
|
+
return { changed: false };
|
|
1170
|
+
args[nameIdx + 1] = expected;
|
|
1171
|
+
return { changed: true, from: current, to: expected };
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* SELF-HEAL for the policy-identity contract. Scans MCP client config files (JSON and Codex
|
|
1175
|
+
* TOML) for FCD-gateway-wrapped servers whose baked-in `--agent-name` is a stale generic
|
|
1176
|
+
* `<developer>-<client>` default, and rewrites it to `<developer>-<server>` so dashboard
|
|
1177
|
+
* policies targeting the MCP server (`agentName contains <server>`) match at runtime.
|
|
1178
|
+
*
|
|
1179
|
+
* Runs automatically from `discover` and `protect-all`, so a customer machine with configs
|
|
1180
|
+
* written by an old CLI version converges to the correct identity without manual edits.
|
|
1181
|
+
*/
|
|
1182
|
+
function healGatewayAgentIdentities(files, dryRun = false) {
|
|
1183
|
+
const healed = [];
|
|
1184
|
+
const seen = new Set();
|
|
1185
|
+
for (const file of files) {
|
|
1186
|
+
let resolvedKey;
|
|
1187
|
+
try {
|
|
1188
|
+
resolvedKey = path.resolve(file).toLowerCase();
|
|
1189
|
+
}
|
|
1190
|
+
catch {
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
if (seen.has(resolvedKey))
|
|
1194
|
+
continue;
|
|
1195
|
+
seen.add(resolvedKey);
|
|
1196
|
+
if (/\.toml$/i.test(file)) {
|
|
1197
|
+
healed.push(...healCodexTomlAgentIdentities(file, dryRun));
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
let raw;
|
|
1201
|
+
try {
|
|
1202
|
+
raw = fs.readFileSync(file, 'utf8');
|
|
1203
|
+
}
|
|
1204
|
+
catch {
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
let json;
|
|
1208
|
+
try {
|
|
1209
|
+
json = JSON.parse(raw);
|
|
1210
|
+
}
|
|
1211
|
+
catch {
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
const maps = collectJsonServerMaps(json);
|
|
1215
|
+
let changed = false;
|
|
1216
|
+
for (const map of maps) {
|
|
1217
|
+
for (const [name, entry] of Object.entries(map)) {
|
|
1218
|
+
if (name === MANAGED_SERVER_NAME || !isGatewayWrappedEntry(entry))
|
|
1219
|
+
continue;
|
|
1220
|
+
const args = Array.isArray(entry.args) ? entry.args.map(String) : [];
|
|
1221
|
+
const result = healAgentNameInArgs(args, name);
|
|
1222
|
+
if (result.changed) {
|
|
1223
|
+
entry.args = args;
|
|
1224
|
+
healed.push({ file, server: name, from: result.from, to: result.to });
|
|
1225
|
+
changed = true;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
if (changed && !dryRun) {
|
|
1230
|
+
backupFile(file);
|
|
1231
|
+
fs.writeFileSync(file, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return healed;
|
|
1235
|
+
}
|
|
1236
|
+
/** Codex config.toml variant of the identity self-heal (args are written as a JSON-style array). */
|
|
1237
|
+
function healCodexTomlAgentIdentities(file, dryRun) {
|
|
1238
|
+
let raw;
|
|
1239
|
+
try {
|
|
1240
|
+
raw = fs.readFileSync(file, 'utf8');
|
|
1241
|
+
}
|
|
1242
|
+
catch {
|
|
1243
|
+
return [];
|
|
1244
|
+
}
|
|
1245
|
+
const healed = [];
|
|
1246
|
+
const updated = raw.replace(/\[mcp_servers\.("?)([^\]"]+)\1\]([\s\S]*?)(?=\n\[|$)/g, (section, _q, serverName, body) => {
|
|
1247
|
+
if (serverName === MANAGED_SERVER_NAME || !body.includes('mcp-gateway'))
|
|
1248
|
+
return section;
|
|
1249
|
+
const newBody = body.replace(/("--agent-name",\s*")([^"]+)(")/, (m, pre, current, post) => {
|
|
1250
|
+
const devMatch = body.match(/"--developer-name",\s*"([^"]+)"/);
|
|
1251
|
+
const devPart = safeIdentityPart(devMatch?.[1] || machineScopedUser());
|
|
1252
|
+
const expected = `${devPart}-${safeIdentityPart(serverName)}`;
|
|
1253
|
+
if (current === expected)
|
|
1254
|
+
return m;
|
|
1255
|
+
const isGenericDefault = GENERIC_CLIENT_AGENT_SUFFIXES.some(suffix => current === `${devPart}-${suffix}`);
|
|
1256
|
+
if (!isGenericDefault)
|
|
1257
|
+
return m;
|
|
1258
|
+
healed.push({ file, server: serverName, from: current, to: expected });
|
|
1259
|
+
return `${pre}${expected}${post}`;
|
|
1260
|
+
});
|
|
1261
|
+
return section.replace(body, newBody);
|
|
1262
|
+
});
|
|
1263
|
+
if (healed.length > 0 && !dryRun) {
|
|
1264
|
+
backupFile(file);
|
|
1265
|
+
fs.writeFileSync(file, updated, 'utf8');
|
|
1266
|
+
}
|
|
1267
|
+
return healed;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Per-server installs (`install-*-mcp-gateway --server-name foo`) must report the SAME
|
|
1271
|
+
* runtime-agnostic agent identity that protect-all/auto-protect produce (`<developer>-<server>`).
|
|
1272
|
+
* Otherwise the gateway reports a generic `<developer>-<client>` name and a dashboard policy
|
|
1273
|
+
* targeting the MCP server (agentName contains <server>) silently never matches — the exact
|
|
1274
|
+
* bug where a block policy on `patria-bank-finance` did not fire because the Claude Desktop
|
|
1275
|
+
* entry reported `boazl-claude-desktop`. An explicit --agent-name / FCD_AGENT_NAME still wins.
|
|
1276
|
+
*/
|
|
1277
|
+
function applyPerServerAgentName(gatewayConfig, args, serverName) {
|
|
1278
|
+
if (args.agentName || process.env.FCD_AGENT_NAME)
|
|
1279
|
+
return gatewayConfig;
|
|
1280
|
+
if (!serverName || serverName === MANAGED_SERVER_NAME)
|
|
1281
|
+
return gatewayConfig;
|
|
1282
|
+
return {
|
|
1283
|
+
...gatewayConfig,
|
|
1284
|
+
agentName: perServerAgentName(gatewayConfig.developerName, gatewayConfig.agentClient, serverName),
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1144
1287
|
function wrapJsonConfigFile(file, gatewayConfig, agentClient, dryRun) {
|
|
1145
1288
|
const stats = newWrapStats();
|
|
1146
1289
|
let json;
|
|
@@ -1411,6 +1554,15 @@ async function protectAllCommand(args, config) {
|
|
|
1411
1554
|
return;
|
|
1412
1555
|
}
|
|
1413
1556
|
console.log(`\x1b[2mAuto-searching every known MCP client on this machine — ${files.length} config file(s) found.\x1b[0m\n`);
|
|
1557
|
+
// Converge stale generic gateway identities (old CLI defaults) to the per-server
|
|
1558
|
+
// contract before wrapping, so policy matching works on already-wrapped entries too.
|
|
1559
|
+
const healedIdentities = healGatewayAgentIdentities(files.map(f => f.path), dryRun);
|
|
1560
|
+
if (healedIdentities.length > 0) {
|
|
1561
|
+
console.log(`\x1b[32m✓ ${dryRun ? 'would fix' : 'fixed'} ${healedIdentities.length} stale gateway identit${healedIdentities.length === 1 ? 'y' : 'ies'}:\x1b[0m`);
|
|
1562
|
+
for (const heal of healedIdentities)
|
|
1563
|
+
console.log(` \x1b[2m•\x1b[0m ${heal.server}: ${heal.from} → ${heal.to}`);
|
|
1564
|
+
console.log('');
|
|
1565
|
+
}
|
|
1414
1566
|
let totalWrapped = 0;
|
|
1415
1567
|
let totalHealed = 0;
|
|
1416
1568
|
let totalManaged = 0;
|
|
@@ -1495,7 +1647,7 @@ async function unprotectAllCommand(args, config) {
|
|
|
1495
1647
|
console.log(`\n${dryRun ? 'Would restore' : 'Restored'} ${total} server(s) to their original commands. Restart each client.`);
|
|
1496
1648
|
}
|
|
1497
1649
|
async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
1498
|
-
const
|
|
1650
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
1499
1651
|
agentClient: 'claude-code',
|
|
1500
1652
|
agentNamePrefix: 'claude-code',
|
|
1501
1653
|
userObjective: 'Developer requested this action from Claude Code.',
|
|
@@ -1503,6 +1655,7 @@ async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
|
1503
1655
|
const scope = normalizeClaudeCodeScope(args.scope);
|
|
1504
1656
|
const nodeExe = process.execPath;
|
|
1505
1657
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1658
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
1506
1659
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1507
1660
|
if (!downstreamCommand)
|
|
1508
1661
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
@@ -1513,7 +1666,13 @@ async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
|
1513
1666
|
includeShieldKey,
|
|
1514
1667
|
});
|
|
1515
1668
|
const claudeArgs = ['mcp', 'add', '--scope', scope, '--transport', 'stdio', serverName, '--', nodeExe, ...commandArgs];
|
|
1516
|
-
|
|
1669
|
+
// On Windows `claude` is a .cmd shim, so spawnSync needs shell:true — but with shell:true
|
|
1670
|
+
// Node does NOT quote args. Unquoted args with spaces/JSON (e.g. --mcp-args '["C:\\..."]',
|
|
1671
|
+
// --user-objective 'Developer requested...') get word-split by cmd.exe and written mangled
|
|
1672
|
+
// into the config. Quote them ourselves before handing the line to the shell.
|
|
1673
|
+
const shellQuote = (value) => (/[\s"]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value);
|
|
1674
|
+
const spawnClaudeArgs = process.platform === 'win32' ? claudeArgs.map(shellQuote) : claudeArgs;
|
|
1675
|
+
let result = (0, child_process_1.spawnSync)('claude', spawnClaudeArgs, {
|
|
1517
1676
|
stdio: 'inherit',
|
|
1518
1677
|
shell: process.platform === 'win32',
|
|
1519
1678
|
});
|
|
@@ -1523,7 +1682,7 @@ async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
|
1523
1682
|
shell: process.platform === 'win32',
|
|
1524
1683
|
});
|
|
1525
1684
|
if (removeResult.status === 0) {
|
|
1526
|
-
result = (0, child_process_1.spawnSync)('claude',
|
|
1685
|
+
result = (0, child_process_1.spawnSync)('claude', spawnClaudeArgs, {
|
|
1527
1686
|
stdio: 'inherit',
|
|
1528
1687
|
shell: process.platform === 'win32',
|
|
1529
1688
|
});
|
|
@@ -1555,7 +1714,7 @@ async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
|
1555
1714
|
throw new Error('Claude Code CLI was not available. Install Claude Code or rerun with --scope project to write .mcp.json directly.');
|
|
1556
1715
|
}
|
|
1557
1716
|
async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
1558
|
-
const
|
|
1717
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
1559
1718
|
agentClient: 'claude-desktop',
|
|
1560
1719
|
agentNamePrefix: 'claude-desktop',
|
|
1561
1720
|
userObjective: 'Developer requested this action from Claude Desktop.',
|
|
@@ -1563,6 +1722,7 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
1563
1722
|
const targets = (0, discoverPaths_1.claudeDesktopInstallTargets)(args.configPath);
|
|
1564
1723
|
const nodeExe = process.execPath;
|
|
1565
1724
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1725
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
1566
1726
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1567
1727
|
if (!downstreamCommand)
|
|
1568
1728
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
@@ -1580,7 +1740,7 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
1580
1740
|
console.log('Restart Claude Desktop to load the protected tools.');
|
|
1581
1741
|
}
|
|
1582
1742
|
async function installCodexMcpGatewayCommand(args, config) {
|
|
1583
|
-
const
|
|
1743
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
1584
1744
|
agentClient: 'codex',
|
|
1585
1745
|
agentNamePrefix: 'codex',
|
|
1586
1746
|
userObjective: 'Developer requested this action from Codex.',
|
|
@@ -1589,6 +1749,7 @@ async function installCodexMcpGatewayCommand(args, config) {
|
|
|
1589
1749
|
const file = codexConfigPath(projectScope);
|
|
1590
1750
|
const nodeExe = process.execPath;
|
|
1591
1751
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1752
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
1592
1753
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1593
1754
|
if (!downstreamCommand)
|
|
1594
1755
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
@@ -1601,7 +1762,7 @@ async function installCodexMcpGatewayCommand(args, config) {
|
|
|
1601
1762
|
console.log('Restart Codex or reload MCP servers. Project config requires a trusted project.');
|
|
1602
1763
|
}
|
|
1603
1764
|
async function installGeminiMcpGatewayCommand(args, config) {
|
|
1604
|
-
const
|
|
1765
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
1605
1766
|
agentClient: 'gemini-cli',
|
|
1606
1767
|
agentNamePrefix: 'gemini',
|
|
1607
1768
|
userObjective: 'Developer requested this action from Gemini CLI.',
|
|
@@ -1610,6 +1771,7 @@ async function installGeminiMcpGatewayCommand(args, config) {
|
|
|
1610
1771
|
const file = geminiSettingsPath(projectScope);
|
|
1611
1772
|
const nodeExe = process.execPath;
|
|
1612
1773
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1774
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
1613
1775
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1614
1776
|
if (!downstreamCommand)
|
|
1615
1777
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
@@ -1622,7 +1784,7 @@ async function installGeminiMcpGatewayCommand(args, config) {
|
|
|
1622
1784
|
console.log('Restart Gemini CLI to load the protected tools.');
|
|
1623
1785
|
}
|
|
1624
1786
|
async function installWindsurfMcpGatewayCommand(args, config) {
|
|
1625
|
-
const
|
|
1787
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
1626
1788
|
agentClient: 'windsurf',
|
|
1627
1789
|
agentNamePrefix: 'windsurf',
|
|
1628
1790
|
userObjective: 'Developer requested this action from Windsurf.',
|
|
@@ -1630,6 +1792,7 @@ async function installWindsurfMcpGatewayCommand(args, config) {
|
|
|
1630
1792
|
const file = windsurfConfigPath();
|
|
1631
1793
|
const nodeExe = process.execPath;
|
|
1632
1794
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1795
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
1633
1796
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1634
1797
|
if (!downstreamCommand)
|
|
1635
1798
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
@@ -1642,7 +1805,7 @@ async function installWindsurfMcpGatewayCommand(args, config) {
|
|
|
1642
1805
|
console.log('Restart Windsurf to load the protected tools.');
|
|
1643
1806
|
}
|
|
1644
1807
|
async function installVscodeMcpGatewayCommand(args, config) {
|
|
1645
|
-
const
|
|
1808
|
+
const baseGatewayConfig = resolveGatewayConfig(args, config, {
|
|
1646
1809
|
agentClient: 'vscode',
|
|
1647
1810
|
agentNamePrefix: 'vscode',
|
|
1648
1811
|
userObjective: 'Developer requested this action from VS Code.',
|
|
@@ -1650,6 +1813,7 @@ async function installVscodeMcpGatewayCommand(args, config) {
|
|
|
1650
1813
|
const projectScope = args.project !== 'false';
|
|
1651
1814
|
const nodeExe = process.execPath;
|
|
1652
1815
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1816
|
+
const gatewayConfig = applyPerServerAgentName(baseGatewayConfig, args, serverName);
|
|
1653
1817
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1654
1818
|
if (!downstreamCommand)
|
|
1655
1819
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.12",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"test:agent-file-posture": "npm run build && node scripts/test-agent-file-posture.js",
|
|
25
25
|
"test:realworld-posture": "npm run build && node scripts/test-realworld-posture-fixtures.js",
|
|
26
26
|
"test:discover-stdio-mcp": "npm run build && node scripts/test-discover-stdio-mcp-tools.js",
|
|
27
|
+
"test:per-server-agent-name": "npm run build && node scripts/test-per-server-agent-name.js",
|
|
27
28
|
"test:posture-blast": "npm run build && node scripts/test-posture-blast-radius.js",
|
|
28
29
|
"test:ping-e2e": "npm run build && node scripts/test-ping-e2e.js",
|
|
29
30
|
"prepublishOnly": "npm run build"
|