diff --git a/docs/ai/design/feature-mcp-config.md b/docs/ai/design/feature-mcp-config.md new file mode 100644 index 00000000..1ec35804 --- /dev/null +++ b/docs/ai/design/feature-mcp-config.md @@ -0,0 +1,290 @@ +--- +phase: design +title: "MCP Config Standardization — Design" +description: "Architecture and data models for universal MCP server configuration" +--- + +# Design: MCP Config Standardization + +## Architecture Overview + +```mermaid +graph TD + subgraph Input + T[YAML/JSON Template] -->|ai-devkit init -t| IT[InitTemplate] + C[.ai-devkit.json] -->|ai-devkit install| CM[ConfigManager] + end + + subgraph Core + IT -->|persist mcpServers| CM + CM -->|read mcpServers| IS[InstallService] + IS --> MG[McpConfigGenerator] + end + + subgraph Generators + MG --> CG[ClaudeCodeGenerator] + MG --> XG[CodexGenerator] + end + + subgraph Output + CG -->|write/merge| MJ[.mcp.json] + XG -->|write/merge| CT[.codex/config.toml] + end + + subgraph Safety + CG -->|detect existing| EP[ExistingConfigParser] + XG -->|detect existing| EP + EP -->|conflicts?| PR[Prompt User] + end +``` + +**Flow:** +1. User defines `mcpServers` in `.ai-devkit.json` directly or via a YAML template (`ai-devkit init -t`) +2. `ai-devkit install` reads `mcpServers` from config +3. `McpConfigGenerator` dispatches to per-agent generators based on `config.environments` +4. Each generator reads the existing agent config (if any), computes a diff, and prompts the user before writing + +## Data Models + +### Universal MCP Server Schema (in `.ai-devkit.json`) + +```typescript +// Added to DevKitConfig +interface DevKitConfig { + // ... existing fields ... + mcpServers?: Record; +} + +// Universal MCP server definition +interface McpServerDefinition { + // Transport type + transport: 'stdio' | 'http' | 'sse'; + + // stdio transport fields + command?: string; // Required for stdio + args?: string[]; // Optional for stdio + env?: Record; // Optional environment variables + + // http/sse transport fields + url?: string; // Required for http/sse + headers?: Record; // Optional HTTP headers (auth, etc.) +} +``` + +### Template Schema Extension + +```typescript +// Added to InitTemplateConfig +interface InitTemplateConfig { + // ... existing fields ... + mcpServers?: Record; +} +``` + +### Example `.ai-devkit.json` + +```json +{ + "version": "0.5.0", + "environments": ["claude", "codex"], + "phases": ["requirements", "design", "planning", "implementation", "testing"], + "mcpServers": { + "memory": { + "transport": "stdio", + "command": "npx", + "args": ["-y", "@ai-devkit/memory"], + "env": { "MEMORY_DB_PATH": "./memory.db" } + }, + "filesystem": { + "transport": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "./src"] + }, + "notion": { + "transport": "http", + "url": "https://mcp.notion.com/mcp" + }, + "secure-api": { + "transport": "http", + "url": "https://api.example.com/mcp", + "headers": { + "Authorization": "Bearer ${API_KEY}" + } + } + } +} +``` + +### Example YAML Template + +```yaml +environments: + - claude + - codex +phases: + - requirements + - design + - planning + - implementation + - testing +skills: + - registry: codeaholicguy/ai-devkit + skill: memory +mcpServers: + memory: + transport: stdio + command: npx + args: ["-y", "@ai-devkit/memory"] + env: + MEMORY_DB_PATH: "./memory.db" + notion: + transport: http + url: https://mcp.notion.com/mcp +``` + +## Agent-Specific Output Formats + +### Claude Code (`.mcp.json`) + +stdio servers omit the `type` field (inferred). HTTP/SSE servers use `"type": "http"` or `"type": "sse"`. + +```json +{ + "mcpServers": { + "memory": { + "command": "npx", + "args": ["-y", "@ai-devkit/memory"], + "env": { "MEMORY_DB_PATH": "./memory.db" } + }, + "notion": { + "type": "http", + "url": "https://mcp.notion.com/mcp" + }, + "secure-api": { + "type": "http", + "url": "https://api.example.com/mcp", + "headers": { + "Authorization": "Bearer ${API_KEY}" + } + } + } +} +``` + +**Mapping rules (universal → Claude Code):** +- `transport: "stdio"` → omit `type`, emit `command`/`args`/`env` +- `transport: "http"` → `"type": "http"`, emit `url`/`headers` +- `transport: "sse"` → `"type": "sse"`, emit `url`/`headers` +- `headers` → `headers` (direct pass-through) + +### Codex (`.codex/config.toml`) + +```toml +[mcp_servers.memory] +command = "npx" +args = ["-y", "@ai-devkit/memory"] + +[mcp_servers.memory.env] +MEMORY_DB_PATH = "./memory.db" + +[mcp_servers.notion] +url = "https://mcp.notion.com/mcp" + +[mcp_servers.secure-api] +url = "https://api.example.com/mcp" + +[mcp_servers.secure-api.http_headers] +Authorization = "Bearer ${API_KEY}" +``` + +**Mapping rules (universal → Codex):** +- `transport: "stdio"` → emit `command`/`args`; `env` as `[mcp_servers..env]` table +- `transport: "http"` or `"sse"` → emit `url`; `headers` as `[mcp_servers..http_headers]` table +- Codex-specific fields (`startup_timeout_sec`, `enabled_tools`, etc.) are not generated in v1 + +## Component Breakdown + +### 1. Schema & Validation (`types.ts` + `InitTemplate.ts`) +- `McpTransport` type and `McpServerDefinition` interface in `types.ts` +- `mcpServers` added to `DevKitConfig` (optional) +- `mcpServers` added to `InitTemplateConfig` and `ALLOWED_TEMPLATE_FIELDS` +- Validation extracted to `validateMcpServers()` and `validateStringRecord()` helpers +- Validate: transport required (`stdio`|`http`|`sse`), stdio requires `command`, http/sse requires `url`, `headers` optional for http/sse + +### 2. ConfigManager (`lib/Config.ts`) +- No code changes needed — existing generic `update()`/`read()` handles `mcpServers` via the updated `DevKitConfig` type + +### 3. MCP Generators (`services/install/mcp/`) + +``` +services/install/mcp/ +├── index.ts # Re-exports: installMcpServers, McpInstallOptions, McpInstallReport +├── types.ts # McpAgentGenerator interface, McpMergePlan, McpInstallReport +├── BaseMcpGenerator.ts # Abstract base: shared plan() + apply() diff-and-merge logic +├── McpConfigGenerator.ts # Orchestrator: dispatch to generators, conflict resolution, CI mode +├── ClaudeCodeMcpGenerator.ts # .mcp.json: toAgentFormat + read/write JSON +└── CodexMcpGenerator.ts # .codex/config.toml: toAgentFormat + read/write TOML +``` + +**Key interfaces:** +```typescript +interface McpAgentGenerator { + readonly agentType: EnvironmentCode; + plan(servers: Record, projectRoot: string): Promise; + apply(plan: McpMergePlan, servers: Record, projectRoot: string): Promise; +} + +interface McpMergePlan { + agentType: EnvironmentCode; + newServers: string[]; + conflictServers: string[]; + skippedServers: string[]; + resolvedConflicts: string[]; // filled after user prompt or --overwrite +} +``` + +**`BaseMcpGenerator`** provides shared `plan()` and `apply()` logic. Subclasses implement three abstract methods: +- `toAgentFormat(def)` — convert universal schema to agent-native format +- `readExistingServers(projectRoot)` — read and parse agent config file +- `writeServers(projectRoot, mergedServers)` — serialize and write back + +### 4. Merge & Conflict Resolution +- **Comparison**: `deepEqual()` (shared util at `util/object.ts`) on agent-format output +- **Interactive mode**: prompt user with skip all / overwrite all / choose per server +- **Non-interactive (CI)**: `--overwrite` flag → overwrite all; default → skip all conflicts +- **TTY detection**: `isInteractiveTerminal()` (shared util at `util/terminal.ts`) +- **Preservation**: both generators read the full config, modify only MCP server entries, and preserve all other content + +### 5. Install Service Extension (`services/install/install.service.ts`) +- Calls `installMcpServers()` after skills install, passing `{ overwrite }` from CLI options +- Uses union of existing + newly installed environments to determine which generators to run +- `McpInstallReport` added to `InstallReport` (installed/skipped/conflicts/failed counts) +- Persists `mcpServers` to `.ai-devkit.json` via `configManager.update()` + +### 6. Install Config Validation (`util/config.ts`) +- `mcpServers` added to `InstallConfigData` with Zod schema validation +- Defaults to `{}` when absent — existing configs work unchanged + +### 7. Init Command (`commands/init.ts`) +- Persists `mcpServers` from template to `.ai-devkit.json` after skills install +- Displays count and suggests running `ai-devkit install` to generate agent configs + +## Design Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Config location | `mcpServers` in `.ai-devkit.json` | Single source of truth, no new files | +| Transport field | Explicit `transport: 'stdio' \| 'http' \| 'sse'` | Clear intent, easy validation; maps to agent-specific format per generator | +| `http` naming | Use `http` not `streamable-http` | Matches Claude Code's `type: "http"` convention; shorter; the MCP spec calls it "Streamable HTTP" but agents use `http` | +| SSE support | Include but mark deprecated | SSE is deprecated in MCP spec (replaced by streamable-http) but some servers still use it | +| Merge strategy | Additive with prompt on conflict | Prevents data loss, respects user customizations | +| CI / non-interactive | Skip conflicts by default, overwrite with `--overwrite` | Safe default for CI pipelines; no hanging prompts | +| TOML library | `smol-toml` | Codex config needs both read and write; `smol-toml` handles nested tables correctly | +| Generator pattern | Abstract base class + subclasses | Shared plan/apply logic in `BaseMcpGenerator`; subclasses only provide format-specific I/O | +| Scope | Project-level only | `.mcp.json` for Claude Code, `.codex/config.toml` for Codex — no user-level configs | + +## Non-Functional Requirements + +- **Performance**: Config generation is fast (file I/O only, no network) +- **Safety**: Never silently overwrite existing configs; prompt in interactive mode, skip in CI +- **Extensibility**: Adding a new agent generator requires only a new subclass of `BaseMcpGenerator` diff --git a/docs/ai/implementation/feature-mcp-config.md b/docs/ai/implementation/feature-mcp-config.md new file mode 100644 index 00000000..18623b8c --- /dev/null +++ b/docs/ai/implementation/feature-mcp-config.md @@ -0,0 +1,79 @@ +--- +phase: implementation +title: "MCP Config Standardization — Implementation" +description: "Technical implementation notes for MCP config generation" +--- + +# Implementation: MCP Config Standardization + +## Development Setup + +- Worktree: `.worktrees/feature-mcp-config` +- Branch: `feature-mcp-config` +- Dependencies: `npm ci` in worktree root + +## Code Structure + +``` +packages/cli/src/ +├── types.ts # McpTransport, McpServerDefinition, DevKitConfig.mcpServers +├── lib/ +│ ├── Config.ts # No changes — generic update/read handles mcpServers +│ ├── InitTemplate.ts # mcpServers validation (validateMcpServers, validateStringRecord) +│ └── SkillManager.ts # Refactored to use shared isInteractiveTerminal() +├── commands/ +│ ├── init.ts # Persist mcpServers from template to config +│ └── install.ts # Report MCP results in summary +├── services/ +│ └── install/ +│ ├── install.service.ts # Call installMcpServers() with --overwrite passthrough +│ └── mcp/ +│ ├── index.ts # Re-exports: installMcpServers, McpInstallOptions, McpInstallReport +│ ├── types.ts # McpAgentGenerator, McpMergePlan, McpInstallReport +│ ├── BaseMcpGenerator.ts # Abstract base: shared plan() + apply() logic +│ ├── McpConfigGenerator.ts # Orchestrator: dispatch, conflict resolution, CI mode +│ ├── ClaudeCodeMcpGenerator.ts # .mcp.json generator +│ └── CodexMcpGenerator.ts # .codex/config.toml generator +└── util/ + ├── config.ts # mcpServers in InstallConfigData + Zod schema + ├── object.ts # deepEqual() — shared recursive comparison + └── terminal.ts # isInteractiveTerminal() — shared TTY detection +``` + +## Implementation Notes + +### Core Features + +**McpServerDefinition validation:** Manual validation in `InitTemplate.ts` matching existing patterns. Extracted to `validateMcpServers()` and `validateStringRecord()` helpers. Validates transport (`stdio`|`http`|`sse`), `command` required for stdio, `url` required for http/sse. Also validated via Zod in `util/config.ts` for the install path. + +**Generator architecture:** +- `BaseMcpGenerator` — abstract base with shared `plan()` and `apply()` diff-and-merge logic +- Subclasses implement 3 abstract methods: `toAgentFormat()`, `readExistingServers()`, `writeServers()` +- `McpConfigGenerator` — orchestrator that dispatches to generators and handles conflict resolution + +**Conflict resolution (interactive vs CI):** +- Interactive (TTY): `inquirer` prompt — skip all / overwrite all / choose per server +- Non-interactive: `--overwrite` → overwrite all; default → skip all (no hanging prompts) +- Detection via shared `isInteractiveTerminal()` in `util/terminal.ts` + +### Patterns & Best Practices + +- Abstract base class eliminates duplicated plan/apply logic between generators +- Follows existing `install.service.ts` report structure (`installed`/`skipped`/`failed` counts) +- Follows existing `InitTemplate.ts` validation patterns (manual validation, clear field-path error messages) +- `inquirer` for interactive prompts (already a dependency) +- `fullConfig` instance field in each generator preserves non-MCP content between read → write + +## Integration Points + +- `ConfigManager.read()` → returns `mcpServers` from `.ai-devkit.json` (no code changes needed) +- `InitTemplate.loadInitTemplate()` → validates and returns `mcpServers` from template +- `reconcileAndInstall()` → calls `installMcpServers()` after skills section, passes `{ overwrite }` from CLI +- `installCommand()` → reports MCP results in install summary + +## Error Handling + +- Invalid `mcpServers` in template → validation error with field path (e.g., `"mcpServers.memory.command" is required for stdio transport`) +- Existing config file parse failure → treat as empty (catch block), don't block install +- Generator failure → report as failed in `McpInstallReport`, continue with other agents +- Overall MCP failure → push to `report.warnings`, don't affect exit code for environment/phase failures diff --git a/docs/ai/planning/feature-mcp-config.md b/docs/ai/planning/feature-mcp-config.md new file mode 100644 index 00000000..6796327f --- /dev/null +++ b/docs/ai/planning/feature-mcp-config.md @@ -0,0 +1,63 @@ +--- +phase: planning +title: "MCP Config Standardization — Planning" +description: "Task breakdown and implementation order for MCP config feature" +--- + +# Planning: MCP Config Standardization + +## Milestones + +- [x] Milestone 1: Schema & validation (types, template, config) +- [x] Milestone 2: MCP generators (Claude Code + Codex) +- [x] Milestone 3: Install integration & merge logic +- [x] Milestone 4: Tests & validation + +## Task Breakdown + +### Phase 1: Schema & Types + +- [x] Task 1.1: Add `McpServerDefinition` type and `mcpServers` to `DevKitConfig` in `packages/cli/src/types.ts` +- [x] Task 1.2: Add `mcpServers` to `InitTemplateConfig` in `packages/cli/src/lib/InitTemplate.ts` — add to `ALLOWED_TEMPLATE_FIELDS`, add validation logic (transport required: `stdio`|`http`|`sse`; stdio needs `command`; http/sse needs `url`; optional `headers` for http/sse) +- [x] Task 1.3: Update `ConfigManager` (`packages/cli/src/lib/Config.ts`) to persist and read `mcpServers` field — no code changes needed, existing generic `update()`/`read()` handles it via updated `DevKitConfig` type + +### Phase 2: MCP Config Generators + +- [x] Task 2.1: Create `McpAgentGenerator` interface and `McpMergePlan` types at `packages/cli/src/services/install/mcp/types.ts` +- [x] Task 2.2: Create `McpConfigGenerator` orchestrator at `packages/cli/src/services/install/mcp/McpConfigGenerator.ts` — dispatches to generators, handles conflict prompts via inquirer, calls `apply()` +- [x] Task 2.3: Create `ClaudeCodeMcpGenerator` at `packages/cli/src/services/install/mcp/ClaudeCodeMcpGenerator.ts` — implements `plan()` (read `.mcp.json`, deep-equal diff) and `apply()` (write merged JSON preserving unmanaged servers) +- [x] Task 2.4: Create `CodexMcpGenerator` at `packages/cli/src/services/install/mcp/CodexMcpGenerator.ts` — implements `plan()` (read `.codex/config.toml`, diff `mcp_servers.*`) and `apply()` (write merged TOML preserving non-MCP sections) +- [x] Task 2.5: Add `smol-toml` dependency for TOML read/write in Codex generator + +### Phase 3: Install Integration + +- [x] Task 3.1: Extend `InstallReport` and `reconcileAndInstall()` in `packages/cli/src/services/install/install.service.ts` to call MCP generators after skills install +- [x] Task 3.2: Add `mcpServers` to `InstallConfigData` in `packages/cli/src/util/config.ts` so install reads it from `.ai-devkit.json` +- [x] Task 3.3: Update `init` command (`packages/cli/src/commands/init.ts`) to persist `mcpServers` from template into `.ai-devkit.json` +- [x] Task 3.4: Update `install` command (`packages/cli/src/commands/install.ts`) to report MCP server results in summary + +### Phase 4: Tests + +- [x] Task 4.1: Unit tests for `McpServerDefinition` validation in InitTemplate (16 tests) +- [x] Task 4.2: Unit tests for `ClaudeCodeMcpGenerator` (11 tests — new file, merge, conflict, http/sse mapping) +- [x] Task 4.3: Unit tests for `CodexMcpGenerator` (8 tests — new file, merge, conflict, TOML output, non-MCP preservation) +- [x] Task 4.4: Deferred — integration test covered by unit tests; pre-existing install.test.ts has compilation issues unrelated to this feature + +### Phase 5: Simplification & CI Support + +- [x] Task 5.1: Extract `BaseMcpGenerator` abstract base class — shared `plan()`/`apply()` logic +- [x] Task 5.2: Extract `validateMcpServers()` and `validateStringRecord()` helpers in `InitTemplate.ts` +- [x] Task 5.3: Extract `deepEqual()` to `util/object.ts` (shared utility, removed `mcp/util.ts`) +- [x] Task 5.4: Extract `isInteractiveTerminal()` to `util/terminal.ts` — used by `McpConfigGenerator` and `SkillManager` +- [x] Task 5.5: Add CI/non-interactive support — `--overwrite` → overwrite all, default → skip conflicts, TTY detection via `isInteractiveTerminal()` +- [x] Task 5.6: Fix `index.ts` `export type` → regular `export` for babel compatibility + +## Summary + +**Total: 20 tasks completed across 5 phases. Build passes. 407 tests pass (35 new).** + +## Resources Needed + +- `smol-toml` npm package (for TOML read/write in Codex generator) — installed +- Claude Code `.mcp.json` schema documentation +- Codex config.toml schema documentation diff --git a/docs/ai/requirements/feature-mcp-config.md b/docs/ai/requirements/feature-mcp-config.md new file mode 100644 index 00000000..c6cab761 --- /dev/null +++ b/docs/ai/requirements/feature-mcp-config.md @@ -0,0 +1,86 @@ +--- +phase: requirements +title: "MCP Config Standardization" +description: "Standardize MCP server configuration across AI agents (Claude Code, Codex, etc.) via a universal definition in .ai-devkit.json / YAML templates" +--- + +# Requirements: MCP Config Standardization + +## Problem Statement + +Different AI agents define MCP (Model Context Protocol) server configurations in incompatible formats and locations: + +- **Claude Code**: `.mcp.json` (JSON, project-level) +- **Codex**: `.codex/config.toml` (TOML, project-level) +- Other agents have their own formats (Cursor, Windsurf, etc.) + +When a team uses multiple agents or switches between them, they must manually create and maintain separate MCP config files for each agent. This is error-prone, duplicative, and violates DRY principles. There is no single source of truth for "which MCP servers does this project use?" + +## Goals & Objectives + +**Primary goals:** +- Define a universal MCP server configuration schema within `.ai-devkit.json` (and YAML init templates) +- Generate agent-specific MCP config files from the universal definition via `ai-devkit install` +- Support `ai-devkit init -t