@geraldmaron/construct 1.0.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.
- package/.env.example +69 -0
- package/LICENSE +21 -0
- package/README.md +344 -0
- package/agents/prompts/cx-accessibility.md +29 -0
- package/agents/prompts/cx-ai-engineer.md +31 -0
- package/agents/prompts/cx-architect.md +40 -0
- package/agents/prompts/cx-business-strategist.md +25 -0
- package/agents/prompts/cx-data-analyst.md +30 -0
- package/agents/prompts/cx-data-engineer.md +32 -0
- package/agents/prompts/cx-debugger.md +27 -0
- package/agents/prompts/cx-designer.md +33 -0
- package/agents/prompts/cx-devil-advocate.md +36 -0
- package/agents/prompts/cx-docs-keeper.md +50 -0
- package/agents/prompts/cx-engineer.md +36 -0
- package/agents/prompts/cx-evaluator.md +26 -0
- package/agents/prompts/cx-explorer.md +38 -0
- package/agents/prompts/cx-legal-compliance.md +25 -0
- package/agents/prompts/cx-operations.md +28 -0
- package/agents/prompts/cx-orchestrator.md +30 -0
- package/agents/prompts/cx-platform-engineer.md +25 -0
- package/agents/prompts/cx-product-manager.md +30 -0
- package/agents/prompts/cx-qa.md +42 -0
- package/agents/prompts/cx-rd-lead.md +25 -0
- package/agents/prompts/cx-release-manager.md +31 -0
- package/agents/prompts/cx-researcher.md +31 -0
- package/agents/prompts/cx-reviewer.md +36 -0
- package/agents/prompts/cx-security.md +37 -0
- package/agents/prompts/cx-sre.md +26 -0
- package/agents/prompts/cx-test-automation.md +35 -0
- package/agents/prompts/cx-trace-reviewer.md +76 -0
- package/agents/prompts/cx-ux-researcher.md +30 -0
- package/agents/registry.json +879 -0
- package/agents/teams.json +94 -0
- package/bin/construct +613 -0
- package/commands/build/feature.md +21 -0
- package/commands/build/fix.md +20 -0
- package/commands/design/access.md +19 -0
- package/commands/design/flow.md +16 -0
- package/commands/design/ui.md +18 -0
- package/commands/measure/experiment.md +18 -0
- package/commands/measure/metrics.md +18 -0
- package/commands/measure/results.md +18 -0
- package/commands/plan/api.md +19 -0
- package/commands/plan/challenge.md +18 -0
- package/commands/plan/decide.md +20 -0
- package/commands/plan/feature.md +49 -0
- package/commands/plan/requirements.md +24 -0
- package/commands/remember/context.md +26 -0
- package/commands/remember/handoff.md +19 -0
- package/commands/remember/runbook.md +20 -0
- package/commands/review/code.md +21 -0
- package/commands/review/quality.md +19 -0
- package/commands/review/security.md +16 -0
- package/commands/ship/ready.md +19 -0
- package/commands/ship/release.md +19 -0
- package/commands/ship/status.md +18 -0
- package/commands/understand/docs.md +22 -0
- package/commands/understand/research.md +22 -0
- package/commands/understand/this.md +23 -0
- package/commands/understand/why.md +18 -0
- package/commands/work/clean.md +32 -0
- package/commands/work/drive.md +69 -0
- package/commands/work/optimize-prompts.md +46 -0
- package/commands/work/parallel-review.md +40 -0
- package/db/migrations/001_init.sql +38 -0
- package/langfuse/docker-compose.yml +82 -0
- package/lib/audit-skills.mjs +118 -0
- package/lib/auto-docs.mjs +293 -0
- package/lib/cli-commands.mjs +409 -0
- package/lib/codex-config.mjs +114 -0
- package/lib/comment-lint.mjs +191 -0
- package/lib/completions.mjs +170 -0
- package/lib/context-state.mjs +99 -0
- package/lib/cost.mjs +213 -0
- package/lib/distill.mjs +403 -0
- package/lib/efficiency.mjs +139 -0
- package/lib/env-config.mjs +53 -0
- package/lib/eval-harness.mjs +59 -0
- package/lib/features.mjs +209 -0
- package/lib/headhunt.mjs +597 -0
- package/lib/hooks/adaptive-lint.mjs +112 -0
- package/lib/hooks/agent-tracker.mjs +50 -0
- package/lib/hooks/bootstrap-guard.mjs +90 -0
- package/lib/hooks/comment-lint.mjs +26 -0
- package/lib/hooks/config-protection.mjs +52 -0
- package/lib/hooks/console-warn.mjs +43 -0
- package/lib/hooks/context-window-recovery.mjs +90 -0
- package/lib/hooks/continuation-enforcer.mjs +72 -0
- package/lib/hooks/dep-audit.mjs +155 -0
- package/lib/hooks/drive-guard.mjs +89 -0
- package/lib/hooks/edit-accumulator.mjs +36 -0
- package/lib/hooks/edit-error-recovery.mjs +46 -0
- package/lib/hooks/edit-guard.mjs +109 -0
- package/lib/hooks/env-check.mjs +80 -0
- package/lib/hooks/guard-bash.mjs +83 -0
- package/lib/hooks/mcp-audit.mjs +57 -0
- package/lib/hooks/mcp-health-check.mjs +51 -0
- package/lib/hooks/mcp-task-scope.mjs +47 -0
- package/lib/hooks/model-fallback.mjs +105 -0
- package/lib/hooks/pre-compact.mjs +212 -0
- package/lib/hooks/pre-push-gate.mjs +129 -0
- package/lib/hooks/read-tracker.mjs +129 -0
- package/lib/hooks/registry-sync.mjs +23 -0
- package/lib/hooks/scan-secrets.mjs +76 -0
- package/lib/hooks/session-start.mjs +95 -0
- package/lib/hooks/stop-notify.mjs +191 -0
- package/lib/hooks/stop-typecheck.mjs +83 -0
- package/lib/hooks/task-completed-guard.mjs +43 -0
- package/lib/hooks/teammate-idle-guard.mjs +54 -0
- package/lib/hooks/workflow-guard.mjs +62 -0
- package/lib/host-capabilities.mjs +123 -0
- package/lib/init-docs.mjs +382 -0
- package/lib/mcp/server.mjs +1123 -0
- package/lib/mcp-catalog.json +243 -0
- package/lib/mcp-manager.mjs +433 -0
- package/lib/mcp-platform-config.mjs +104 -0
- package/lib/model-router.mjs +810 -0
- package/lib/opencode-config.mjs +44 -0
- package/lib/opencode-runtime-plugin.mjs +909 -0
- package/lib/opencode-telemetry.mjs +433 -0
- package/lib/orchestration-policy.mjs +258 -0
- package/lib/prompt-composer.mjs +196 -0
- package/lib/prompt-metadata.mjs +68 -0
- package/lib/review.mjs +429 -0
- package/lib/role-preload.mjs +52 -0
- package/lib/schemas/decision.json +50 -0
- package/lib/schemas/implementation.json +51 -0
- package/lib/schemas/review-report.json +32 -0
- package/lib/schemas/test-report.json +43 -0
- package/lib/server/index.mjs +334 -0
- package/lib/server/static/app.js +841 -0
- package/lib/server/static/index.html +820 -0
- package/lib/service-manager.mjs +225 -0
- package/lib/setup.mjs +319 -0
- package/lib/status.mjs +707 -0
- package/lib/storage/backend.mjs +44 -0
- package/lib/storage/embeddings.mjs +70 -0
- package/lib/storage/hybrid-query.mjs +184 -0
- package/lib/storage/sql-store.mjs +55 -0
- package/lib/storage/state-source.mjs +101 -0
- package/lib/storage/sync.mjs +164 -0
- package/lib/storage/vector-store.mjs +59 -0
- package/lib/telemetry/backends/langfuse.mjs +58 -0
- package/lib/telemetry/backends/noop.mjs +16 -0
- package/lib/telemetry/langfuse-ingest.mjs +108 -0
- package/lib/telemetry/langfuse-model-sync.mjs +270 -0
- package/lib/telemetry/team-rollup.mjs +143 -0
- package/lib/toolkit-env.mjs +30 -0
- package/lib/validator.mjs +181 -0
- package/lib/workflow-state.mjs +794 -0
- package/package.json +53 -0
- package/personas/construct.md +94 -0
- package/platforms/claude/CLAUDE.md +30 -0
- package/platforms/claude/settings.template.json +472 -0
- package/platforms/opencode/config.template.json +65 -0
- package/platforms/opencode/plugins/construct-fallback.js +5 -0
- package/platforms/opencode/sync-config.mjs +69 -0
- package/rules/common/agents.md +55 -0
- package/rules/common/code-review.md +129 -0
- package/rules/common/coding-style.md +95 -0
- package/rules/common/comments.md +139 -0
- package/rules/common/cx-agent-routing.md +61 -0
- package/rules/common/cx-skill-routing.md +11 -0
- package/rules/common/development-workflow.md +49 -0
- package/rules/common/git-workflow.md +29 -0
- package/rules/common/hooks.md +35 -0
- package/rules/common/patterns.md +36 -0
- package/rules/common/performance.md +71 -0
- package/rules/common/security.md +34 -0
- package/rules/common/testing.md +62 -0
- package/rules/golang/coding-style.md +37 -0
- package/rules/golang/hooks.md +22 -0
- package/rules/golang/patterns.md +50 -0
- package/rules/golang/security.md +39 -0
- package/rules/golang/testing.md +36 -0
- package/rules/python/coding-style.md +47 -0
- package/rules/python/hooks.md +24 -0
- package/rules/python/patterns.md +44 -0
- package/rules/python/security.md +35 -0
- package/rules/python/testing.md +43 -0
- package/rules/swift/coding-style.md +52 -0
- package/rules/swift/hooks.md +25 -0
- package/rules/swift/patterns.md +71 -0
- package/rules/swift/security.md +38 -0
- package/rules/swift/testing.md +50 -0
- package/rules/typescript/coding-style.md +204 -0
- package/rules/typescript/hooks.md +27 -0
- package/rules/typescript/patterns.md +57 -0
- package/rules/typescript/security.md +33 -0
- package/rules/typescript/testing.md +23 -0
- package/rules/web/coding-style.md +101 -0
- package/rules/web/design-quality.md +68 -0
- package/rules/web/hooks.md +125 -0
- package/rules/web/patterns.md +84 -0
- package/rules/web/performance.md +69 -0
- package/rules/web/security.md +62 -0
- package/rules/web/testing.md +60 -0
- package/rules/zh/README.md +113 -0
- package/rules/zh/agents.md +55 -0
- package/rules/zh/code-review.md +129 -0
- package/rules/zh/coding-style.md +53 -0
- package/rules/zh/development-workflow.md +49 -0
- package/rules/zh/git-workflow.md +29 -0
- package/rules/zh/hooks.md +35 -0
- package/rules/zh/patterns.md +36 -0
- package/rules/zh/performance.md +60 -0
- package/rules/zh/security.md +34 -0
- package/rules/zh/testing.md +34 -0
- package/skills/ai/agent-dev.md +102 -0
- package/skills/ai/llm-security.md +105 -0
- package/skills/ai/ml-ops.md +169 -0
- package/skills/ai/orchestration-workflow.md +87 -0
- package/skills/ai/prompt-and-eval.md +114 -0
- package/skills/ai/prompt-optimizer.md +114 -0
- package/skills/ai/rag-system.md +104 -0
- package/skills/architecture/api-design.md +100 -0
- package/skills/architecture/caching.md +101 -0
- package/skills/architecture/cloud-native.md +97 -0
- package/skills/architecture/message-queue.md +100 -0
- package/skills/architecture/security-arch.md +105 -0
- package/skills/development/cpp.md +127 -0
- package/skills/development/go.md +129 -0
- package/skills/development/java.md +134 -0
- package/skills/development/kotlin.md +140 -0
- package/skills/development/mobile-crossplatform.md +144 -0
- package/skills/development/python.md +109 -0
- package/skills/development/rust.md +127 -0
- package/skills/development/shell.md +127 -0
- package/skills/development/swift.md +129 -0
- package/skills/development/typescript.md +129 -0
- package/skills/devops/ci-cd.md +108 -0
- package/skills/devops/containerization.md +106 -0
- package/skills/devops/cost-optimization.md +105 -0
- package/skills/devops/data-engineering.md +181 -0
- package/skills/devops/database.md +95 -0
- package/skills/devops/dependency-management.md +91 -0
- package/skills/devops/devsecops.md +96 -0
- package/skills/devops/git-workflow.md +100 -0
- package/skills/devops/incident-response.md +167 -0
- package/skills/devops/monorepo.md +87 -0
- package/skills/devops/observability.md +103 -0
- package/skills/devops/performance.md +104 -0
- package/skills/devops/testing.md +104 -0
- package/skills/docs/adr-workflow.md +32 -0
- package/skills/docs/backlog-proposal-workflow.md +19 -0
- package/skills/docs/customer-profile-workflow.md +22 -0
- package/skills/docs/evidence-ingest-workflow.md +23 -0
- package/skills/docs/init-docs.md +160 -0
- package/skills/docs/init-project.md +50 -0
- package/skills/docs/prd-workflow.md +57 -0
- package/skills/docs/prfaq-workflow.md +25 -0
- package/skills/docs/product-intelligence-review.md +22 -0
- package/skills/docs/product-intelligence-workflow.md +62 -0
- package/skills/docs/product-signal-workflow.md +27 -0
- package/skills/docs/research-workflow.md +28 -0
- package/skills/docs/runbook-workflow.md +24 -0
- package/skills/exploration/repo-map.md +297 -0
- package/skills/frameworks/django.md +159 -0
- package/skills/frameworks/nextjs.md +148 -0
- package/skills/frameworks/react.md +132 -0
- package/skills/frameworks/spring-boot.md +165 -0
- package/skills/frontend-design/accessibility.md +153 -0
- package/skills/frontend-design/component-patterns.md +111 -0
- package/skills/frontend-design/engineering.md +122 -0
- package/skills/frontend-design/state-management.md +118 -0
- package/skills/frontend-design/ui-aesthetics.md +102 -0
- package/skills/frontend-design/ux-principles.md +126 -0
- package/skills/quality-gates/review-work.md +90 -0
- package/skills/quality-gates/verify-change.md +94 -0
- package/skills/quality-gates/verify-module.md +96 -0
- package/skills/quality-gates/verify-quality.md +93 -0
- package/skills/quality-gates/verify-security.md +95 -0
- package/skills/roles/architect.ai-systems.md +37 -0
- package/skills/roles/architect.data.md +37 -0
- package/skills/roles/architect.enterprise.md +37 -0
- package/skills/roles/architect.integration.md +37 -0
- package/skills/roles/architect.md +68 -0
- package/skills/roles/architect.platform.md +37 -0
- package/skills/roles/data-analyst.experiment.md +37 -0
- package/skills/roles/data-analyst.md +68 -0
- package/skills/roles/data-analyst.product-intelligence.md +37 -0
- package/skills/roles/data-analyst.product.md +37 -0
- package/skills/roles/data-analyst.telemetry.md +37 -0
- package/skills/roles/data-engineer.pipeline.md +37 -0
- package/skills/roles/data-engineer.vector-retrieval.md +37 -0
- package/skills/roles/data-engineer.warehouse.md +37 -0
- package/skills/roles/debugger.md +68 -0
- package/skills/roles/designer.accessibility.md +43 -0
- package/skills/roles/designer.md +68 -0
- package/skills/roles/engineer.ai.md +49 -0
- package/skills/roles/engineer.data.md +49 -0
- package/skills/roles/engineer.md +85 -0
- package/skills/roles/engineer.platform.md +49 -0
- package/skills/roles/operator.docs.md +43 -0
- package/skills/roles/operator.md +68 -0
- package/skills/roles/operator.release.md +43 -0
- package/skills/roles/operator.sre.md +43 -0
- package/skills/roles/orchestrator.md +66 -0
- package/skills/roles/product-manager.ai-product.md +37 -0
- package/skills/roles/product-manager.business-strategy.md +43 -0
- package/skills/roles/product-manager.enterprise.md +37 -0
- package/skills/roles/product-manager.growth.md +37 -0
- package/skills/roles/product-manager.md +67 -0
- package/skills/roles/product-manager.platform.md +37 -0
- package/skills/roles/product-manager.product.md +37 -0
- package/skills/roles/qa.ai-eval.md +37 -0
- package/skills/roles/qa.api-contract.md +37 -0
- package/skills/roles/qa.data-pipeline.md +37 -0
- package/skills/roles/qa.md +68 -0
- package/skills/roles/qa.test-automation.md +49 -0
- package/skills/roles/qa.web-ui.md +37 -0
- package/skills/roles/researcher.explorer.md +43 -0
- package/skills/roles/researcher.md +68 -0
- package/skills/roles/researcher.ux.md +43 -0
- package/skills/roles/reviewer.devil-advocate.md +43 -0
- package/skills/roles/reviewer.evaluator.md +43 -0
- package/skills/roles/reviewer.md +68 -0
- package/skills/roles/reviewer.trace.md +43 -0
- package/skills/roles/security.ai.md +37 -0
- package/skills/roles/security.appsec.md +37 -0
- package/skills/roles/security.cloud.md +37 -0
- package/skills/roles/security.legal-compliance.md +49 -0
- package/skills/roles/security.md +68 -0
- package/skills/roles/security.privacy.md +37 -0
- package/skills/roles/security.supply-chain.md +37 -0
- package/skills/routing.md +139 -0
- package/skills/security/blue-team.md +83 -0
- package/skills/security/code-audit.md +94 -0
- package/skills/security/pentest.md +84 -0
- package/skills/security/red-team.md +81 -0
- package/skills/security/threat-intel.md +89 -0
- package/skills/security/vuln-research.md +87 -0
- package/skills/utility/clean-code.md +165 -0
- package/sync-agents.mjs +899 -0
- package/templates/docs/adr.md +27 -0
- package/templates/docs/backlog-proposal.md +26 -0
- package/templates/docs/customer-profile.md +34 -0
- package/templates/docs/evidence-brief.md +37 -0
- package/templates/docs/incident-report.md +46 -0
- package/templates/docs/memo.md +27 -0
- package/templates/docs/meta-prd.md +56 -0
- package/templates/docs/one-pager.md +24 -0
- package/templates/docs/prd-business.md +61 -0
- package/templates/docs/prd-platform.md +62 -0
- package/templates/docs/prd.md +47 -0
- package/templates/docs/prfaq.md +31 -0
- package/templates/docs/product-intelligence-report.md +31 -0
- package/templates/docs/research-brief.md +29 -0
- package/templates/docs/rfc-platform.md +60 -0
- package/templates/docs/rfc.md +46 -0
- package/templates/docs/runbook.md +33 -0
- package/templates/docs/signal-brief.md +25 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"mcps": [
|
|
4
|
+
{
|
|
5
|
+
"id": "context7",
|
|
6
|
+
"name": "Context7",
|
|
7
|
+
"category": "core",
|
|
8
|
+
"description": "Library and framework documentation lookup. Agents use this before writing code against any dependency.",
|
|
9
|
+
"package": "@upstash/context7-mcp@latest",
|
|
10
|
+
"command": "npx",
|
|
11
|
+
"args": [
|
|
12
|
+
"-y",
|
|
13
|
+
"@upstash/context7-mcp@latest"
|
|
14
|
+
],
|
|
15
|
+
"env": {},
|
|
16
|
+
"requiredEnv": [],
|
|
17
|
+
"usedBy": [
|
|
18
|
+
"cx-docs-researcher",
|
|
19
|
+
"cx-engineer",
|
|
20
|
+
"cx-architect"
|
|
21
|
+
],
|
|
22
|
+
"degradedMessage": "No live docs lookup. Agents rely on training data which may be outdated."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "sequential-thinking",
|
|
26
|
+
"name": "Sequential Thinking",
|
|
27
|
+
"category": "core",
|
|
28
|
+
"description": "Structured multi-step reasoning chains. Helps with complex planning and analysis.",
|
|
29
|
+
"package": "@modelcontextprotocol/server-sequential-thinking",
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": [
|
|
32
|
+
"-y",
|
|
33
|
+
"@modelcontextprotocol/server-sequential-thinking"
|
|
34
|
+
],
|
|
35
|
+
"env": {},
|
|
36
|
+
"requiredEnv": [],
|
|
37
|
+
"usedBy": [
|
|
38
|
+
"construct",
|
|
39
|
+
"cx-architect",
|
|
40
|
+
"cx-researcher"
|
|
41
|
+
],
|
|
42
|
+
"degradedMessage": "Complex reasoning chains unavailable. Agents still work but may skip structured thinking steps."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "memory",
|
|
46
|
+
"name": "Memory",
|
|
47
|
+
"category": "optional",
|
|
48
|
+
"description": "Persistent cross-session knowledge graph. Agents remember decisions and findings across sessions.",
|
|
49
|
+
"type": "url",
|
|
50
|
+
"url": "http://127.0.0.1:8765/",
|
|
51
|
+
"env": {},
|
|
52
|
+
"requiredEnv": [],
|
|
53
|
+
"setupModes": [
|
|
54
|
+
"auto",
|
|
55
|
+
"manual"
|
|
56
|
+
],
|
|
57
|
+
"usedBy": [
|
|
58
|
+
"construct",
|
|
59
|
+
"cx-docs-keeper",
|
|
60
|
+
"cx-researcher"
|
|
61
|
+
],
|
|
62
|
+
"degradedMessage": "No cross-session memory. Each session starts fresh with no prior context."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "playwright",
|
|
66
|
+
"name": "Playwright",
|
|
67
|
+
"category": "optional",
|
|
68
|
+
"description": "Browser automation for E2E testing and web research.",
|
|
69
|
+
"package": "@playwright/mcp@latest",
|
|
70
|
+
"command": "npx",
|
|
71
|
+
"args": [
|
|
72
|
+
"-y",
|
|
73
|
+
"@playwright/mcp@latest"
|
|
74
|
+
],
|
|
75
|
+
"env": {},
|
|
76
|
+
"requiredEnv": [],
|
|
77
|
+
"usedBy": [
|
|
78
|
+
"construct",
|
|
79
|
+
"cx-qa",
|
|
80
|
+
"cx-researcher"
|
|
81
|
+
],
|
|
82
|
+
"degradedMessage": "No browser automation. E2E tests and live web research unavailable."
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "github",
|
|
86
|
+
"name": "GitHub",
|
|
87
|
+
"category": "optional",
|
|
88
|
+
"description": "GitHub API: issues, PRs, code search, repository operations.",
|
|
89
|
+
"type": "url",
|
|
90
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
91
|
+
"headers": {
|
|
92
|
+
"Authorization": "Bearer __GITHUB_TOKEN__"
|
|
93
|
+
},
|
|
94
|
+
"env": {
|
|
95
|
+
"GITHUB_TOKEN": "__GITHUB_TOKEN__"
|
|
96
|
+
},
|
|
97
|
+
"requiredEnv": [],
|
|
98
|
+
"setupModes": [
|
|
99
|
+
"auto",
|
|
100
|
+
"manual"
|
|
101
|
+
],
|
|
102
|
+
"setupUrl": null,
|
|
103
|
+
"setupNote": "Run 'gh auth login' to authenticate via browser, then re-run 'construct mcp add github'. The token is sourced from the gh CLI automatically.",
|
|
104
|
+
"usedBy": [
|
|
105
|
+
"construct",
|
|
106
|
+
"cx-engineer",
|
|
107
|
+
"cx-reviewer",
|
|
108
|
+
"cx-release-manager"
|
|
109
|
+
],
|
|
110
|
+
"degradedMessage": "No GitHub API. Agents use gh CLI only — PR creation, issue tracking, code search limited."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "atlassian",
|
|
114
|
+
"name": "Atlassian (Jira + Confluence)",
|
|
115
|
+
"category": "integration",
|
|
116
|
+
"description": "Jira issue tracking and Confluence wiki access via Atlassian's official remote MCP server. OAuth is handled by each MCP client.",
|
|
117
|
+
"type": "url",
|
|
118
|
+
"url": "https://mcp.atlassian.com/v1/mcp",
|
|
119
|
+
"env": {},
|
|
120
|
+
"requiredEnv": [],
|
|
121
|
+
"setupModes": [
|
|
122
|
+
"auto"
|
|
123
|
+
],
|
|
124
|
+
"setupNote": "Uses Atlassian's official remote MCP server with OAuth 2.1. Browser auth prompt appears on first use. Requires a Rovo license on your Atlassian account.",
|
|
125
|
+
"usedBy": [
|
|
126
|
+
"cx-product-manager",
|
|
127
|
+
"cx-program-manager",
|
|
128
|
+
"cx-docs-keeper",
|
|
129
|
+
"cx-release-manager"
|
|
130
|
+
],
|
|
131
|
+
"degradedMessage": "No Jira/Confluence access. Issue tracking and wiki lookup must be done manually."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "linear",
|
|
135
|
+
"name": "Linear",
|
|
136
|
+
"category": "integration",
|
|
137
|
+
"description": "Linear issue tracking — create issues, update status, search projects.",
|
|
138
|
+
"package": "@linear/mcp-server",
|
|
139
|
+
"command": "npx",
|
|
140
|
+
"args": [
|
|
141
|
+
"-y",
|
|
142
|
+
"@linear/mcp-server"
|
|
143
|
+
],
|
|
144
|
+
"env": {
|
|
145
|
+
"LINEAR_API_KEY": "__LINEAR_API_KEY__"
|
|
146
|
+
},
|
|
147
|
+
"requiredEnv": [
|
|
148
|
+
"LINEAR_API_KEY"
|
|
149
|
+
],
|
|
150
|
+
"setupModes": [
|
|
151
|
+
"manual"
|
|
152
|
+
],
|
|
153
|
+
"setupUrl": "https://linear.app/settings/api",
|
|
154
|
+
"setupNote": "Generate a personal API key at linear.app/settings/api.",
|
|
155
|
+
"usedBy": [
|
|
156
|
+
"cx-product-manager",
|
|
157
|
+
"cx-program-manager",
|
|
158
|
+
"cx-operations"
|
|
159
|
+
],
|
|
160
|
+
"degradedMessage": "No Linear access. Issue creation and sprint tracking must be done manually."
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "slack",
|
|
164
|
+
"name": "Slack",
|
|
165
|
+
"category": "integration",
|
|
166
|
+
"description": "Slack messaging — send messages, read channels, search conversations.",
|
|
167
|
+
"package": "@modelcontextprotocol/server-slack",
|
|
168
|
+
"command": "npx",
|
|
169
|
+
"args": [
|
|
170
|
+
"-y",
|
|
171
|
+
"@modelcontextprotocol/server-slack"
|
|
172
|
+
],
|
|
173
|
+
"env": {
|
|
174
|
+
"SLACK_BOT_TOKEN": "__SLACK_BOT_TOKEN__",
|
|
175
|
+
"SLACK_TEAM_ID": "__SLACK_TEAM_ID__"
|
|
176
|
+
},
|
|
177
|
+
"requiredEnv": [
|
|
178
|
+
"SLACK_BOT_TOKEN",
|
|
179
|
+
"SLACK_TEAM_ID"
|
|
180
|
+
],
|
|
181
|
+
"setupModes": [
|
|
182
|
+
"manual"
|
|
183
|
+
],
|
|
184
|
+
"setupUrl": "https://api.slack.com/apps",
|
|
185
|
+
"setupNote": "Create a Slack app at api.slack.com/apps. Add Bot Token Scopes: channels:read, chat:write, channels:history.",
|
|
186
|
+
"usedBy": [
|
|
187
|
+
"construct",
|
|
188
|
+
"cx-release-manager",
|
|
189
|
+
"cx-sre"
|
|
190
|
+
],
|
|
191
|
+
"degradedMessage": "No Slack integration. Deployment notifications and team alerts must be sent manually."
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "notion",
|
|
195
|
+
"name": "Notion",
|
|
196
|
+
"category": "integration",
|
|
197
|
+
"description": "Notion workspace access — read/write pages, databases, and comments.",
|
|
198
|
+
"package": "@anthropic-ai/notion-mcp",
|
|
199
|
+
"command": "npx",
|
|
200
|
+
"args": [
|
|
201
|
+
"-y",
|
|
202
|
+
"@anthropic-ai/notion-mcp"
|
|
203
|
+
],
|
|
204
|
+
"env": {
|
|
205
|
+
"NOTION_API_TOKEN": "__NOTION_API_TOKEN__"
|
|
206
|
+
},
|
|
207
|
+
"requiredEnv": [
|
|
208
|
+
"NOTION_API_TOKEN"
|
|
209
|
+
],
|
|
210
|
+
"setupModes": [
|
|
211
|
+
"manual"
|
|
212
|
+
],
|
|
213
|
+
"setupUrl": "https://www.notion.so/profile/integrations",
|
|
214
|
+
"setupNote": "Create an integration at notion.so/profile/integrations. Share target pages/databases with the integration.",
|
|
215
|
+
"usedBy": [
|
|
216
|
+
"cx-docs-keeper",
|
|
217
|
+
"cx-product-manager",
|
|
218
|
+
"cx-program-manager"
|
|
219
|
+
],
|
|
220
|
+
"degradedMessage": "No Notion access. Documentation must be managed manually."
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"id": "filesystem",
|
|
224
|
+
"name": "Filesystem",
|
|
225
|
+
"category": "optional",
|
|
226
|
+
"description": "Extended file operations beyond built-in Read/Write/Edit tools.",
|
|
227
|
+
"package": "@modelcontextprotocol/server-filesystem",
|
|
228
|
+
"command": "npx",
|
|
229
|
+
"args": [
|
|
230
|
+
"-y",
|
|
231
|
+
"@modelcontextprotocol/server-filesystem",
|
|
232
|
+
"/"
|
|
233
|
+
],
|
|
234
|
+
"env": {},
|
|
235
|
+
"requiredEnv": [],
|
|
236
|
+
"usedBy": [
|
|
237
|
+
"cx-engineer",
|
|
238
|
+
"cx-explorer"
|
|
239
|
+
],
|
|
240
|
+
"degradedMessage": "Using built-in file tools only. Some advanced file operations unavailable."
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP manager — add, remove, list MCPs in ~/.claude/settings.json
|
|
3
|
+
* Uses mcp-catalog.json as the source of truth for known integrations.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { createInterface } from 'node:readline';
|
|
9
|
+
import { homedir, platform } from 'node:os';
|
|
10
|
+
import { join, dirname } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { readOpenCodeConfig, writeOpenCodeConfig } from './opencode-config.mjs';
|
|
13
|
+
import { getUserEnvPath, loadConstructEnv, writeEnvValues } from './env-config.mjs';
|
|
14
|
+
import {
|
|
15
|
+
buildCodexMcpEntry,
|
|
16
|
+
getCodexConfigPath,
|
|
17
|
+
readCodexConfig,
|
|
18
|
+
removeDanglingConstructMcpMarkers,
|
|
19
|
+
removeDanglingConstructMcpTimeouts,
|
|
20
|
+
removeTomlTables,
|
|
21
|
+
serializeCodexMcpTable,
|
|
22
|
+
tomlString,
|
|
23
|
+
writeCodexConfig,
|
|
24
|
+
} from './codex-config.mjs';
|
|
25
|
+
import {
|
|
26
|
+
buildClaudeMcpEntry,
|
|
27
|
+
buildOpenCodeMcpEntry,
|
|
28
|
+
getOpenCodeMcpId,
|
|
29
|
+
normalizeInstalledOpenCodeMcpId,
|
|
30
|
+
} from './mcp-platform-config.mjs';
|
|
31
|
+
|
|
32
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
|
|
34
|
+
const CATALOG_PATH = join(__dirname, 'mcp-catalog.json');
|
|
35
|
+
const CLAUDE_SETTINGS_PATH = join(homedir(), '.claude', 'settings.json');
|
|
36
|
+
|
|
37
|
+
function openUrl(url) {
|
|
38
|
+
try {
|
|
39
|
+
const opener = platform() === 'darwin' ? 'open' : platform() === 'win32' ? 'start' : 'xdg-open';
|
|
40
|
+
execSync(`${opener} "${url}"`, { stdio: 'ignore' });
|
|
41
|
+
} catch {
|
|
42
|
+
// best effort
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function loadCatalog() {
|
|
47
|
+
const catalog = JSON.parse(readFileSync(CATALOG_PATH, 'utf8'));
|
|
48
|
+
return {
|
|
49
|
+
...catalog,
|
|
50
|
+
mcps: (catalog.mcps ?? []).map(normalizeMcp),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeMcp(mcp) {
|
|
55
|
+
return {
|
|
56
|
+
...mcp,
|
|
57
|
+
args: Array.isArray(mcp.args) ? mcp.args : [],
|
|
58
|
+
env: mcp.env && typeof mcp.env === 'object' ? mcp.env : {},
|
|
59
|
+
requiredEnv: Array.isArray(mcp.requiredEnv) ? mcp.requiredEnv : [],
|
|
60
|
+
setupModes: Array.isArray(mcp.setupModes) ? mcp.setupModes : ['manual'],
|
|
61
|
+
usedBy: Array.isArray(mcp.usedBy) ? mcp.usedBy : [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function hasSetupMode(mcp, mode) {
|
|
66
|
+
return mcp.setupModes.includes(mode);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseAddFlags() {
|
|
70
|
+
const args = new Set(process.argv.slice(2));
|
|
71
|
+
return {
|
|
72
|
+
auto: args.has('--auto'),
|
|
73
|
+
manual: args.has('--manual'),
|
|
74
|
+
token: args.has('--token'),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function chooseSetupMode(mcp, flags) {
|
|
79
|
+
const canAuto = hasSetupMode(mcp, 'auto');
|
|
80
|
+
const canManual = hasSetupMode(mcp, 'manual');
|
|
81
|
+
|
|
82
|
+
if (!canAuto && !canManual) return 'manual';
|
|
83
|
+
if (flags.auto && canAuto) return 'auto';
|
|
84
|
+
if (flags.manual && canManual) return 'manual';
|
|
85
|
+
if (!canAuto) return 'manual';
|
|
86
|
+
if (!canManual) return 'auto';
|
|
87
|
+
if (!process.stdin.isTTY) return 'manual';
|
|
88
|
+
|
|
89
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
90
|
+
console.log('\nSetup mode:');
|
|
91
|
+
console.log(' 1) Auto-configure what Construct can detect or provision');
|
|
92
|
+
console.log(' 2) Manual setup (I will provide credentials/URLs myself)');
|
|
93
|
+
const answer = await prompt(rl, ' Choice [1/2]: ');
|
|
94
|
+
rl.close();
|
|
95
|
+
return answer.trim() === '2' ? 'manual' : 'auto';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getConfiguredEnvValue(name) {
|
|
99
|
+
const configuredEnv = loadConstructEnv({ env: process.env });
|
|
100
|
+
if (name === 'GITHUB_TOKEN') {
|
|
101
|
+
return configuredEnv.GITHUB_TOKEN || configuredEnv.GITHUB_PERSONAL_ACCESS_TOKEN || '';
|
|
102
|
+
}
|
|
103
|
+
return configuredEnv[name] || '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getTemplateVariableNames(input) {
|
|
107
|
+
if (!input || typeof input !== 'object') return [];
|
|
108
|
+
const names = new Set();
|
|
109
|
+
for (const value of Object.values(input)) {
|
|
110
|
+
if (typeof value !== 'string') continue;
|
|
111
|
+
for (const match of value.matchAll(/__([A-Z0-9_]+)__/g)) {
|
|
112
|
+
names.add(match[1]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return Array.from(names);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getConfiguredEnvValues(mcp) {
|
|
119
|
+
const names = new Set([
|
|
120
|
+
...mcp.requiredEnv,
|
|
121
|
+
...getTemplateVariableNames(mcp.env),
|
|
122
|
+
...getTemplateVariableNames(mcp.headers),
|
|
123
|
+
]);
|
|
124
|
+
const values = {};
|
|
125
|
+
for (const name of names) {
|
|
126
|
+
const value = getConfiguredEnvValue(name);
|
|
127
|
+
if (value) values[name] = value;
|
|
128
|
+
}
|
|
129
|
+
return values;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function loadSettings() {
|
|
133
|
+
if (!existsSync(CLAUDE_SETTINGS_PATH)) return {};
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(readFileSync(CLAUDE_SETTINGS_PATH, 'utf8'));
|
|
136
|
+
} catch {
|
|
137
|
+
return {};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function saveSettings(settings) {
|
|
142
|
+
mkdirSync(dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
143
|
+
writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function loadOpenCodeConfig() {
|
|
147
|
+
return readOpenCodeConfig().config ?? {};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function saveOpenCodeConfig(config) {
|
|
151
|
+
writeOpenCodeConfig(config);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getMcpById(id) {
|
|
155
|
+
const catalog = loadCatalog();
|
|
156
|
+
return catalog.mcps.find(m => m.id === id) ?? null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getInstalledMcps() {
|
|
160
|
+
const settings = loadSettings();
|
|
161
|
+
const oc = loadOpenCodeConfig();
|
|
162
|
+
const codexConfig = readCodexConfig();
|
|
163
|
+
const codexServers = Array.from(codexConfig.matchAll(/^\[mcp_servers\.("?)([^"\]\n]+)\1\]/gm)).map((match) => match[2]);
|
|
164
|
+
const set = new Set([
|
|
165
|
+
...Object.keys(settings.mcpServers ?? {}),
|
|
166
|
+
...Object.keys(oc.mcp ?? {}).map(normalizeInstalledOpenCodeMcpId),
|
|
167
|
+
...codexServers,
|
|
168
|
+
]);
|
|
169
|
+
return Array.from(set);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function prompt(rl, question) {
|
|
173
|
+
return new Promise(resolve => rl.question(question, resolve));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function writeCodexMcp(id, mcp, resolvedValues) {
|
|
177
|
+
const configPath = getCodexConfigPath(homedir());
|
|
178
|
+
const existing = removeDanglingConstructMcpMarkers(removeDanglingConstructMcpTimeouts(readCodexConfig(configPath)));
|
|
179
|
+
const cleaned = removeDanglingConstructMcpMarkers(removeTomlTables(existing, [`mcp_servers.${id}`, `mcp_servers.${tomlString(id)}`]));
|
|
180
|
+
const table = serializeCodexMcpTable(id, buildCodexMcpEntry(id, mcp, resolvedValues));
|
|
181
|
+
writeCodexConfig(`${cleaned.trimEnd()}\n\n${table}`, configPath);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function removeCodexMcp(id) {
|
|
185
|
+
const configPath = getCodexConfigPath(homedir());
|
|
186
|
+
const existing = readCodexConfig(configPath);
|
|
187
|
+
if (!existing) return false;
|
|
188
|
+
const cleaned = removeTomlTables(existing, [`mcp_servers.${id}`, `mcp_servers.${tomlString(id)}`]);
|
|
189
|
+
if (cleaned === existing.trimEnd()) return false;
|
|
190
|
+
writeCodexConfig(cleaned, configPath);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* construct mcp list
|
|
196
|
+
* Show all MCPs from catalog with installed/missing status.
|
|
197
|
+
*/
|
|
198
|
+
export function cmdMcpList() {
|
|
199
|
+
const catalog = loadCatalog();
|
|
200
|
+
const installed = new Set(getInstalledMcps());
|
|
201
|
+
|
|
202
|
+
const categories = ['core', 'optional', 'integration'];
|
|
203
|
+
const labels = { core: 'Core (Essential)', optional: 'Enhancements', integration: 'Third-Party' };
|
|
204
|
+
|
|
205
|
+
for (const cat of categories) {
|
|
206
|
+
const mcps = catalog.mcps.filter(m => m.category === cat);
|
|
207
|
+
if (!mcps.length) continue;
|
|
208
|
+
|
|
209
|
+
console.log(`\n${labels[cat]}:`);
|
|
210
|
+
console.log('─'.repeat(72));
|
|
211
|
+
for (const mcp of mcps) {
|
|
212
|
+
const status = installed.has(mcp.id) ? '✓' : '·';
|
|
213
|
+
const name = mcp.name.padEnd(20);
|
|
214
|
+
const req = mcp.requiredEnv.length ? ` [needs: ${mcp.requiredEnv.join(', ')}]` : '';
|
|
215
|
+
console.log(` ${status} ${name} ${mcp.description.substring(0, 50)}${req}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log('\n Run "construct mcp add <name>" to set up an integration.');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* construct mcp add <id>
|
|
224
|
+
* Interactive Guided Wizard: collect required env vars and write to settings.json.
|
|
225
|
+
*/
|
|
226
|
+
export async function cmdMcpAdd(id) {
|
|
227
|
+
const mcp = getMcpById(id);
|
|
228
|
+
if (!mcp) {
|
|
229
|
+
console.error(`Unknown MCP: "${id}"`);
|
|
230
|
+
console.error('Run "construct mcp list" to see available integrations.');
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
console.log(`\n╔══════════════════════════════════════════════════════════════╗`);
|
|
235
|
+
console.log(`║ CONSTRUCT SETUP WIZARD: ${mcp.name.toUpperCase().padEnd(16)} ║`);
|
|
236
|
+
console.log(`╚══════════════════════════════════════════════════════════════╝`);
|
|
237
|
+
console.log(`\n${mcp.description}`);
|
|
238
|
+
|
|
239
|
+
const flags = parseAddFlags();
|
|
240
|
+
const setupMode = await chooseSetupMode(mcp, flags);
|
|
241
|
+
const isAuto = setupMode === 'auto';
|
|
242
|
+
|
|
243
|
+
const envValues = {};
|
|
244
|
+
const resolvedValues = { ...process.env };
|
|
245
|
+
|
|
246
|
+
// --- AUTO-PROVISIONING LOGIC ---
|
|
247
|
+
if (id === 'memory' && isAuto) {
|
|
248
|
+
const port = process.env.MEMORY_PORT || 8765;
|
|
249
|
+
const isLocalHealthy = await new Promise(resolve => {
|
|
250
|
+
try {
|
|
251
|
+
execSync(`curl -sf http://localhost:${port}`, { stdio: 'ignore' });
|
|
252
|
+
resolve(true);
|
|
253
|
+
} catch {
|
|
254
|
+
resolve(false);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (isLocalHealthy || isAuto) {
|
|
259
|
+
console.log(`\n ✓ Local Memory detected at port ${port}. Auto-configuring...`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// GitHub: source token from gh CLI, env, or prompt (--token flag)
|
|
264
|
+
if (id === 'github') {
|
|
265
|
+
const envToken = process.env.GITHUB_TOKEN || process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
|
|
266
|
+
const forceToken = flags.token;
|
|
267
|
+
|
|
268
|
+
let ghCliToken = '';
|
|
269
|
+
try {
|
|
270
|
+
execSync('gh auth status -h github.com', { stdio: 'ignore' });
|
|
271
|
+
ghCliToken = execSync('gh auth token -h github.com 2>/dev/null', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
272
|
+
} catch { /* gh not installed or not authed */ }
|
|
273
|
+
|
|
274
|
+
if (forceToken) {
|
|
275
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
276
|
+
const best = envToken || ghCliToken;
|
|
277
|
+
const hint = best ? ' [press Enter to use detected token]' : '';
|
|
278
|
+
const value = await prompt(rl, `\n > GITHUB_TOKEN${hint}: `);
|
|
279
|
+
rl.close();
|
|
280
|
+
envValues.GITHUB_TOKEN = value.trim() || best || '';
|
|
281
|
+
} else if (ghCliToken) {
|
|
282
|
+
console.log('\n ✓ GitHub token sourced from gh CLI (gh auth login).');
|
|
283
|
+
envValues.GITHUB_TOKEN = ghCliToken;
|
|
284
|
+
} else if (envToken) {
|
|
285
|
+
console.log('\n ✓ GitHub token detected in environment.');
|
|
286
|
+
envValues.GITHUB_TOKEN = envToken;
|
|
287
|
+
} else {
|
|
288
|
+
console.log('\n ✗ No GitHub token found.');
|
|
289
|
+
console.log(' Run: gh auth login -h github.com (opens browser, stores token automatically)');
|
|
290
|
+
console.log(' Then re-run: construct mcp add github');
|
|
291
|
+
console.log(' Or provide a PAT: construct mcp add github --token');
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// -------------------------------
|
|
296
|
+
|
|
297
|
+
for (const [key, value] of Object.entries(getConfiguredEnvValues(mcp))) {
|
|
298
|
+
if (!envValues[key]) envValues[key] = value;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Only open URLs and ask for credentials if auto-provisioning didn't find them and not in --auto mode
|
|
302
|
+
if (mcp.requiredEnv.length > 0 && Object.keys(envValues).length === 0 && !isAuto) {
|
|
303
|
+
if (mcp.setupUrl) {
|
|
304
|
+
console.log(`\n1. OPENING AUTHORIZATION PAGE...`);
|
|
305
|
+
console.log(` URL: ${mcp.setupUrl}`);
|
|
306
|
+
openUrl(mcp.setupUrl);
|
|
307
|
+
console.log(`\n2. GUIDANCE:`);
|
|
308
|
+
console.log(` ${mcp.setupNote}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
console.log(`\n3. ENTER CREDENTIALS:`);
|
|
312
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
313
|
+
for (const envKey of mcp.requiredEnv) {
|
|
314
|
+
let current = process.env[envKey];
|
|
315
|
+
if (envKey === 'GITHUB_TOKEN' && !current) {
|
|
316
|
+
current = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
|
|
317
|
+
}
|
|
318
|
+
const hint = current ? ` [detected in env]` : '';
|
|
319
|
+
const value = await prompt(rl, ` > ${envKey}${hint}: `);
|
|
320
|
+
envValues[envKey] = value.trim() || current || '';
|
|
321
|
+
}
|
|
322
|
+
rl.close();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Validate all required env vars are present
|
|
326
|
+
for (const key of mcp.requiredEnv) {
|
|
327
|
+
if (!envValues[key]) {
|
|
328
|
+
console.error(`\nError: ${key} is required. Setup cancelled.`);
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
Object.assign(resolvedValues, envValues);
|
|
334
|
+
if (mcp.type === 'url' && id === 'memory') {
|
|
335
|
+
resolvedValues.MEMORY_PORT = process.env.MEMORY_PORT || '8765';
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Write to Claude settings
|
|
339
|
+
const settings = loadSettings();
|
|
340
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
341
|
+
settings.mcpServers[id] = buildClaudeMcpEntry(id, mcp, resolvedValues);
|
|
342
|
+
saveSettings(settings);
|
|
343
|
+
|
|
344
|
+
// Write to OpenCode config
|
|
345
|
+
const oc = loadOpenCodeConfig();
|
|
346
|
+
if (!oc.mcp) oc.mcp = {};
|
|
347
|
+
const openCodeId = getOpenCodeMcpId(id);
|
|
348
|
+
delete oc.mcp[id];
|
|
349
|
+
oc.mcp[openCodeId] = buildOpenCodeMcpEntry(id, mcp, resolvedValues).entry;
|
|
350
|
+
saveOpenCodeConfig(oc);
|
|
351
|
+
|
|
352
|
+
// Write to Codex config. GitHub uses the official remote server URL so Codex
|
|
353
|
+
// can authenticate via `codex mcp login github` instead of launching stale
|
|
354
|
+
// local github-mcp-server binaries.
|
|
355
|
+
writeCodexMcp(id, mcp, resolvedValues);
|
|
356
|
+
|
|
357
|
+
// --- SYNC TO .ENV ---
|
|
358
|
+
const envPath = getUserEnvPath(homedir());
|
|
359
|
+
writeEnvValues(envPath, envValues);
|
|
360
|
+
console.log(`✓ Credentials synchronized to ${envPath}`);
|
|
361
|
+
// --------------------
|
|
362
|
+
|
|
363
|
+
console.log(`\n✓ ${mcp.name} successfully wired into Claude Code, OpenCode, and Codex.`);
|
|
364
|
+
console.log(`✓ Setup mode: ${setupMode}`);
|
|
365
|
+
console.log(`✓ Services synchronized.`);
|
|
366
|
+
console.log(`\nNext: Restart OpenCode, Claude Code, or Codex to activate the new tools.`);
|
|
367
|
+
if (id === 'github') {
|
|
368
|
+
console.log(' For Codex OAuth, run: codex mcp login github');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (mcp.usedBy.length) {
|
|
372
|
+
console.log(`\nAgents now empowered: ${mcp.usedBy.join(', ')}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* construct mcp remove <id>
|
|
378
|
+
*/
|
|
379
|
+
export function cmdMcpRemove(id) {
|
|
380
|
+
const settings = loadSettings();
|
|
381
|
+
const openCodeState = readOpenCodeConfig();
|
|
382
|
+
const oc = openCodeState.config ?? {};
|
|
383
|
+
const openCodeId = getOpenCodeMcpId(id);
|
|
384
|
+
const hasClaudeEntry = Boolean(settings.mcpServers?.[id]);
|
|
385
|
+
const hasOpenCodeEntry = Boolean(oc.mcp?.[openCodeId] || oc.mcp?.[id]);
|
|
386
|
+
const hasCodexEntry = readCodexConfig().includes(`[mcp_servers.${id}]`) || readCodexConfig().includes(`[mcp_servers.${tomlString(id)}]`);
|
|
387
|
+
if (!hasClaudeEntry && !hasOpenCodeEntry && !hasCodexEntry) {
|
|
388
|
+
console.error(`${id} is not installed.`);
|
|
389
|
+
process.exit(1);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const mcp = getMcpById(id);
|
|
393
|
+
const name = mcp?.name ?? id;
|
|
394
|
+
|
|
395
|
+
if (settings.mcpServers) delete settings.mcpServers[id];
|
|
396
|
+
saveSettings(settings);
|
|
397
|
+
|
|
398
|
+
if (oc.mcp) {
|
|
399
|
+
delete oc.mcp[openCodeId];
|
|
400
|
+
delete oc.mcp[id];
|
|
401
|
+
}
|
|
402
|
+
if (openCodeState.config || hasOpenCodeEntry) saveOpenCodeConfig(oc);
|
|
403
|
+
removeCodexMcp(id);
|
|
404
|
+
|
|
405
|
+
console.log(`✓ ${name} removed from Claude Code, OpenCode, and Codex config`);
|
|
406
|
+
console.log('Restart Claude Code, OpenCode, or Codex to deactivate it.');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* construct mcp info <id>
|
|
411
|
+
*/
|
|
412
|
+
export function cmdMcpInfo(id) {
|
|
413
|
+
const mcp = getMcpById(id);
|
|
414
|
+
if (!mcp) {
|
|
415
|
+
console.error(`Unknown MCP: "${id}"`);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const installed = getInstalledMcps().includes(id);
|
|
420
|
+
|
|
421
|
+
console.log(`\n${mcp.name} (${mcp.id})`);
|
|
422
|
+
console.log('─'.repeat(48));
|
|
423
|
+
console.log(`Status: ${installed ? '✓ installed' : '· not installed'}`);
|
|
424
|
+
console.log(`Category: ${mcp.category}`);
|
|
425
|
+
if (mcp.package) console.log(`Package: ${mcp.package}`);
|
|
426
|
+
console.log(`\n${mcp.description}`);
|
|
427
|
+
if (mcp.setupUrl) console.log(`\nSetup: ${mcp.setupUrl}`);
|
|
428
|
+
if (mcp.setupNote) console.log(` ${mcp.setupNote}`);
|
|
429
|
+
if (mcp.requiredEnv.length) console.log(`\nRequires: ${mcp.requiredEnv.join(', ')}`);
|
|
430
|
+
if (mcp.usedBy.length) console.log(`Used by: ${mcp.usedBy.join(', ')}`);
|
|
431
|
+
console.log(`\nDegraded: ${mcp.degradedMessage}`);
|
|
432
|
+
console.log('');
|
|
433
|
+
}
|