expxagents 0.19.0 → 0.20.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.
Files changed (38) hide show
  1. package/assets/mcps/_catalog.yaml +17 -0
  2. package/assets/mcps/figma.mcp.yaml +35 -0
  3. package/assets/mcps/github.mcp.yaml +44 -0
  4. package/assets/mcps/linear.mcp.yaml +37 -0
  5. package/assets/mcps/notion.mcp.yaml +37 -0
  6. package/assets/mcps/pencil.mcp.yaml +32 -0
  7. package/assets/mcps/postgresql.mcp.yaml +39 -0
  8. package/assets/mcps/sentry.mcp.yaml +41 -0
  9. package/assets/mcps/slack.mcp.yaml +37 -0
  10. package/assets/mcps/vercel.mcp.yaml +39 -0
  11. package/dist/cli/src/commands/doctor.js +26 -10
  12. package/dist/cli/src/commands/init.js +35 -15
  13. package/dist/cli/src/commands/mcp.d.ts +2 -0
  14. package/dist/cli/src/commands/mcp.js +155 -0
  15. package/dist/cli/src/index.js +2 -0
  16. package/dist/cli/src/mcp/__tests__/catalog.test.d.ts +1 -0
  17. package/dist/cli/src/mcp/__tests__/catalog.test.js +101 -0
  18. package/dist/cli/src/mcp/__tests__/detect.test.d.ts +1 -0
  19. package/dist/cli/src/mcp/__tests__/detect.test.js +84 -0
  20. package/dist/cli/src/mcp/__tests__/setup.test.d.ts +1 -0
  21. package/dist/cli/src/mcp/__tests__/setup.test.js +75 -0
  22. package/dist/cli/src/mcp/__tests__/validate.test.d.ts +1 -0
  23. package/dist/cli/src/mcp/__tests__/validate.test.js +42 -0
  24. package/dist/cli/src/mcp/catalog.d.ts +4 -0
  25. package/dist/cli/src/mcp/catalog.js +53 -0
  26. package/dist/cli/src/mcp/detect.d.ts +9 -0
  27. package/dist/cli/src/mcp/detect.js +75 -0
  28. package/dist/cli/src/mcp/setup.d.ts +4 -0
  29. package/dist/cli/src/mcp/setup.js +56 -0
  30. package/dist/cli/src/mcp/types.d.ts +68 -0
  31. package/dist/cli/src/mcp/types.js +1 -0
  32. package/dist/cli/src/mcp/validate.d.ts +2 -0
  33. package/dist/cli/src/mcp/validate.js +23 -0
  34. package/dist/cli/src/pencil/detect.d.ts +6 -11
  35. package/dist/cli/src/pencil/detect.js +8 -39
  36. package/dist/core/squad-loader.d.ts +1 -0
  37. package/dist/core/squad-loader.js +2 -0
  38. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ catalog:
