expxagents 0.19.0 → 0.20.1
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/assets/mcps/_catalog.yaml +17 -0
- package/assets/mcps/figma.mcp.yaml +35 -0
- package/assets/mcps/github.mcp.yaml +44 -0
- package/assets/mcps/linear.mcp.yaml +37 -0
- package/assets/mcps/notion.mcp.yaml +37 -0
- package/assets/mcps/pencil.mcp.yaml +32 -0
- package/assets/mcps/postgresql.mcp.yaml +39 -0
- package/assets/mcps/sentry.mcp.yaml +41 -0
- package/assets/mcps/slack.mcp.yaml +37 -0
- package/assets/mcps/vercel.mcp.yaml +39 -0
- package/dist/cli/src/commands/doctor.js +26 -10
- package/dist/cli/src/commands/info.js +1 -1
- package/dist/cli/src/commands/init.js +35 -15
- package/dist/cli/src/commands/login.js +1 -1
- package/dist/cli/src/commands/mcp.d.ts +2 -0
- package/dist/cli/src/commands/mcp.js +155 -0
- package/dist/cli/src/commands/registry-install.js +1 -1
- package/dist/cli/src/commands/search.js +1 -1
- package/dist/cli/src/index.js +2 -0
- package/dist/cli/src/mcp/__tests__/catalog.test.d.ts +1 -0
- package/dist/cli/src/mcp/__tests__/catalog.test.js +101 -0
- package/dist/cli/src/mcp/__tests__/detect.test.d.ts +1 -0
- package/dist/cli/src/mcp/__tests__/detect.test.js +84 -0
- package/dist/cli/src/mcp/__tests__/setup.test.d.ts +1 -0
- package/dist/cli/src/mcp/__tests__/setup.test.js +75 -0
- package/dist/cli/src/mcp/__tests__/validate.test.d.ts +1 -0
- package/dist/cli/src/mcp/__tests__/validate.test.js +42 -0
- package/dist/cli/src/mcp/catalog.d.ts +4 -0
- package/dist/cli/src/mcp/catalog.js +53 -0
- package/dist/cli/src/mcp/detect.d.ts +9 -0
- package/dist/cli/src/mcp/detect.js +75 -0
- package/dist/cli/src/mcp/setup.d.ts +4 -0
- package/dist/cli/src/mcp/setup.js +56 -0
- package/dist/cli/src/mcp/types.d.ts +68 -0
- package/dist/cli/src/mcp/types.js +1 -0
- package/dist/cli/src/mcp/validate.d.ts +2 -0
- package/dist/cli/src/mcp/validate.js +23 -0
- package/dist/cli/src/pencil/detect.d.ts +6 -11
- package/dist/cli/src/pencil/detect.js +8 -39
- package/dist/core/squad-loader.d.ts +1 -0
- package/dist/core/squad-loader.js +2 -0
- package/dist/server/api/registry-routes.js +1 -1
- package/dist/server/api/registry-routes.js.map +1 -1
- 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
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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) {
|
|
@@ -6,7 +6,7 @@ export const infoCommand = new Command('info')
|
|
|
6
6
|
.action(async (name) => {
|
|
7
7
|
const creds = readCredentials();
|
|
8
8
|
const client = new RegistryClient({
|
|
9
|
-
registryUrl: creds?.registryUrl || 'https://
|
|
9
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
10
10
|
});
|
|
11
11
|
try {
|
|
12
12
|
const squad = await client.getSquad(name);
|
|
@@ -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
|
-
//
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
417
|
-
|
|
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'];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { RegistryClient, writeCredentials } from '@expxagents/registry-client';
|
|
3
|
-
const DEFAULT_REGISTRY = 'https://
|
|
3
|
+
const DEFAULT_REGISTRY = 'https://expxagents-marketplace-production.up.railway.app';
|
|
4
4
|
export const loginCommand = new Command('login')
|
|
5
5
|
.description('Authenticate with the ExpxAgents registry')
|
|
6
6
|
.option('--registry <url>', 'Registry URL', DEFAULT_REGISTRY)
|
|
@@ -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
|
+
}
|
|
@@ -9,7 +9,7 @@ export const registryInstallCommand = new Command('add')
|
|
|
9
9
|
.action(async (name, options) => {
|
|
10
10
|
const creds = readCredentials();
|
|
11
11
|
const client = new RegistryClient({
|
|
12
|
-
registryUrl: creds?.registryUrl || 'https://
|
|
12
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
13
13
|
apiKey: creds?.apiKey,
|
|
14
14
|
});
|
|
15
15
|
const targetDir = resolve(options.dir);
|