fullcourtdefense-cli 1.1.13 → 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 +227 -22
- 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"));
|
|
@@ -80,24 +81,27 @@ function normalizeAgentClient(value, fallback) {
|
|
|
80
81
|
return value;
|
|
81
82
|
return fallback;
|
|
82
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Detect which agent client spawned this gateway process at runtime.
|
|
86
|
+
*
|
|
87
|
+
* Claude Code sets `CLAUDECODE=1` and `CLAUDE_CODE_SESSION_ID` in every stdio
|
|
88
|
+
* MCP subprocess it starts, so a gateway launched by Claude Code can self-label
|
|
89
|
+
* even when it was installed with no explicit `--agent-client`. Cursor and
|
|
90
|
+
* Claude Desktop don't expose a runtime marker, so they fall through to the
|
|
91
|
+
* install-time default baked into the MCP config.
|
|
92
|
+
*/
|
|
93
|
+
function detectRuntimeAgentClient() {
|
|
94
|
+
if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_SESSION_ID)
|
|
95
|
+
return 'claude-code';
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/** Claude Code passes the session id to spawned MCP servers for correlation. */
|
|
99
|
+
function runtimeSessionId() {
|
|
100
|
+
return firstNonEmpty(process.env.CLAUDE_CODE_SESSION_ID, process.env.FCD_SESSION_ID);
|
|
101
|
+
}
|
|
83
102
|
function firstNonEmpty(...values) {
|
|
84
103
|
return values.map(value => value?.trim()).find(Boolean);
|
|
85
104
|
}
|
|
86
|
-
function commandOutput(command, args) {
|
|
87
|
-
try {
|
|
88
|
-
const result = (0, child_process_1.spawnSync)(command, args, {
|
|
89
|
-
encoding: 'utf8',
|
|
90
|
-
shell: process.platform === 'win32',
|
|
91
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
92
|
-
});
|
|
93
|
-
if (result.status !== 0)
|
|
94
|
-
return undefined;
|
|
95
|
-
return result.stdout?.trim() || undefined;
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return undefined;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
105
|
function safeIdentityPart(value) {
|
|
102
106
|
return value
|
|
103
107
|
.trim()
|
|
@@ -106,8 +110,18 @@ function safeIdentityPart(value) {
|
|
|
106
110
|
.replace(/^-+|-+$/g, '')
|
|
107
111
|
.toLowerCase() || 'local-user';
|
|
108
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the developer identity for runtime attribution.
|
|
115
|
+
*
|
|
116
|
+
* Same behavior for Cursor, Claude Code, and Claude Desktop: prefer an explicit
|
|
117
|
+
* name (flag or env var IT can set per machine), otherwise fall back to
|
|
118
|
+
* `username@hostname` — exactly like the Cursor hook. We intentionally do NOT
|
|
119
|
+
* guess from git email / npm whoami: neither Cursor nor Claude exposes the
|
|
120
|
+
* account email, so the machine-scoped login is the one stable, predictable,
|
|
121
|
+
* collision-free identity.
|
|
122
|
+
*/
|
|
109
123
|
function detectedDeveloperName(args) {
|
|
110
|
-
return firstNonEmpty(args.developerName, process.env.FCD_DEVELOPER_NAME, process.env.FULLCOURTDEFENSE_DEVELOPER_NAME, process.env.AGENTGUARD_DEVELOPER_NAME,
|
|
124
|
+
return firstNonEmpty(args.developerName, process.env.FCD_DEVELOPER_NAME, process.env.FULLCOURTDEFENSE_DEVELOPER_NAME, process.env.AGENTGUARD_DEVELOPER_NAME, machineScopedUser()) || 'unknown';
|
|
111
125
|
}
|
|
112
126
|
function defaultAgentName(agentClient, developerName, defaults) {
|
|
113
127
|
if (defaults.agentName)
|
|
@@ -120,7 +134,7 @@ function resolveGatewayConfig(args, config, defaults = {}) {
|
|
|
120
134
|
const shieldId = args.shieldId || process.env.FCD_SHIELD_ID || process.env.FULLCOURTDEFENSE_SHIELD_ID || process.env.AGENTGUARD_SHIELD_ID || config.shieldId || home.shieldId;
|
|
121
135
|
if (!shieldId)
|
|
122
136
|
throw new Error('Missing Shield ID. Pass --shield-id or run fullcourtdefense configure.');
|
|
123
|
-
const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT, defaults.agentClient || 'cursor');
|
|
137
|
+
const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT || detectRuntimeAgentClient(), defaults.agentClient || 'cursor');
|
|
124
138
|
const developerName = detectedDeveloperName(args);
|
|
125
139
|
return {
|
|
126
140
|
shieldId,
|
|
@@ -140,15 +154,17 @@ function resolveGatewayConfig(args, config, defaults = {}) {
|
|
|
140
154
|
};
|
|
141
155
|
}
|
|
142
156
|
function machineMetadata(developerName, agentClient = 'cursor') {
|
|
157
|
+
const sessionId = runtimeSessionId();
|
|
143
158
|
return {
|
|
144
|
-
developerName: developerName ||
|
|
159
|
+
developerName: developerName || machineScopedUser(),
|
|
145
160
|
machineName: os.hostname(),
|
|
146
161
|
os: `${os.platform()} ${os.release()}`,
|
|
147
162
|
username: safeUserName(),
|
|
148
163
|
ipAddress: firstPrivateIp(),
|
|
149
164
|
cwd: process.cwd(),
|
|
150
|
-
workspacePath: process.env.WORKSPACE_PATH || process.cwd(),
|
|
165
|
+
workspacePath: process.env.WORKSPACE_PATH || process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
|
151
166
|
agentClient,
|
|
167
|
+
...(sessionId ? { sessionId } : {}),
|
|
152
168
|
gateway: 'agentguard-mcp-gateway',
|
|
153
169
|
};
|
|
154
170
|
}
|
|
@@ -170,6 +186,25 @@ function safeUserName() {
|
|
|
170
186
|
return 'unknown';
|
|
171
187
|
}
|
|
172
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Machine-scoped identity: `username@hostname` (e.g. `boazl@BOAZ-PC`).
|
|
191
|
+
*
|
|
192
|
+
* Bare OS usernames collide across machines (every laptop has an `admin` /
|
|
193
|
+
* `user`), which breaks per-person attribution and fails SOC 2 CC6.1
|
|
194
|
+
* non-repudiation. Pairing the login with the hostname makes the identity
|
|
195
|
+
* unique per device with zero config, and matches the Cursor hook's format.
|
|
196
|
+
* For verified corporate identity, set `FCD_DEVELOPER_NAME` (e.g. via MDM/IdP).
|
|
197
|
+
*/
|
|
198
|
+
function machineScopedUser() {
|
|
199
|
+
const user = safeUserName();
|
|
200
|
+
try {
|
|
201
|
+
const host = os.hostname();
|
|
202
|
+
return host ? `${user}@${host}` : user;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return user;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
173
208
|
function summarizeToolArgs(args, developerName, agentClient = 'cursor') {
|
|
174
209
|
const out = { ...machineMetadata(developerName, agentClient) };
|
|
175
210
|
for (const [key, value] of Object.entries(args || {})) {
|
|
@@ -629,6 +664,16 @@ function writeMcpServerConfig(file, serverName, nodeExe, commandArgs) {
|
|
|
629
664
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
630
665
|
fs.writeFileSync(file, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
631
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
|
+
}
|
|
632
677
|
function claudeDesktopMcpPath() {
|
|
633
678
|
const home = os.homedir();
|
|
634
679
|
if (process.platform === 'win32') {
|
|
@@ -641,11 +686,128 @@ function claudeDesktopMcpPath() {
|
|
|
641
686
|
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
642
687
|
return path.join(xdg, 'Claude', 'claude_desktop_config.json');
|
|
643
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
|
+
}
|
|
644
744
|
function normalizeClaudeCodeScope(scope) {
|
|
645
745
|
if (scope === 'project' || scope === 'user' || scope === 'local')
|
|
646
746
|
return scope;
|
|
647
747
|
return 'local';
|
|
648
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
|
+
}
|
|
649
811
|
async function installCursorMcpGatewayCommand(args, config) {
|
|
650
812
|
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
651
813
|
agentClient: 'cursor',
|
|
@@ -668,6 +830,45 @@ async function installCursorMcpGatewayCommand(args, config) {
|
|
|
668
830
|
console.log(`Downstream: ${downstreamCommand} ${downstreamArgs.join(' ')}`);
|
|
669
831
|
console.log('Restart Cursor or reload MCP servers to use the protected tools.');
|
|
670
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
|
+
}
|
|
671
872
|
async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
672
873
|
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
673
874
|
agentClient: 'claude-code',
|
|
@@ -719,7 +920,7 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
719
920
|
agentNamePrefix: 'claude-desktop',
|
|
720
921
|
userObjective: 'Developer requested this action from Claude Desktop.',
|
|
721
922
|
});
|
|
722
|
-
const
|
|
923
|
+
const targets = claudeDesktopInstallTargets(args.configPath);
|
|
723
924
|
const nodeExe = process.execPath;
|
|
724
925
|
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
725
926
|
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
@@ -727,9 +928,13 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
727
928
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
728
929
|
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
729
930
|
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs);
|
|
730
|
-
|
|
931
|
+
for (const target of targets) {
|
|
932
|
+
writeMcpServerConfig(target.file, serverName, nodeExe, commandArgs);
|
|
933
|
+
}
|
|
731
934
|
console.log('AgentGuard MCP Gateway installed for Claude Desktop.');
|
|
732
|
-
|
|
935
|
+
for (const target of targets) {
|
|
936
|
+
console.log(`Config: ${target.file} (${target.source})`);
|
|
937
|
+
}
|
|
733
938
|
console.log(`Server: ${serverName}`);
|
|
734
939
|
console.log(`Downstream: ${downstreamCommand} ${downstreamArgs.join(' ')}`);
|
|
735
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,
|