2
+ version: "1.0.0"
3
+ categories:
4
+ - name: development
5
+ mcps: [github, postgresql]
6
+ - name: communication
7
+ mcps: [slack]
8
+ - name: project-management
9
+ mcps: [linear]
10
+ - name: monitoring
11
+ mcps: [sentry]
12
+ - name: knowledge
13
+ mcps: [notion]
14
+ - name: design
15
+ mcps: [figma, pencil]
16
+ - name: infrastructure
17
+ mcps: [vercel]
@@ -0,0 +1,35 @@
1
+ id: figma
2
+ name: Figma
3
+ description: Design files, components, and style inspection
4
+ category: design
5
+ icon: figma
6
+
7
+ detection:
8
+ method: npm
9
+ npm:
10
+ package: "figma-developer-mcp"
11
+
12
+ server:
13
+ command: npx
14
+ args: ["-y", "figma-developer-mcp"]
15
+ env:
16
+ FIGMA_ACCESS_TOKEN: "${FIGMA_ACCESS_TOKEN}"
17
+
18
+ auth:
19
+ - key: FIGMA_ACCESS_TOKEN
20
+ label: "Figma Access Token"
21
+ hint: "Create at https://www.figma.com/developers/api#access-tokens"
22
+ required: true
23
+
24
+ tools:
25
+ - name: get_file
26
+ description: Get a Figma file and its contents
27
+ - name: get_components
28
+ description: Get components from a Figma file
29
+ - name: get_styles
30
+ description: Get styles defined in a Figma file
31
+
32
+ relevant_sectors:
33
+ - design
34
+ - product
35
+ - development
@@ -0,0 +1,44 @@
1
+ id: github
2
+ name: GitHub
3
+ description: PRs, issues, code review, releases
4
+ category: development
5
+ icon: github
6
+
7
+ detection:
8
+ method: hybrid
9
+ cli:
10
+ command: gh
11
+ version_flag: --version
12
+ min_version: "2.0.0"
13
+ npm:
14
+ package: "@modelcontextprotocol/server-github"
15
+
16
+ server:
17
+ command: npx
18
+ args: ["-y", "@modelcontextprotocol/server-github"]
19
+ env:
20
+ GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_PERSONAL_ACCESS_TOKEN}"
21
+
22
+ auth:
23
+ - key: GITHUB_PERSONAL_ACCESS_TOKEN
24
+ label: "GitHub Personal Access Token"
25
+ hint: "Create at https://github.com/settings/tokens (scopes: repo, read:org)"
26
+ required: true
27
+ validate: "gh auth status"
28
+
29
+ tools:
30
+ - name: create_pull_request
31
+ description: Create a new pull request
32
+ - name: list_issues
33
+ description: List repository issues
34
+ - name: create_issue
35
+ description: Create a new issue
36
+ - name: search_code
37
+ description: Search code across repositories
38
+ - name: get_file_contents
39
+ description: Get contents of a file
40
+
41
+ relevant_sectors:
42
+ - development
43
+ - quality
44
+ - operations
@@ -0,0 +1,37 @@
1
+ id: linear
2
+ name: Linear
3
+ description: Issue tracking, project management, and team workflows
4
+ category: project-management
5
+ icon: linear
6
+
7
+ detection:
8
+ method: npm
9
+ npm:
10
+ package: "@larryhudson/linear-mcp-server"
11
+
12
+ server:
13
+ command: npx
14
+ args: ["-y", "@larryhudson/linear-mcp-server"]
15
+ env:
16
+ LINEAR_API_KEY: "${LINEAR_API_KEY}"
17
+
18
+ auth:
19
+ - key: LINEAR_API_KEY
20
+ label: "Linear API Key"
21
+ hint: "Create at https://linear.app/settings/api"
22
+ required: true
23
+
24
+ tools:
25
+ - name: create_issue
26
+ description: Create a new issue
27
+ - name: list_issues
28
+ description: List project issues
29
+ - name: update_issue
30
+ description: Update an existing issue
31
+ - name: list_projects
32
+ description: List available projects
33
+
34
+ relevant_sectors:
35
+ - product
36
+ - development
37
+ - operations
@@ -0,0 +1,37 @@
1
+ id: notion
2
+ name: Notion
3
+ description: Knowledge base, documentation, and collaborative workspace
4
+ category: knowledge
5
+ icon: notion
6
+
7
+ detection:
8
+ method: npm
9
+ npm:
10
+ package: "@notionhq/notion-mcp-server"
11
+
12
+ server:
13
+ command: npx
14
+ args: ["-y", "@notionhq/notion-mcp-server"]
15
+ env:
16
+ NOTION_API_KEY: "${NOTION_API_KEY}"
17
+
18
+ auth:
19
+ - key: NOTION_API_KEY
20
+ label: "Notion API Key"
21
+ hint: "Create at https://www.notion.so/my-integrations"
22
+ required: true
23
+
24
+ tools:
25
+ - name: search
26
+ description: Search pages and databases
27
+ - name: get_page
28
+ description: Get the content of a page
29
+ - name: create_page
30
+ description: Create a new page
31
+ - name: update_page
32
+ description: Update an existing page
33
+
34
+ relevant_sectors:
35
+ - knowledge
36
+ - product
37
+ - operations
@@ -0,0 +1,32 @@
1
+ id: pencil
2
+ name: Pencil
3
+ description: Visual design creation and editing in VS Code
4
+ category: design
5
+ icon: pencil
6
+
7
+ detection:
8
+ method: extension
9
+ extension:
10
+ prefix: "highagency.pencildev-"
11
+ binary_dir: out
12
+ binary_resolve: platform
13
+
14
+ server:
15
+ command: null
16
+ args: ["--app", "visual_studio_code"]
17
+ env: {}
18
+
19
+ auth: []
20
+
21
+ tools:
22
+ - name: batch_design
23
+ description: Create and modify visual designs
24
+ - name: batch_get
25
+ description: Read design nodes
26
+ - name: get_screenshot
27
+ description: Capture design screenshots
28
+
29
+ relevant_sectors:
30
+ - design
31
+ - marketing
32
+ - product
@@ -0,0 +1,39 @@
1
+ id: postgresql
2
+ name: PostgreSQL
3
+ description: SQL queries, schema inspection, and database management
4
+ category: development
5
+ icon: postgresql
6
+
7
+ detection:
8
+ method: hybrid
9
+ cli:
10
+ command: psql
11
+ version_flag: --version
12
+ npm:
13
+ package: "@modelcontextprotocol/server-postgres"
14
+
15
+ server:
16
+ command: npx
17
+ args: ["-y", "@modelcontextprotocol/server-postgres"]
18
+ env:
19
+ POSTGRESQL_URL: "${POSTGRESQL_URL}"
20
+
21
+ auth:
22
+ - key: POSTGRESQL_URL
23
+ label: "PostgreSQL Connection URL"
24
+ hint: "Format: postgresql://user:password@host:5432/database"
25
+ required: true
26
+ validate: "psql -c 'SELECT 1'"
27
+
28
+ tools:
29
+ - name: query
30
+ description: Execute a SQL query
31
+ - name: list_tables
32
+ description: List all tables in the database
33
+ - name: describe_table
34
+ description: Get the schema of a specific table
35
+
36
+ relevant_sectors:
37
+ - development
38
+ - data
39
+ - operations
@@ -0,0 +1,41 @@
1
+ id: sentry
2
+ name: Sentry
3
+ description: Error tracking, performance monitoring, and issue management
4
+ category: monitoring
5
+ icon: sentry
6
+
7
+ detection:
8
+ method: npm
9
+ npm:
10
+ package: "@sentry/mcp-server"
11
+
12
+ server:
13
+ command: npx
14
+ args: ["-y", "@sentry/mcp-server"]
15
+ env:
16
+ SENTRY_AUTH_TOKEN: "${SENTRY_AUTH_TOKEN}"
17
+ SENTRY_ORG: "${SENTRY_ORG}"
18
+
19
+ auth:
20
+ - key: SENTRY_AUTH_TOKEN
21
+ label: "Sentry Auth Token"
22
+ hint: "Create at https://sentry.io/settings/auth-tokens/"
23
+ required: true
24
+ validate: "sentry-cli info"
25
+ - key: SENTRY_ORG
26
+ label: "Sentry Organization Slug"
27
+ hint: "Your Sentry organization slug"
28
+ required: true
29
+
30
+ tools:
31
+ - name: list_issues
32
+ description: List Sentry issues
33
+ - name: get_issue
34
+ description: Get details of a specific issue
35
+ - name: search_events
36
+ description: Search error events
37
+
38
+ relevant_sectors:
39
+ - development
40
+ - quality
41
+ - infrastructure
@@ -0,0 +1,37 @@
1
+ id: slack
2
+ name: Slack
3
+ description: Team messaging, channels, and notifications
4
+ category: communication
5
+ icon: slack
6
+
7
+ detection:
8
+ method: npm
9
+ npm:
10
+ package: "@modelcontextprotocol/server-slack"
11
+
12
+ server:
13
+ command: npx
14
+ args: ["-y", "@modelcontextprotocol/server-slack"]
15
+ env:
16
+ SLACK_BOT_TOKEN: "${SLACK_BOT_TOKEN}"
17
+
18
+ auth:
19
+ - key: SLACK_BOT_TOKEN
20
+ label: "Slack Bot Token"
21
+ hint: "Create a Slack app at https://api.slack.com/apps"
22
+ required: true
23
+
24
+ tools:
25
+ - name: send_message
26
+ description: Send a message to a channel
27
+ - name: list_channels
28
+ description: List available channels
29
+ - name: search_messages
30
+ description: Search messages across channels
31
+ - name: get_thread
32
+ description: Get messages in a thread
33
+
34
+ relevant_sectors:
35
+ - communication
36
+ - operations
37
+ - customer-success
@@ -0,0 +1,39 @@
1
+ id: vercel
2
+ name: Vercel
3
+ description: Deployments, projects, and hosting management
4
+ category: infrastructure
5
+ icon: vercel
6
+
7
+ detection:
8
+ method: hybrid
9
+ cli:
10
+ command: vercel
11
+ version_flag: --version
12
+ npm:
13
+ package: "mcp-handler"
14
+
15
+ server:
16
+ command: npx
17
+ args: ["-y", "mcp-handler"]
18
+ env:
19
+ VERCEL_TOKEN: "${VERCEL_TOKEN}"
20
+
21
+ auth:
22
+ - key: VERCEL_TOKEN
23
+ label: "Vercel Token"
24
+ hint: "Create at https://vercel.com/account/tokens"
25
+ required: true
26
+ validate: "vercel whoami"
27
+
28
+ tools:
29
+ - name: list_deployments
30
+ description: List recent deployments
31
+ - name: get_deployment
32
+ description: Get details of a specific deployment
33
+ - name: list_projects
34
+ description: List all projects
35
+
36
+ relevant_sectors:
37
+ - infrastructure
38
+ - development
39
+ - operations
@@ -2,7 +2,6 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { execSync } from 'child_process';
4
4
  import { getAssetsDir } from '../utils/config.js';
