fullcourtdefense-cli 1.1.14 → 1.1.15
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 +17 -0
- package/dist/commands/mcpGateway.d.ts +7 -0
- package/dist/commands/mcpGateway.js +174 -3
- package/dist/index.js +35 -1
- package/package.json +1 -1
|
@@ -44,6 +44,7 @@ const SECRET_KEY = /key|token|secret|password|credential|api[_-]?key/i;
|
|
|
44
44
|
function candidateConfigPaths(cwd, extra) {
|
|
45
45
|
const home = os.homedir();
|
|
46
46
|
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
47
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
|
|
47
48
|
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
48
49
|
const list = [
|
|
49
50
|
// Cursor
|
|
@@ -64,6 +65,22 @@ function candidateConfigPaths(cwd, extra) {
|
|
|
64
65
|
{ path: path.join(cwd, '.mcp.json'), source: 'Repo (.mcp.json)' },
|
|
65
66
|
{ path: path.join(cwd, 'mcp.json'), source: 'Repo (mcp.json)' },
|
|
66
67
|
];
|
|
68
|
+
if (process.platform === 'win32') {
|
|
69
|
+
const packagesDir = path.join(localAppData, 'Packages');
|
|
70
|
+
try {
|
|
71
|
+
for (const packageName of fs.readdirSync(packagesDir)) {
|
|
72
|
+
if (!/^Claude_/i.test(packageName))
|
|
73
|
+
continue;
|
|
74
|
+
list.push({
|
|
75
|
+
path: path.join(packagesDir, packageName, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json'),
|
|
76
|
+
source: 'Claude Desktop (win MSIX)',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Missing Packages directory is normal on non-Store installs.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
67
84
|
if (extra)
|
|
68
85
|
list.push({ path: path.resolve(extra), source: 'Custom path' });
|
|
69
86
|
return list;
|
|
@@ -29,7 +29,14 @@ export interface InstallClaudeDesktopMcpGatewayArgs extends McpGatewayArgs {
|
|
|
29
29
|
configPath?: string;
|
|
30
30
|
serverName?: string;
|
|
31
31
|
}
|
|
32
|
+
export interface InstallMcpGatewayArgs extends McpGatewayArgs {
|
|
33
|
+
clients?: string;
|
|
34
|
+
cursorProject?: string;
|
|
35
|
+
claudeCodeScope?: string;
|
|
36
|
+
serverName?: string;
|
|
37
|
+
}
|
|
32
38
|
export declare function mcpGatewayCommand(args: McpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
33
39
|
export declare function installCursorMcpGatewayCommand(args: InstallCursorMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
40
|
+
export declare function installMcpGatewayCommand(args: InstallMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
34
41
|
export declare function installClaudeCodeMcpGatewayCommand(args: InstallClaudeCodeMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
35
42
|
export declare function installClaudeDesktopMcpGatewayCommand(args: InstallClaudeDesktopMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.mcpGatewayCommand = mcpGatewayCommand;
|
|
37
37
|
exports.installCursorMcpGatewayCommand = installCursorMcpGatewayCommand;
|
|
38
|
+
exports.installMcpGatewayCommand = installMcpGatewayCommand;
|
|
38
39
|
exports.installClaudeCodeMcpGatewayCommand = installClaudeCodeMcpGatewayCommand;
|
|
39
40
|
exports.installClaudeDesktopMcpGatewayCommand = installClaudeDesktopMcpGatewayCommand;
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
@@ -663,6 +664,16 @@ function writeMcpServerConfig(file, serverName, nodeExe, commandArgs) {
|
|
|
663
664
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
664
665
|
fs.writeFileSync(file, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
665
666
|
}
|
|
667
|
+
function dedupePaths(candidates) {
|
|
668
|
+
const seen = new Set();
|
|
669
|
+
return candidates.filter(candidate => {
|
|
670
|
+
const key = path.resolve(candidate.file).toLowerCase();
|
|
671
|
+
if (seen.has(key))
|
|
672
|
+
return false;
|
|
673
|
+
seen.add(key);
|
|
674
|
+
return true;
|
|
675
|
+
});
|
|
676
|
+
}
|
|
666
677
|
function claudeDesktopMcpPath() {
|
|
667
678
|
const home = os.homedir();
|
|
668
679
|
if (process.platform === 'win32') {
|
|
@@ -675,11 +686,128 @@ function claudeDesktopMcpPath() {
|
|
|
675
686
|
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
676
687
|
return path.join(xdg, 'Claude', 'claude_desktop_config.json');
|
|
677
688
|
}
|
|
689
|
+
function claudeDesktopMsixCandidates() {
|
|
690
|
+
if (process.platform !== 'win32')
|
|
691
|
+
return [];
|
|
692
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
693
|
+
const packagesDir = path.join(localAppData, 'Packages');
|
|
694
|
+
let packageNames = [];
|
|
695
|
+
try {
|
|
696
|
+
packageNames = fs.readdirSync(packagesDir)
|
|
697
|
+
.filter(name => /^Claude_/i.test(name));
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
return [];
|
|
701
|
+
}
|
|
702
|
+
return packageNames.map(name => {
|
|
703
|
+
const file = path.join(packagesDir, name, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
704
|
+
return {
|
|
705
|
+
file,
|
|
706
|
+
source: 'Claude Desktop (Microsoft Store/MSIX)',
|
|
707
|
+
exists: fs.existsSync(file),
|
|
708
|
+
};
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
function claudeDesktopMcpCandidates() {
|
|
712
|
+
const documented = claudeDesktopMcpPath();
|
|
713
|
+
const normal = {
|
|
714
|
+
file: documented,
|
|
715
|
+
source: process.platform === 'win32'
|
|
716
|
+
? 'Claude Desktop (Windows documented AppData)'
|
|
717
|
+
: process.platform === 'darwin'
|
|
718
|
+
? 'Claude Desktop (macOS)'
|
|
719
|
+
: 'Claude Desktop (Linux/XDG)',
|
|
720
|
+
exists: fs.existsSync(documented),
|
|
721
|
+
};
|
|
722
|
+
// Store/MSIX Claude Desktop reads a virtualized AppData path. Prefer it when present.
|
|
723
|
+
return dedupePaths([
|
|
724
|
+
...claudeDesktopMsixCandidates(),
|
|
725
|
+
normal,
|
|
726
|
+
]);
|
|
727
|
+
}
|
|
728
|
+
function claudeDesktopInstallTargets(configPath) {
|
|
729
|
+
if (configPath) {
|
|
730
|
+
const file = path.resolve(configPath);
|
|
731
|
+
return [{ file, source: 'Claude Desktop (custom config path)', exists: fs.existsSync(file) }];
|
|
732
|
+
}
|
|
733
|
+
const candidates = claudeDesktopMcpCandidates();
|
|
734
|
+
const existing = candidates.filter(candidate => candidate.exists);
|
|
735
|
+
if (existing.length > 0)
|
|
736
|
+
return existing;
|
|
737
|
+
const msix = candidates.find(candidate => candidate.source.includes('MSIX'));
|
|
738
|
+
return [msix || candidates[candidates.length - 1] || {
|
|
739
|
+
file: claudeDesktopMcpPath(),
|
|
740
|
+
source: 'Claude Desktop',
|
|
741
|
+
exists: false,
|
|
742
|
+
}];
|
|
743
|
+
}
|
|
678
744
|
function normalizeClaudeCodeScope(scope) {
|
|
679
745
|
if (scope === 'project' || scope === 'user' || scope === 'local')
|
|
680
746
|
return scope;
|
|
681
747
|
return 'local';
|
|
682
748
|
}
|
|
749
|
+
function parseClientList(value) {
|
|
750
|
+
const raw = (value || 'auto').trim().toLowerCase();
|
|
751
|
+
if (!raw || raw === 'auto')
|
|
752
|
+
return 'auto';
|
|
753
|
+
const aliases = {
|
|
754
|
+
cursor: 'cursor',
|
|
755
|
+
'claude-code': 'claude-code',
|
|
756
|
+
claude_code: 'claude-code',
|
|
757
|
+
code: 'claude-code',
|
|
758
|
+
'claude-desktop': 'claude-desktop',
|
|
759
|
+
claude_desktop: 'claude-desktop',
|
|
760
|
+
desktop: 'claude-desktop',
|
|
761
|
+
};
|
|
762
|
+
const requested = raw === 'all'
|
|
763
|
+
? ['cursor', 'claude-code', 'claude-desktop']
|
|
764
|
+
: raw.split(',').map(part => part.trim()).filter(Boolean);
|
|
765
|
+
const clients = requested.map(client => aliases[client]);
|
|
766
|
+
const unknown = requested.filter((_, index) => !clients[index]);
|
|
767
|
+
if (unknown.length > 0)
|
|
768
|
+
throw new Error(`Unknown client(s): ${unknown.join(', ')}. Use auto, all, cursor, claude-code, claude-desktop.`);
|
|
769
|
+
return Array.from(new Set(clients));
|
|
770
|
+
}
|
|
771
|
+
function commandAvailable(command) {
|
|
772
|
+
const result = (0, child_process_1.spawnSync)(process.platform === 'win32' ? 'where' : 'command', process.platform === 'win32' ? [command] : ['-v', command], {
|
|
773
|
+
stdio: 'ignore',
|
|
774
|
+
shell: process.platform !== 'win32',
|
|
775
|
+
});
|
|
776
|
+
return result.status === 0;
|
|
777
|
+
}
|
|
778
|
+
function detectInstallClients(cursorProject) {
|
|
779
|
+
const clients = [];
|
|
780
|
+
const cursorGlobal = path.join(os.homedir(), '.cursor');
|
|
781
|
+
const cursorProjectDir = path.join(process.cwd(), '.cursor');
|
|
782
|
+
if (fs.existsSync(cursorGlobal) || fs.existsSync(cursorProjectDir) || cursorProject)
|
|
783
|
+
clients.push('cursor');
|
|
784
|
+
if (commandAvailable('claude'))
|
|
785
|
+
clients.push('claude-code');
|
|
786
|
+
if (claudeDesktopMcpCandidates().some(candidate => candidate.exists) || fs.existsSync(path.dirname(claudeDesktopMcpPath()))) {
|
|
787
|
+
clients.push('claude-desktop');
|
|
788
|
+
}
|
|
789
|
+
return clients;
|
|
790
|
+
}
|
|
791
|
+
function clientInstallArgs(args) {
|
|
792
|
+
return {
|
|
793
|
+
mcpCommand: args.mcpCommand,
|
|
794
|
+
mcpArgs: args.mcpArgs,
|
|
795
|
+
agentName: args.agentName,
|
|
796
|
+
agentClient: args.agentClient,
|
|
797
|
+
developerName: args.developerName,
|
|
798
|
+
shieldId: args.shieldId,
|
|
799
|
+
shieldKey: args.shieldKey,
|
|
800
|
+
apiUrl: args.apiUrl,
|
|
801
|
+
environment: args.environment,
|
|
802
|
+
userObjective: args.userObjective,
|
|
803
|
+
authority: args.authority,
|
|
804
|
+
approvalMode: args.approvalMode,
|
|
805
|
+
approvalTimeoutMs: args.approvalTimeoutMs,
|
|
806
|
+
approvalPollMs: args.approvalPollMs,
|
|
807
|
+
scanResponse: args.scanResponse,
|
|
808
|
+
failClosed: args.failClosed,
|
|
809
|
+
};
|
|
810
|
+
}
|
|
683
811
|
async function installCursorMcpGatewayCommand(args, config) {
|
|
684
812
|
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
685
813
|
agentClient: 'cursor',
|
|
@@ -702,6 +830,45 @@ async function installCursorMcpGatewayCommand(args, config) {
|
|
|
702
830
|
console.log(`Downstream: ${downstreamCommand} ${downstreamArgs.join(' ')}`);
|
|
703
831
|
console.log('Restart Cursor or reload MCP servers to use the protected tools.');
|
|
704
832
|
}
|
|
833
|
+
async function installMcpGatewayCommand(args, config) {
|
|
834
|
+
const cursorProject = args.cursorProject === 'true';
|
|
835
|
+
const selection = parseClientList(args.clients);
|
|
836
|
+
const clients = selection === 'auto' ? detectInstallClients(cursorProject) : selection;
|
|
837
|
+
if (clients.length === 0) {
|
|
838
|
+
throw new Error('No supported MCP clients detected. Re-run with --clients all or a comma list such as cursor,claude-desktop.');
|
|
839
|
+
}
|
|
840
|
+
const baseArgs = clientInstallArgs(args);
|
|
841
|
+
const successes = [];
|
|
842
|
+
const failures = [];
|
|
843
|
+
for (const client of clients) {
|
|
844
|
+
try {
|
|
845
|
+
if (client === 'cursor') {
|
|
846
|
+
await installCursorMcpGatewayCommand({ ...baseArgs, project: String(cursorProject), serverName: args.serverName }, config);
|
|
847
|
+
}
|
|
848
|
+
else if (client === 'claude-code') {
|
|
849
|
+
await installClaudeCodeMcpGatewayCommand({ ...baseArgs, scope: args.claudeCodeScope, serverName: args.serverName }, config);
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
await installClaudeDesktopMcpGatewayCommand({ ...baseArgs, serverName: args.serverName }, config);
|
|
853
|
+
}
|
|
854
|
+
successes.push(client);
|
|
855
|
+
}
|
|
856
|
+
catch (error) {
|
|
857
|
+
failures.push(`${client}: ${error instanceof Error ? error.message : String(error)}`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
console.log('');
|
|
861
|
+
console.log('AgentGuard one-install summary:');
|
|
862
|
+
console.log(`Installed: ${successes.length > 0 ? successes.join(', ') : 'none'}`);
|
|
863
|
+
if (failures.length > 0) {
|
|
864
|
+
console.log('Failed:');
|
|
865
|
+
for (const failure of failures)
|
|
866
|
+
console.log(`- ${failure}`);
|
|
867
|
+
}
|
|
868
|
+
console.log(`Runtime identity: ${detectedDeveloperName(args)}`);
|
|
869
|
+
console.log('Restart/reload each client to use the protected tools.');
|
|
870
|
+
console.log('Smoke test prompt: "Use lookup_customer to look up customer C-1001."');
|
|
871
|
+
}
|
|
705
872
|
async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
706
873
|
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
707
874
|
agentClient: 'claude-code',
|
|
@@ -753,7 +920,7 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
753
920
|
agentNamePrefix: 'claude-desktop',
|
|
754
921
|
userObjective: 'Developer requested this action from Claude Desktop.',
|
|
755
922
|
});
|
|
756
|
-
const
|
|
923
|
+
const targets = claudeDesktopInstallTargets(args.configPath);
|
|
757
924
|
const nodeExe = process.execPath;
|
|
758
925
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
759
926
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
@@ -761,9 +928,13 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
761
928
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
762
929
|
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
763
930
|
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs);
|
|
764
|
-
|
|
931
|
+
for (const target of targets) {
|
|
932
|
+
writeMcpServerConfig(target.file, serverName, nodeExe, commandArgs);
|
|
933
|
+
}
|
|
765
934
|
console.log('AgentGuard MCP Gateway installed for Claude Desktop.');
|
|
766
|
-
|
|
935
|
+
for (const target of targets) {
|
|
936
|
+
console.log(`Config: ${target.file} (${target.source})`);
|
|
937
|
+
}
|
|
767
938
|
console.log(`Server: ${serverName}`);
|
|
768
939
|
console.log(`Downstream: ${downstreamCommand} ${downstreamArgs.join(' ')}`);
|
|
769
940
|
console.log('Restart Claude Desktop to load the protected tools.');
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const discover_1 = require("./commands/discover");
|
|
|
11
11
|
const hook_1 = require("./commands/hook");
|
|
12
12
|
const installCursorHook_1 = require("./commands/installCursorHook");
|
|
13
13
|
const mcpGateway_1 = require("./commands/mcpGateway");
|
|
14
|
-
const VERSION = '1.1.
|
|
14
|
+
const VERSION = '1.1.15';
|
|
15
15
|
function parseArgs(argv) {
|
|
16
16
|
const flags = {};
|
|
17
17
|
let command = '';
|
|
@@ -73,6 +73,9 @@ function printHelp() {
|
|
|
73
73
|
(shell, MCP) on this machine is scanned by your Shield.
|
|
74
74
|
uninstall-cursor-hook
|
|
75
75
|
Removes the FullCourtDefense Cursor hook entries.
|
|
76
|
+
install-mcp-gateway
|
|
77
|
+
One command to install the AgentGuard MCP Gateway into detected
|
|
78
|
+
clients (Cursor, Claude Code, Claude Desktop).
|
|
76
79
|
mcp-gateway
|
|
77
80
|
Runs a local MCP gateway that checks AgentGuard runtime/action
|
|
78
81
|
policies before forwarding tool calls to a real MCP server.
|
|
@@ -148,9 +151,13 @@ function printHelp() {
|
|
|
148
151
|
--mcp-args <args> MCP server args/path, for example ./server.js
|
|
149
152
|
--mcp-tool <tool> MCP tool to test, or all
|
|
150
153
|
--server-name <name> MCP gateway server name for client installers
|
|
154
|
+
--clients <list> Gateway install clients: auto, all, cursor,
|
|
155
|
+
claude-code, claude-desktop
|
|
151
156
|
--developer-name <name> Runtime identity for user-scoped policies (auto-detected if omitted)
|
|
152
157
|
--agent-name <name> Runtime agent name (auto-generated from identity + client if omitted)
|
|
153
158
|
--scope <scope> Claude Code MCP scope: local, project, or user
|
|
159
|
+
--claude-code-scope <s> Claude Code scope for install-mcp-gateway
|
|
160
|
+
--cursor-project <bool> Cursor project config for install-mcp-gateway
|
|
154
161
|
--config-path <path> Claude Desktop config override path
|
|
155
162
|
--mcp-auth-type <type> HTTP MCP auth: none, bearer, basic, api-key
|
|
156
163
|
--mcp-token <token> HTTP MCP bearer token
|
|
@@ -198,6 +205,8 @@ function printHelp() {
|
|
|
198
205
|
$ fullcourtdefense install-cursor-hook --events prompt,shell,mcp,file
|
|
199
206
|
$ fullcourtdefense install-cursor-hook --project true # this repo only
|
|
200
207
|
$ fullcourtdefense uninstall-cursor-hook
|
|
208
|
+
$ fullcourtdefense install-mcp-gateway --clients auto --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
|
|
209
|
+
$ fullcourtdefense install-mcp-gateway --clients all --mcp-command node --mcp-args ./server.js
|
|
201
210
|
$ fullcourtdefense install-cursor-mcp-gateway --project true --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
|
|
202
211
|
$ fullcourtdefense install-claude-code-mcp-gateway --scope local --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
|
|
203
212
|
$ fullcourtdefense install-claude-code-mcp-gateway --scope project --shield-id <id> --mcp-command npm --mcp-args "run mcp"
|
|
@@ -368,6 +377,31 @@ async function main() {
|
|
|
368
377
|
await (0, mcpGateway_1.mcpGatewayCommand)(args, config);
|
|
369
378
|
break;
|
|
370
379
|
}
|
|
380
|
+
case 'install-mcp-gateway': {
|
|
381
|
+
const args = {
|
|
382
|
+
clients: flags.clients,
|
|
383
|
+
cursorProject: flags['cursor-project'],
|
|
384
|
+
claudeCodeScope: flags['claude-code-scope'] || flags.scope,
|
|
385
|
+
serverName: flags['server-name'],
|
|
386
|
+
mcpCommand: flags['mcp-command'],
|
|
387
|
+
mcpArgs: flags['mcp-args'],
|
|
388
|
+
agentName: flags['agent-name'],
|
|
389
|
+
developerName: flags['developer-name'],
|
|
390
|
+
shieldId: flags['shield-id'],
|
|
391
|
+
shieldKey: flags['shield-key'],
|
|
392
|
+
apiUrl: flags['api-url'],
|
|
393
|
+
environment: flags.environment,
|
|
394
|
+
userObjective: flags['user-objective'],
|
|
395
|
+
authority: flags.authority,
|
|
396
|
+
approvalMode: flags['approval-mode'],
|
|
397
|
+
approvalTimeoutMs: flags['approval-timeout-ms'],
|
|
398
|
+
approvalPollMs: flags['approval-poll-ms'],
|
|
399
|
+
scanResponse: flags['scan-response'],
|
|
400
|
+
failClosed: flags['fail-closed'],
|
|
401
|
+
};
|
|
402
|
+
await (0, mcpGateway_1.installMcpGatewayCommand)(args, config);
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
371
405
|
case 'install-cursor-mcp-gateway': {
|
|
372
406
|
const args = {
|
|
373
407
|
project: flags.project,
|