Files

320 lines
12 KiB
Markdown
Raw Permalink Normal View History

2026-02-21 03:10:39 +00:00
# Task ID: 9
**Title:** Implement mcpctl Project Commands
**Status:** pending
**Dependencies:** 7, 5
**Priority:** high
**Description:** Add commands for managing MCP projects and the critical 'claude add-mcp-project' command for integrating with Claude sessions.
**Details:**
Add project commands:
```typescript
// commands/projects.ts
const projectCmd = program
.command('project')
.description('Manage MCP projects');
projectCmd
.command('create')
.argument('<name>', 'Project name')
.option('--profiles <profiles...>', 'Profile names to include')
.action(async (name, options) => {
const client = getClient();
const profiles = await client.get('/api/profiles');
const profileIds = profiles
.filter(p => options.profiles?.includes(p.name))
.map(p => p.id);
const project = await client.post('/api/projects', { name, profileIds });
console.log(`Project "${project.name}" created`);
});
projectCmd
.command('add-profile')
.argument('<project>', 'Project name')
.argument('<profile>', 'Profile name to add')
.action(async (project, profile) => {
// Add profile to project
});
// Critical command: claude add-mcp-project
const claudeCmd = program
.command('claude')
.description('Claude integration commands');
claudeCmd
.command('add-mcp-project')
.argument('<project>', 'Project name')
.option('--path <path>', 'Path to .mcp.json', '.mcp.json')
.action(async (projectName, options) => {
const client = getClient();
const mcpConfig = await client.get(`/api/projects/${projectName}/mcp-config`);
// Read existing .mcp.json or create new
let existing = {};
if (fs.existsSync(options.path)) {
existing = JSON.parse(fs.readFileSync(options.path, 'utf8'));
}
// Merge project MCPs into existing config
const merged = {
mcpServers: {
...existing.mcpServers,
...mcpConfig.mcpServers
}
};
fs.writeFileSync(options.path, JSON.stringify(merged, null, 2));
console.log(`Added project "${projectName}" to ${options.path}`);
console.log('MCPs added:', Object.keys(mcpConfig.mcpServers).join(', '));
});
claudeCmd
.command('remove-mcp-project')
.argument('<project>', 'Project name')
.action(async (projectName) => {
// Remove project MCPs from .mcp.json
});
```
**Test Strategy:**
Test project creation with and without profiles. Test claude add-mcp-project creates valid .mcp.json. Test merging with existing .mcp.json preserves other entries.
## Subtasks
### 9.1. Write TDD tests for project command Zod schemas and CLI argument parsing
**Status:** pending
**Dependencies:** None
Create comprehensive Vitest test suites for project command validation schemas, CLI argument parsing for project create/add-profile/remove-profile/status commands, and the claude command group structure BEFORE implementing any commands.
**Details:**
Create src/cli/tests/unit/commands/project.test.ts with TDD tests for:
1. Project command validation schemas:
- CreateProjectSchema: name (alphanumeric-dash, 3-64 chars), --profiles array (optional, profile names)
- AddProfileSchema: project name (required), profile name (required)
- Test invalid project names rejected (spaces, special chars, empty)
- Test profile names validated against expected format
2. CLI argument parsing tests:
- Test 'mcpctl project create weekly_reports' parses correctly
- Test 'mcpctl project create weekly_reports --profiles slack-ro jira-ro' captures profile array
- Test 'mcpctl project add-profile weekly_reports slack-full' captures both arguments
- Test 'mcpctl project remove-profile' validates required arguments
- Test 'mcpctl project status <name>' parses project name
- Test '--help' on project subcommands shows usage
3. Claude command group structure tests:
- Test 'mcpctl claude' shows available subcommands
- Test 'mcpctl claude add-mcp-project' is recognized
- Test 'mcpctl claude remove-mcp-project' is recognized
- Verify extensible command group architecture for future Claude integration features
Create src/cli/tests/fixtures/mock-profiles.ts with sample profile data (slack-ro, slack-full, jira-ro, jira-full, github-ro). All tests should initially fail (TDD red phase).
### 9.2. Write TDD tests for claude add-mcp-project with .mcp.json security validation
**Status:** pending
**Dependencies:** 9.1
Create comprehensive Vitest test suites for the critical claude add-mcp-project command focusing on .mcp.json manipulation, merge behavior with existing configs, path validation, and SECURITY: ensuring secrets are NEVER written to .mcp.json.
**Details:**
Create src/cli/tests/unit/commands/claude.test.ts with TDD tests:
1. Basic functionality tests:
- Test 'mcpctl claude add-mcp-project weekly_reports' calls GET /api/projects/weekly_reports/mcp-config
- Test creates .mcp.json when file doesn't exist
- Test writes valid JSON with mcpServers object
- Test output includes list of added MCP server names
- Test '--path custom.mcp.json' writes to specified path
2. Merge behavior tests:
- Test merges with existing .mcp.json preserving other entries
- Test existing mcpServers entries NOT overwritten (no data loss)
- Test handles empty existing .mcp.json gracefully
- Test handles malformed existing .mcp.json with clear error
3. SECURITY tests (critical):
- Test .mcp.json output NEVER contains secret env values (SLACK_BOT_TOKEN, JIRA_API_TOKEN, GITHUB_TOKEN)
- Test env object only contains non-secret placeholder or reference values
- Test path traversal rejected: --path '../../../etc/passwd' fails
- Test --path validates parent directory exists
- Test command injection patterns in project name rejected
4. Error handling tests:
- Test 404 from API shows 'Project not found' message
- Test network error shows connection error
- Test write permission error handled gracefully
5. Remove command tests:
- Test 'mcpctl claude remove-mcp-project weekly_reports' removes project's servers from .mcp.json
- Test preserves other unrelated mcpServers entries
Create src/cli/tests/fixtures/sample-mcp-json.ts with various .mcp.json states for testing.
### 9.3. Implement project command group with CRUD operations and profile management
**Status:** pending
**Dependencies:** 9.1
Implement the project subcommand group (create, add-profile, remove-profile, list, describe) using Commander.js with full TDD tests passing. Include project status command showing MCP server health for SRE dashboards.
**Details:**
Create src/cli/src/commands/project.ts implementing CommandModule:
1. Command registration:
```typescript
const projectCmd = program.command('project').description('Manage MCP projects');
```
2. 'project create' command:
- Arguments: <name> (required)
- Options: --profiles <profiles...> (profile names to include)
- Implementation: Fetch /api/profiles to resolve names to IDs, POST /api/projects
- Validation: Project name format validation via Zod schema
- Output: 'Project "name" created with N profiles'
3. 'project add-profile' command:
- Arguments: <project> <profile> (both required)
- Implementation: GET current project, add profile ID, PUT /api/projects/:id/profiles
- Handle profile not found with clear error message
4. 'project remove-profile' command:
- Arguments: <project> <profile>
- Implementation: Remove profile from project's profile list
5. 'project list' command:
- Output: Table format showing NAME, PROFILES, CREATED columns
- Support -o json/yaml output formats
6. 'project describe <name>' command:
- Show full project details including all profiles and their servers
7. 'project status <name>' command (SRE requirement):
- Show project with all MCP servers and their health status
- Display: SERVER_NAME, PROFILE, STATUS (running/stopped/error), LAST_HEALTH_CHECK
- Support -o json for monitoring pipeline integration
- Exit code 0 if all healthy, 1 if any unhealthy (for alerting)
8. Support tags/labels for data engineer categorization:
- Add --tag <key=value> option to create command
- Add --filter-tag <key=value> option to list command
### 9.4. Implement claude command group with secure add-mcp-project and remove-mcp-project
**Status:** pending
**Dependencies:** 9.2, 9.3
Implement the extensible claude subcommand group with the critical add-mcp-project command that safely writes .mcp.json without secrets, supporting both direct mcpd URLs and service discovery patterns for networking team requirements.
**Details:**
Create src/cli/src/commands/claude.ts implementing CommandModule:
1. Extensible command group architecture:
```typescript
const claudeCmd = program.command('claude').description('Claude integration commands');
// Designed for future: claude sync, claude validate, claude diagnose
```
2. 'claude add-mcp-project' implementation:
- Arguments: <project> (project name)
- Options: --path <path> (default: .mcp.json), --dry-run (show what would be written)
- Implementation:
a. Validate --path: reject traversal (../), validate extension (.json)
b. GET /api/projects/<project>/mcp-config from mcpd
c. SECURITY: Verify response contains NO secret values (double-check even though API shouldn't return them)
d. Read existing .mcp.json if exists, parse JSON
e. Merge: existing.mcpServers + new mcpServers (new overwrites conflicts)
f. Write atomic (temp file + rename)
- Output: List of added MCP server names
3. SECURITY implementation in mcp-json-writer.ts:
- Create sanitizeMcpConfig() function that strips any env values matching secret patterns
- Log warning if API returns unexpected secret-looking values
- Never write plain-text credentials to filesystem
4. Service discovery support (networking team requirement):
- Support mcpServers entries pointing to mcpd via:
a. Direct URL: env.MCPD_URL = 'http://nas:3000'
b. Service discovery: env.MCPD_SERVICE = 'mcpd.local'
- Document both patterns in command help
5. 'claude remove-mcp-project' implementation:
- Read .mcp.json, identify servers added by this project (track via metadata)
- Remove only those servers, preserve others
- Add __mcpctl_source metadata to track which project added each server
6. Create utils/mcp-json-utils.ts:
- readMcpJson(path): safely read and parse
- writeMcpJson(path, config): atomic write with backup
- mergeMcpServers(existing, new): merge logic
- validateMcpJson(config): structure validation
### 9.5. Create integration tests and comprehensive security review documentation
**Status:** pending
**Dependencies:** 9.3, 9.4
Build complete integration test suite testing project and claude commands against mock mcpd server, perform security review of .mcp.json manipulation, and document all security considerations including injection risks and credential handling.
**Details:**
Create src/cli/tests/integration/project-commands.test.ts:
1. Full workflow integration tests:
- Start mock mcpd server with realistic responses
- Create project with profiles via CLI
- Add profiles to project
- Run 'claude add-mcp-project' and verify .mcp.json output
- Verify merge preserves existing entries
- Remove project and verify cleanup
2. SRE integration tests:
- Test 'project status' output is grep-friendly
- Test exit codes work with shell scripts
- Test JSON output parseable by jq
- Test integration with monitoring (mock Prometheus metrics endpoint)
3. Data engineer integration tests:
- Test project with tags (--tag team=data, --tag category=analytics)
- Test filtering by tags works
- Test BigQuery/Snowflake-style profile groupings
4. Create src/cli/docs/SECURITY_REVIEW.md documenting:
- .mcp.json manipulation security:
a. Path traversal prevention with test evidence
b. Atomic file writes to prevent corruption
c. NEVER writing secrets (enforced at multiple layers)
- JSON injection prevention:
a. Input validation on project/profile names
b. Safe JSON serialization (no eval)
- Credential flow documentation:
a. Secrets stored server-side only
b. .mcp.json contains references, not values
c. CLI prompts for secrets locally when needed
- File permission recommendations (chmod 600)
5. Mock mcpd server enhancements:
- Add /api/projects/:name/mcp-config endpoint
- Return realistic MCP config structure
- Test error scenarios (404, 500, timeout)
6. Run full security audit:
- 'pnpm audit' for dependencies
- grep for console.log of sensitive data
- Verify no hardcoded credentials
- Document findings in SECURITY_REVIEW.md