5
- import { detectPencilExtension } from '../pencil/detect.js';
6
5
  export async function doctorCommand() {
7
6
  const cwd = process.cwd();
8
7
  const checks = [];
@@ -22,15 +21,32 @@ export async function doctorCommand() {
22
21
  catch {
23
22
  checks.push({ name: 'Claude Code CLI', status: 'fail', message: 'Not found — install from https://claude.ai/code' });
24
23
  }
25
- // Pencil extension (optional)
26
- const pencilInfo = detectPencilExtension();
27
- checks.push({
28
- name: 'Pencil extension',
29
- status: pencilInfo ? 'ok' : 'warn',
30
- message: pencilInfo
31
- ? `v${pencilInfo.version} (visual templates enabled)`
32
- : 'not found (optional install highagency.pencildev for visual templates)',
33
- });
24
+ // MCP integrations check
25
+ const { getConfiguredMcpIds } = await import('../mcp/validate.js');
26
+ const { loadMcpDefinition } = await import('../mcp/catalog.js');
27
+ const { detectMcp } = await import('../mcp/detect.js');
28
+ const configured = getConfiguredMcpIds(process.cwd());
29
+ const mcpsDir = path.resolve('mcps');
30
+ if (configured.length > 0) {
31
+ for (const id of configured) {
32
+ const def = loadMcpDefinition(mcpsDir, id);
33
+ if (def) {
34
+ const result = detectMcp(def);
35
+ checks.push({
36
+ name: `MCP: ${def.name}`,
37
+ status: result.status !== 'unavailable' ? 'ok' : 'warn',
38
+ message: result.detail,
39
+ });
40
+ }
41
+ }
42
+ }
43
+ else {
44
+ checks.push({
45
+ name: 'MCP integrations',
46
+ status: 'warn',
47
+ message: 'No MCPs configured. Run: expxagents mcp setup <id>',
48
+ });
49
+ }
34
50
  // Project structure
35
51
  const requiredDirs = ['squads', '_expxagents/_memory'];
36
52
  for (const dir of requiredDirs) {
@@ -3,7 +3,6 @@ import path from 'path';
3
3
  import crypto from 'crypto';
4
4
  import { execSync } from 'child_process';
5
5
  import { getTemplateDir, getAssetsDir } from '../utils/config.js';
6
- import { detectPencilExtension, resolvePlatformBinary, buildMcpConfig } from '../pencil/detect.js';
7
6
  function getDefaultTemplate(file) {
8
7
  if (file === 'company.md') {
9
8
  return `<!-- NOT CONFIGURED -->
@@ -331,6 +330,12 @@ export async function initCommand(options = {}) {
331
330
  else {
332
331
  console.log(' agents/ already exists (use --update to refresh)');
333
332
  }
333
+ // Copy MCP catalog
334
+ const mcpsSrc = path.join(assetsDir, 'mcps');
335
+ if (fs.existsSync(mcpsSrc)) {
336
+ const mcpsResult = copyDirRecursive(mcpsSrc, path.resolve('mcps'));
337
+ console.log(` 📡 MCP catalog: ${mcpsResult.changed}/${mcpsResult.total} files`);
338
+ }
334
339
  // Create/update Claude Code skill (.claude/skills/expxagents/SKILL.md)
335
340
  const skillDir = path.join(cwd, '.claude', 'skills', 'expxagents');
336
341
  const skillPath = path.join(skillDir, 'SKILL.md');
@@ -403,22 +408,37 @@ BRIDGE_TIMEOUT_MS=300000
403
408
  fs.writeFileSync(gitignorePath, gitignoreEntries.join('\n') + '\n', 'utf-8');
404
409
  console.log(' Created .gitignore');
405
410
  }
406
- // Generate .mcp.json for Pencil MCP (if Pencil extension is installed)
407
- const mcpJsonPath = path.join(cwd, '.mcp.json');
408
- if (!fs.existsSync(mcpJsonPath)) {
409
- const pencilInfo = detectPencilExtension();
410
- if (pencilInfo) {
411
- const binaryName = resolvePlatformBinary();
412
- const mcpConfig = buildMcpConfig(pencilInfo, binaryName);
413
- fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n', 'utf-8');
414
- console.log(` Created .mcp.json (Pencil ${pencilInfo.version} detected)`);
411
+ // MCP Setup
412
+ const mcpsDir = path.resolve('mcps');
413
+ if (fs.existsSync(mcpsDir)) {
414
+ const { listAllMcpIds, loadMcpDefinition } = await import('../mcp/catalog.js');
415
+ const { detectMcp, detectExtension, resolvePlatformBinary } = await import('../mcp/detect.js');
416
+ const { writeMcpConfig } = await import('../mcp/setup.js');
417
+ const ids = listAllMcpIds(mcpsDir);
418
+ const results = ids.map(id => {
419
+ const def = loadMcpDefinition(mcpsDir, id);
420
+ return def ? { def, detection: detectMcp(def) } : null;
421
+ }).filter((r) => r !== null);
422
+ console.log('\n🔍 Detecting available MCP integrations...\n');
423
+ for (const { def, detection } of results) {
424
+ const icon = detection.status === 'detected' ? '✅' : detection.status === 'available' ? '⬜' : '⛔';
425
+ console.log(` ${icon} ${def.id.padEnd(14)} — ${detection.detail}`);
415
426
  }
416
- else {
417
- console.log(' Pencil extension not found skipping .mcp.json (install highagency.pencildev for visual templates)');
427
+ // Auto-configure extension-based MCPs (like Pencil) that need no auth
428
+ for (const { def, detection } of results) {
429
+ if (detection.status === 'detected' && def.detection.method === 'extension' && def.auth.length === 0) {
430
+ if (def.detection.extension) {
431
+ const ext = detectExtension(def.detection.extension.prefix);
432
+ if (ext) {
433
+ const binary = resolvePlatformBinary();
434
+ const resolvedCmd = path.join(ext.extensionPath, def.detection.extension.binary_dir, binary);
435
+ writeMcpConfig(process.cwd(), def, {}, resolvedCmd);
436
+ console.log(` → ${def.name} auto-configured`);
437
+ }
438
+ }
439
+ }
418
440
  }
419
- }
420
- else {
421
- console.log(' .mcp.json already exists');
441
+ console.log('\n Run: expxagents mcp setup <id> to configure additional MCPs\n');
422
442
  }
423
443
  // Install Python dependencies
424
444
  const pythonDeps = ['aiohttp', 'aiofiles', 'python-dotenv'];
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function mcpCommand(): Command;
@@ -0,0 +1,155 @@
1
+ import { Command } from 'commander';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import yaml from 'js-yaml';
5
+ import { loadMcpDefinition, listAllMcpIds } from '../mcp/catalog.js';
6
+ import { detectMcp, detectExtension, resolvePlatformBinary } from '../mcp/detect.js';
7
+ import { writeMcpConfig, removeMcpConfig } from '../mcp/setup.js';
8
+ import { getConfiguredMcpIds } from '../mcp/validate.js';
9
+ import { getAssetsDir } from '../utils/config.js';
10
+ function getMcpsDir() {
11
+ const local = path.resolve('mcps');
12
+ if (fs.existsSync(local))
13
+ return local;
14
+ return path.join(getAssetsDir(), 'mcps');
15
+ }
16
+ export function mcpCommand() {
17
+ const mcp = new Command('mcp')
18
+ .description('Manage MCP integrations');
19
+ mcp
20
+ .command('list')
21
+ .description('List all MCPs: configured, available, unavailable')
22
+ .action(() => {
23
+ const mcpsDir = getMcpsDir();
24
+ const ids = listAllMcpIds(mcpsDir);
25
+ const configured = getConfiguredMcpIds(process.cwd());
26
+ const results = [];
27
+ for (const id of ids) {
28
+ const def = loadMcpDefinition(mcpsDir, id);
29
+ if (def)
30
+ results.push(detectMcp(def));
31
+ }
32
+ console.log('\nMCP Integrations:\n');
33
+ const configuredResults = results.filter(r => configured.includes(r.id));
34
+ if (configuredResults.length > 0) {
35
+ console.log(' CONFIGURED');
36
+ for (const r of configuredResults) {
37
+ console.log(` \u2705 ${r.id.padEnd(14)} \u2014 ${r.detail}`);
38
+ }
39
+ console.log('');
40
+ }
41
+ const available = results.filter(r => !configured.includes(r.id) && r.status !== 'unavailable');
42
+ if (available.length > 0) {
43
+ console.log(' AVAILABLE');
44
+ for (const r of available) {
45
+ console.log(` \u2b1c ${r.id.padEnd(14)} \u2014 ${r.detail}`);
46
+ }
47
+ console.log('');
48
+ }
49
+ const unavailable = results.filter(r => r.status === 'unavailable' && !configured.includes(r.id));
50
+ if (unavailable.length > 0) {
51
+ console.log(' UNAVAILABLE');
52
+ for (const r of unavailable) {
53
+ console.log(` \u26d4 ${r.id.padEnd(14)} \u2014 ${r.detail}`);
54
+ }
55
+ console.log('');
56
+ }
57
+ console.log('Run: expxagents mcp setup <id> to configure\n');
58
+ });
59
+ mcp
60
+ .command('setup <ids...>')
61
+ .description('Configure one or more MCPs')
62
+ .action(async (ids) => {
63
+ const mcpsDir = getMcpsDir();
64
+ const readline = await import('readline');
65
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
66
+ const ask = (q) => new Promise(r => rl.question(q, r));
67
+ for (const id of ids) {
68
+ const def = loadMcpDefinition(mcpsDir, id);
69
+ if (!def) {
70
+ console.log(`\u274c Unknown MCP: ${id}`);
71
+ continue;
72
+ }
73
+ console.log(`\n\ud83d\udce6 Setting up ${def.name} MCP...\n`);
74
+ let resolvedCommand;
75
+ if (def.detection.method === 'extension' && def.detection.extension) {
76
+ const ext = detectExtension(def.detection.extension.prefix);
77
+ if (!ext) {
78
+ console.log(`\u274c ${def.name} VSCode extension not found`);
79
+ continue;
80
+ }
81
+ const binary = resolvePlatformBinary();
82
+ resolvedCommand = path.join(ext.extensionPath, def.detection.extension.binary_dir, binary);
83
+ }
84
+ const authValues = {};
85
+ for (const auth of def.auth) {
86
+ console.log(` ${auth.hint}`);
87
+ const value = await ask(`? ${auth.label}: `);
88
+ authValues[auth.key] = value.trim();
89
+ }
90
+ writeMcpConfig(process.cwd(), def, authValues, resolvedCommand);
91
+ console.log(`\n\u2705 ${def.name} MCP configured\n`);
92
+ }
93
+ rl.close();
94
+ });
95
+ mcp
96
+ .command('remove <id>')
97
+ .description('Remove an MCP from .mcp.json and .env')
98
+ .option('-f, --force', 'Skip squad dependency check')
99
+ .action((id, options) => {
100
+ const configured = getConfiguredMcpIds(process.cwd());
101
+ if (!configured.includes(id)) {
102
+ console.log(`\u274c MCP "${id}" is not configured`);
103
+ return;
104
+ }
105
+ if (!options.force) {
106
+ const squadsDir = path.resolve('squads');
107
+ if (fs.existsSync(squadsDir)) {
108
+ const dependentSquads = [];
109
+ for (const entry of fs.readdirSync(squadsDir)) {
110
+ const yamlPath = path.join(squadsDir, entry, 'squad.yaml');
111
+ if (!fs.existsSync(yamlPath))
112
+ continue;
113
+ try {
114
+ const raw = yaml.load(fs.readFileSync(yamlPath, 'utf-8'));
115
+ const mcps = raw.squad?.mcps;
116
+ if (mcps?.includes(id))
117
+ dependentSquads.push(entry);
118
+ }
119
+ catch { /* skip */ }
120
+ }
121
+ if (dependentSquads.length > 0) {
122
+ console.log(`\u26a0\ufe0f Squads that depend on "${id}": ${dependentSquads.join(', ')}`);
123
+ console.log(` Use --force to remove anyway`);
124
+ return;
125
+ }
126
+ }
127
+ }
128
+ removeMcpConfig(process.cwd(), id);
129
+ console.log(`\u2705 Removed MCP: ${id}`);
130
+ });
131
+ mcp
132
+ .command('check [id]')
133
+ .description('Validate configured MCP connections')
134
+ .action((id) => {
135
+ const configured = getConfiguredMcpIds(process.cwd());
136
+ const toCheck = id ? [id] : configured;
137
+ let issues = 0;
138
+ console.log('\nChecking MCP connections...\n');
139
+ for (const mcpId of toCheck) {
140
+ if (!configured.includes(mcpId)) {
141
+ console.log(` \u274c ${mcpId.padEnd(14)} \u2014 Not configured`);
142
+ issues++;
143
+ continue;
144
+ }
145
+ console.log(` \u2705 ${mcpId.padEnd(14)} \u2014 Configured`);
146
+ }
147
+ if (issues > 0) {
148
+ console.log(`\n${issues} issue(s) found.\n`);
149
+ }
150
+ else {
151
+ console.log(`\nAll MCPs OK.\n`);
152
+ }
153
+ });
154
+ return mcp;
155
+ }
@@ -15,6 +15,7 @@ import { jarvisCommand } from './commands/jarvis.js';
15
15
  import { schedulerCommand } from './commands/scheduler.js';
16
16
  import { reorganizeCommand } from './commands/reorganize.js';
17
17
  import { syncTemplatesCommand } from './commands/sync-templates.js';
18
+ import { mcpCommand } from './commands/mcp.js';
18
19
  import { ingestCommand } from './commands/ingest.js';
19
20
  import { knowledgeCommand } from './commands/knowledge.js';
20
21
  import { loginCommand } from './commands/login.js';
@@ -99,6 +100,7 @@ program
99
100
  .description('Open Jarvis voice assistant in browser')
100
101
  .action(jarvisCommand);
101
102
  program.addCommand(schedulerCommand());
103
+ program.addCommand(mcpCommand());
102
104
  program
103
105
  .command('ingest <path>')
104
106
  .description('Ingest documents into the knowledge base')
@@ -0,0 +1 @@
1
+ export {};