fullcourtdefense-cli 1.1.17 → 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.
@@ -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 (claudeDesktopMcpCandidates().some(candidate => candidate.exists) || fs.existsSync(path.dirname(claudeDesktopMcpPath()))) {
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' ? detectInstallClients(cursorProject) : selection;
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
@@ -66,8 +66,9 @@ function printHelp() {
66
66
  configure Saves Shield ID, Shield key, and API URL to .fullcourtdefense.yml.
67
67
  scan Runs the scan. Use --local for inside-organization scans.
68
68
  discover Finds MCP servers configured on this machine (Cursor, Claude,
69
- VS Code, Windsurf) and reports their risk. Use --upload to add
70
- them to your AI Inventory.
69
+ VS Code, Windsurf, Codex, Gemini CLI) and reports their risk.
70
+ Use --upload to add them to your AI Inventory. Use --deep for
71
+ live HTTP/SSE tools/list. Use --silent for fleet/MDM scripts.
71
72
  install-cursor-hook
72
73
  Installs a Cursor hook so every AI prompt + agent action
73
74
  (shell, MCP) on this machine is scanned by your Shield.
@@ -199,6 +200,7 @@ function printHelp() {
199
200
  $ fullcourtdefense scan --local --type mcp --mcp-command node --mcp-args ./server.js --mcp-tool all --mode full --format report
200
201
  $ fullcourtdefense discover
201
202
  $ fullcourtdefense discover --upload
203
+ $ fullcourtdefense discover --deep --upload --silent --user-email you@company.com
202
204
  $ fullcourtdefense discover --type mcp --json
203
205
  $ fullcourtdefense install-cursor-hook --shield-id <id> --shield-key <key>
204
206
  $ fullcourtdefense install-cursor-hook --shadow true # monitor only
@@ -206,7 +208,7 @@ function printHelp() {
206
208
  $ fullcourtdefense install-cursor-hook --project true # this repo only
207
209
  $ fullcourtdefense uninstall-cursor-hook
208
210
  $ 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
211
+ $ fullcourtdefense install-mcp-gateway --clients all --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp" --upload --api-key <key>
210
212
  $ fullcourtdefense install-cursor-mcp-gateway --project true --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
211
213
  $ fullcourtdefense install-claude-code-mcp-gateway --scope local --shield-id <id> --shield-key <key> --mcp-command npm --mcp-args "run mcp"
212
214
  $ fullcourtdefense install-claude-code-mcp-gateway --scope project --shield-id <id> --mcp-command npm --mcp-args "run mcp"
@@ -320,6 +322,12 @@ async function main() {
320
322
  upload: flags.upload,
321
323
  connectorName: flags['connector-name'],
322
324
  extraPath: flags.path,
325
+ deep: flags.deep,
326
+ silent: flags.silent,
327
+ userEmail: flags['user-email'],
328
+ schedule: flags.schedule,
329
+ unschedule: flags.unschedule,
330
+ scheduleHour: flags['schedule-hour'],
323
331
  };
324
332
  await (0, discover_1.discoverCommand)(args, config);
325
333
  break;
@@ -398,6 +406,8 @@ async function main() {
398
406
  approvalPollMs: flags['approval-poll-ms'],
399
407
  scanResponse: flags['scan-response'],
400
408
  failClosed: flags['fail-closed'],
409
+ upload: flags.upload,
410
+ apiKey: flags['api-key'],
401
411
  };
402
412
  await (0, mcpGateway_1.installMcpGatewayCommand)(args, config);
403
413
  break;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.1.17",
3
+ "version": "1.3.0",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "fullcourtdefense": "dist/index.js",
8
+ "fcd": "dist/index.js",
8
9
  "botguard": "dist/index.js"
9
10
  },
10
11
  "files": [