Files
mcpctl/.taskmaster/tasks/task_007.md

312 lines
10 KiB
Markdown
Raw Permalink Normal View History

2026-02-21 03:10:39 +00:00
# Task ID: 7
**Title:** Build mcpctl CLI Core Framework
**Status:** pending
**Dependencies:** 1
**Priority:** high
**Description:** Create the CLI tool foundation using Commander.js with kubectl-inspired command structure, configuration management, and server communication.
**Details:**
Create CLI in `src/cli/src/`:
```typescript
// index.ts
import { Command } from 'commander';
import { loadConfig, saveConfig } from './config';
const program = new Command();
program
.name('mcpctl')
.description('kubectl-like CLI for managing MCP servers')
.version('0.1.0');
// Config management
program
.command('config')
.description('Manage mcpctl configuration')
.addCommand(
new Command('set-server')
.argument('<url>', 'mcpd server URL')
.action((url) => {
const config = loadConfig();
config.serverUrl = url;
saveConfig(config);
console.log(`Server set to ${url}`);
})
)
.addCommand(
new Command('view')
.action(() => console.log(loadConfig()))
);
// API client
class McpctlClient {
constructor(private serverUrl: string, private token?: string) {}
async get(path: string) {
const res = await fetch(`${this.serverUrl}${path}`, {
headers: this.token ? { Authorization: `Bearer ${this.token}` } : {}
});
return res.json();
}
async post(path: string, data: any) {
const res = await fetch(`${this.serverUrl}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(this.token ? { Authorization: `Bearer ${this.token}` } : {})
},
body: JSON.stringify(data)
});
return res.json();
}
}
```
Config file at `~/.mcpctl/config.json`:
```json
{
"serverUrl": "http://localhost:3000",
"token": "..."
}
```
**Test Strategy:**
Test CLI argument parsing. Test configuration persistence. Mock API calls and verify request formatting. Test error handling for network failures.
## Subtasks
### 7.1. Set up CLI package structure with TDD infrastructure and command registry pattern
**Status:** pending
**Dependencies:** None
Create src/cli directory structure with Commander.js foundation, Vitest test configuration, and extensible command registry pattern designed to scale for all planned commands (get, describe, apply, setup, project, claude, audit, start, stop, logs).
**Details:**
Create src/cli/src/ with the following structure:
- commands/ - Command modules (empty initially, registry pattern)
- config/ - Configuration loading and validation
- client/ - API client for mcpd communication
- formatters/ - Output formatters (table, json, yaml)
- utils/ - Utility functions
- types/ - TypeScript interfaces
- index.ts - Main entry point with Commander setup
Create src/cli/tests/ with matching structure:
- unit/ (commands, config, client, formatters)
- integration/ (CLI end-to-end tests)
- fixtures/ (mock data, mock server)
Implement command registry pattern in src/commands/registry.ts:
```typescript
export interface CommandModule {
name: string;
register(program: Command): void;
}
export class CommandRegistry {
register(module: CommandModule): void;
registerAll(program: Command): void;
}
```
Set up vitest.config.ts extending root config. Install dependencies: commander, chalk, js-yaml, inquirer, zod for validation. DevDependencies: vitest, @vitest/coverage-v8, msw for API mocking.
Write initial TDD tests before implementation:
- tests/unit/commands/registry.test.ts - Test registry adds commands correctly
- tests/unit/index.test.ts - Test CLI entry point parses version, help
### 7.2. Implement secure configuration management with encrypted credential storage
**Status:** pending
**Dependencies:** 7.1
Create configuration loader/saver with ~/.mcpctl/config.json for settings and ~/.mcpctl/credentials encrypted storage for tokens. Include proxy settings, custom CA certificates support, and Zod validation for enterprise environments.
**Details:**
Create src/cli/src/config/index.ts with:
- loadConfig(): McpctlConfig - Load from ~/.mcpctl/config.json with Zod validation
- saveConfig(config: McpctlConfig): void - Save config atomically (write to temp, rename)
- getConfigPath(): string - Platform-aware config directory
- initConfig(): void - Create config directory and initial config if not exists
Create src/cli/src/config/credentials.ts with SECURE credential storage:
- loadCredentials(): Credentials - Load encrypted from ~/.mcpctl/credentials
- saveCredentials(creds: Credentials): void - Encrypt and save credentials
- Use platform keychain when available (keytar package), fallback to encrypted file
- NEVER store tokens in plain text or config.json
- NEVER log tokens or include in error messages
Create McpctlConfig schema with Zod:
```typescript
const ConfigSchema = z.object({
serverUrl: z.string().url().default('http://localhost:3000'),
proxy: z.object({
http: z.string().url().optional(),
https: z.string().url().optional(),
noProxy: z.array(z.string()).optional()
}).optional(),
tls: z.object({
caFile: z.string().optional(), // Custom CA certificate path
insecureSkipVerify: z.boolean().default(false) // For dev only
}).optional(),
output: z.object({
format: z.enum(['table', 'json', 'yaml']).default('table'),
color: z.boolean().default(true)
}).optional()
});
```
Secure token handling:
- loadToken(): string | undefined - Get token from credentials store
- saveToken(token: string): void - Encrypt and save
- clearToken(): void - Securely delete token
Write TDD tests BEFORE implementation in tests/unit/config/
### 7.3. Implement McpctlClient API client with enterprise networking support
**Status:** pending
**Dependencies:** 7.2
Create the HTTP API client for communicating with mcpd server with proper error handling, retry logic, proxy support, custom CA certificates, and request/response interceptors for authentication.
**Details:**
Create src/cli/src/client/index.ts with McpctlClient class:
```typescript
export class McpctlClient {
constructor(config: ClientConfig) // DI for testability
// HTTP methods with proper typing
async get<T>(path: string): Promise<T>
async post<T>(path: string, data: unknown): Promise<T>
async put<T>(path: string, data: unknown): Promise<T>
async delete<T>(path: string): Promise<T>
// Health check for connection testing
async healthCheck(): Promise<boolean>
}
```
Implement networking features:
- Proxy support: Use HTTP_PROXY/HTTPS_PROXY env vars + config.proxy settings
- Custom CA: Support config.tls.caFile for enterprise CAs
- Retry logic: Exponential backoff for transient failures (503, network errors)
- Timeout: Configurable request timeout (default 30s)
- Request interceptor: Add Authorization header from credentials store
- Response interceptor: Handle 401 (clear cached token, prompt re-auth)
Create src/cli/src/client/errors.ts:
- McpctlClientError base class
- NetworkError for connection failures
- AuthenticationError for 401
- NotFoundError for 404
- ServerError for 5xx
IMPORTANT: Never log request bodies that might contain secrets. Redact Authorization header in debug logs.
Create mock server in tests/fixtures/mock-server.ts using msw (Mock Service Worker) for offline testing. Write TDD tests before implementation.
### 7.4. Implement config command group with output formatters for SRE integration
**Status:** pending
**Dependencies:** 7.2, 7.3
Create the config command group (set-server, view, get-token, set-token) and multi-format output system (table, json, yaml) with --output flag designed for SRE tooling integration (jq, grep, monitoring pipelines).
**Details:**
Create src/cli/src/commands/config.ts implementing CommandModule:
```typescript
// config set-server <url>
// config view
// config set-token (interactive, secure input)
// config clear-token
// config set <key> <value> (generic setter for proxy, tls, etc.)
// config get <key>
```
Create src/cli/src/formatters/index.ts:
```typescript
export function formatOutput(data: unknown, format: OutputFormat): string
export function printTable(data: Record<string, unknown>[], columns: ColumnDef[]): void
export function printJson(data: unknown): void // Pretty printed, sortable keys
export function printYaml(data: unknown): void // Clean YAML output
```
SRE-friendly output requirements:
- JSON output must be valid, parseable by jq
- YAML output must be valid, parseable by yq
- Table output should be grep-friendly (consistent column widths)
- All formats support --no-color for CI/scripting
- Add --quiet flag to suppress non-essential output
- Exit codes: 0 success, 1 error, 2 invalid arguments
Add global --output/-o flag to main program:
```typescript
program.option('-o, --output <format>', 'Output format (table, json, yaml)', 'table');
```
Register config command via CommandRegistry. Write TDD tests before implementation.
### 7.5. Create mock mcpd server and comprehensive security/architecture review
**Status:** pending
**Dependencies:** 7.1, 7.2, 7.3, 7.4
Build mock mcpd server for offline CLI testing, write integration tests verifying CLI works against local docker-compose mcpd, and perform comprehensive security review of credential handling, CLI history protection, and token security.
**Details:**
Create src/cli/tests/fixtures/mock-mcpd-server.ts:
- Full mock of mcpd API endpoints using msw or express
- Realistic response data for servers, profiles, projects
- Configurable error scenarios (timeout, 500, 401)
- Startup/shutdown helpers for test lifecycle
Create src/cli/tests/integration/cli.test.ts:
- Full CLI integration tests using execSync against built CLI
- Test against mock server in CI, real docker-compose in local dev
- Test full workflow: config -> connect -> list resources
SECURITY REVIEW - create src/cli/SECURITY_REVIEW.md:
1. Credential Storage Security:
- Verify credentials encrypted at rest (not plain JSON)
- Verify keychain integration on macOS/Windows
- Verify file permissions are 600 on credential file
2. CLI History Protection:
- Document that tokens should NEVER be passed as CLI arguments
- set-token uses stdin or prompt, not --token=xxx
- Verify no sensitive data in bash history
3. Token Handling:
- Verify tokens never logged (search codebase for console.log patterns)
- Verify error messages don't leak tokens
- Verify tokens redacted in debug output
4. Network Security:
- Document TLS verification (not disabled by default)
- Document proxy credential handling
- Verify no credentials sent over non-HTTPS in production
Run security audit: 'pnpm audit --audit-level=high'. Document findings.
Run 'pnpm lint' and 'pnpm test:coverage' ensuring >80% coverage for CLI package.