"title":"Initialize Project Structure and Core Dependencies",
"description":"Set up the monorepo structure for mcpctl with CLI client, mcpd server, and shared libraries. Configure TypeScript, ESLint, and build tooling.",
"details":"Create a monorepo using pnpm workspaces or npm workspaces with the following structure:\n\n```\nmcpctl/\n├── src/\n│ ├── cli/ # mcpctl CLI tool\n│ ├── mcpd/ # Backend daemon server\n│ ├── shared/ # Shared types, utilities, constants\n│ └── local-proxy/ # Local LLM proxy component\n├── deploy/\n│ └── docker-compose.yml\n├── package.json\n├── tsconfig.base.json\n└── pnpm-workspace.yaml\n```\n\nDependencies to install:\n- TypeScript 5.x\n- Commander.js for CLI\n- Express/Fastify for mcpd HTTP server\n- Zod for schema validation\n- Winston/Pino for logging\n- Prisma or Drizzle for database ORM\n\nCreate base tsconfig.json with strict mode, ES2022 target, and module resolution settings. Set up shared ESLint config with TypeScript rules.",
"testStrategy":"Verify project builds successfully with `pnpm build`. Ensure all packages compile without errors. Test workspace linking works correctly between packages.",
"priority":"high",
"dependencies":[],
"status":"done",
"subtasks":[
{
"id":1,
"title":"Initialize pnpm workspace monorepo with future-proof directory structure",
"description":"Create the complete monorepo directory structure using pnpm workspaces that accommodates all 18 planned tasks without requiring future refactoring.",
"testStrategy":"Write Vitest tests that verify: (1) All expected directories exist, (2) All package.json files are valid JSON with correct workspace protocol dependencies, (3) pnpm-workspace.yaml correctly includes all packages, (4) Running 'pnpm install' succeeds and creates correct node_modules symlinks between packages. Run 'pnpm ls' to verify workspace linking.",
"title":"Configure TypeScript with strict mode and project references",
"description":"Set up TypeScript configuration with strict mode, ES2022 target, and proper project references for monorepo build orchestration.",
"dependencies":[
1
],
"details":"Create root tsconfig.base.json with shared compiler options. Create package-specific tsconfig.json in each package that extends the base and sets appropriate paths.",
"title":"Set up Vitest testing framework with workspace configuration",
"description":"Configure Vitest as the test framework across all packages with proper workspace setup, coverage reporting, and test-driven development infrastructure.",
"dependencies":[
2
],
"details":"Install Vitest and related packages at root level. Create root vitest.config.ts and vitest.workspace.ts for workspace-aware testing pointing to src/cli, src/mcpd, src/shared, src/local-proxy, src/db.",
"title":"Configure ESLint with TypeScript rules and docker-compose for local development",
"description":"Set up shared ESLint configuration with TypeScript-aware rules, Prettier integration, and docker-compose.yml for local PostgreSQL database.",
"dependencies":[
2
],
"details":"Install ESLint and plugins at root. Create eslint.config.js (flat config, ESLint 9+). Create deploy/docker-compose.yml for local development with PostgreSQL service.",
"testStrategy":"Write Vitest tests that verify eslint.config.js exists and exports valid config, deploy/docker-compose.yml is valid YAML and defines postgres service.",
"title":"Install core dependencies and perform security/architecture review",
"description":"Install all required production dependencies across packages, run security audit, and validate the directory structure supports all 18 planned tasks.",
"dependencies":[
1,
3,
4
],
"details":"Install dependencies per package in src/cli, src/mcpd, src/shared, src/db, src/local-proxy. Perform security and architecture review.",
"description":"Create the database schema for storing MCP server configurations, projects, profiles, user sessions, and audit logs. Use PostgreSQL for production readiness.",
"details":"Design PostgreSQL schema using Prisma ORM with models: User, McpServer, McpProfile, Project, ProjectMcpProfile, McpInstance, AuditLog, Session. Create migrations and seed data for common MCP servers (slack, jira, github, terraform).",
"testStrategy":"Run Prisma migrations against test database. Verify all relations work correctly with seed data. Test CRUD operations for each model using Prisma client.",
"priority":"high",
"dependencies":[
"1"
],
"status":"pending",
"subtasks":[
{
"id":1,
"title":"Set up Prisma ORM and PostgreSQL test infrastructure with docker-compose",
"description":"Initialize Prisma in the db package with PostgreSQL configuration, create docker-compose.yml for local development with separate test database.",
"dependencies":[],
"details":"Create src/db/prisma directory structure. Install Prisma dependencies. Configure deploy/docker-compose.yml with two PostgreSQL services: mcpctl-postgres (port 5432) for development and mcpctl-postgres-test (port 5433) for testing.",
"title":"Write TDD tests for all Prisma models before implementing schema",
"description":"Create comprehensive Vitest test suites for all 8 models testing CRUD operations, relations, constraints, and edge cases.",
"dependencies":[
1
],
"details":"Create src/db/tests/models directory with separate test files for each model. Tests will initially fail (TDD red phase) until schema is implemented.",
"title":"Implement Prisma schema with all models and security considerations",
"description":"Create the complete Prisma schema with all 8 models, proper relations, indexes for audit queries, and security-conscious field design.",
"dependencies":[
2
],
"details":"Implement src/db/prisma/schema.prisma with all models. Add version Int field and updatedAt DateTime for git-based backup support.",
"description":"Build the mcpd daemon server with Express/Fastify, including middleware for authentication, logging, and error handling. Design for horizontal scalability.",
"details":"Create mcpd server in src/mcpd/src/ with Fastify, health check endpoint, auth middleware, and audit logging. Design for statelessness and scalability.",
"testStrategy":"Unit test middleware functions. Integration test health endpoint. Load test with multiple concurrent requests. Verify statelessness by running two instances.",
"priority":"high",
"dependencies":[
"1",
"2"
],
"status":"pending",
"subtasks":[
{
"id":1,
"title":"Set up mcpd package structure with clean architecture layers and TDD infrastructure",
"description":"Create the src/mcpd directory structure following clean architecture principles with separate layers for routes, controllers, services, and repositories.",
"title":"Implement MCP Server Registry and Profile Management",
"description":"Create APIs for registering MCP servers, managing profiles with different permission levels, and storing configuration templates.",
"details":"Create REST API endpoints in mcpd for MCP server and profile CRUD operations with seed data for common servers.",
"testStrategy":"Test CRUD operations for servers and profiles. Verify profile inheritance works. Test that invalid configurations are rejected by Zod validation.",
"priority":"high",
"dependencies":[
"3"
],
"status":"pending",
"subtasks":[
{
"id":1,
"title":"Create Zod validation schemas with comprehensive TDD test coverage",
"description":"Define and test Zod schemas for MCP server registration, profile management, and configuration templates before implementing any routes.",
"dependencies":[],
"details":"Create src/mcpd/src/validation/mcp-server.schema.ts with CreateMcpServerSchema, UpdateMcpServerSchema, CreateMcpProfileSchema.",
"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/ with Commander.js, configuration management at ~/.mcpctl/config.json, and API client for mcpd.",
"testStrategy":"Test CLI argument parsing. Test configuration persistence. Mock API calls and verify request formatting.",
"priority":"high",
"dependencies":[
"1"
],
"status":"pending",
"subtasks":[
{
"id":1,
"title":"Set up CLI package structure with TDD infrastructure and command registry pattern",
"description":"Create src/cli directory structure with Commander.js foundation, Vitest test configuration, and extensible command registry.",
"dependencies":[],
"details":"Create src/cli/src/ with commands/, config/, client/, formatters/, utils/, types/ directories and registry pattern.",
"title":"Complete MCP Registry Client with Proxy, Metrics Exposure, and HTTP/CA Support",
"description":"Finalize the registry client implementation by adding HTTP proxy support, custom CA certificates for enterprise environments, and exposing SRE metrics via a dedicated metrics interface. The core client with strategy pattern, caching, deduplication, and ranking is already implemented.",
"details":"The registry client foundation already exists in src/cli/src/registry/ with:\n- RegistryClient class with search(), caching, metrics tracking\n- OfficialRegistrySource, GlamaRegistrySource, SmitheryRegistrySource\n- Deduplication by npm package/repo URL\n- Ranking by relevance, popularity, verified status\n- Zod validation of API responses\n- sanitizeString() for XSS prevention\n\nRemaining implementation:\n\n1. **HTTP Proxy & Custom CA Support** (src/cli/src/registry/http-agent.ts):\n```typescript\nimport { Agent } from 'undici';\nimport { ProxyAgent } from 'undici';\nimport fs from 'node:fs';\n\nexport function createHttpAgent(config: {\n httpProxy?: string;\n httpsProxy?: string;\n caPath?: string;\n}): Agent | ProxyAgent | undefined {\n const proxy = config.httpsProxy ?? config.httpProxy;\n if (proxy) {\n const ca = config.caPath ? fs.readFileSync(config.caPath) : undefined;\n return new ProxyAgent({ uri: proxy, connect: { ca } });\n }\n if (config.caPath) {\n const ca = fs.readFileSync(config.caPath);\n return new Agent({ connect: { ca } });\n }\n return undefined;\n}\n```\n\n2. **Update fetch calls** in each source to accept dispatcher option:\n```typescript\n// In retry.ts or each source\nconst agent = createHttpAgent(config);\nconst response = await fetch(url, { dispatcher: agent });\n```\n\n3. **Metrics Exposure Interface** (src/cli/src/registry/metrics.ts):\n```typescript\nexport interface RegistryMetrics {\n queryLatencyMs: { source: string; latencies: number[] }[];\n cacheHitRatio: number;\n cacheHits: number;\n cacheMisses: number;\n errorCounts: { source: string; count: number }[];\n}\n\nexport function collectMetrics(client: RegistryClient): RegistryMetrics {\n const cacheMetrics = client.getCacheMetrics();\n return {\n queryLatencyMs: Array.from(client.getQueryLatencies().entries())\n .map(([source, latencies]) => ({ source, latencies })),\n cacheHitRatio: cacheMetrics.ratio,\n cacheHits: cacheMetrics.hits,\n cacheMisses: cacheMetrics.misses,\n errorCounts: Array.from(client.getErrorCounts().entries())\n .map(([source, count]) => ({ source, count })),\n };\n}\n```\n\n4. **Update RegistryClientConfig** to include caPath:\n```typescript\nexport interface RegistryClientConfig {\n registries?: RegistryName[];\n cacheTTLMs?: number;\n smitheryApiKey?: string;\n httpProxy?: string;\n httpsProxy?: string;\n caPath?: string; // ADD THIS\n}\n```\n\n5. **Add data platform category filter** - update SearchOptions:\n```typescript\ncategory?: 'devops' | 'data-platform' | 'analytics' | 'communication' | 'development' | string;\n```\n\nTDD approach:\n- Write tests for createHttpAgent() with proxy, CA, and combined configs\n- Write tests for metrics collection interface\n- Write tests for category filtering in search results\n- All tests should be written BEFORE implementation",
"testStrategy":"1. Unit tests for http-agent.ts: verify ProxyAgent created with correct proxy URI, verify custom CA loaded from file path, verify combined proxy+CA configuration\n2. Unit tests for metrics.ts: verify collectMetrics() returns correct structure, verify latency arrays are captured per-source\n3. Integration test: mock HTTP server with self-signed cert, verify client connects with custom CA\n4. Test category filtering returns only servers matching category\n5. Run existing test suite to ensure no regressions: pnpm --filter @mcpctl/cli test",
"priority":"high",
"dependencies":[],
"status":"pending",
"subtasks":[]
},
{
"id":26,
"title":"Implement mcpctl discover Command with Interactive Mode",
"description":"Create the `mcpctl discover` CLI command that lets users search for MCP servers across all configured registries with rich filtering, table/JSON/YAML output, and an interactive browsing mode using inquirer.",
"details":"Create src/cli/src/commands/discover.ts with Commander.js:\n\n```typescript\nimport { Command } from 'commander';\nimport { RegistryClient, type SearchOptions, type RegistryServer } from '../registry/index.js';\nimport { getConfig } from '../config/index.js';\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\n\nexport function createDiscoverCommand(): Command {\n return new Command('discover')\n .description('Search for MCP servers across registries')\n .argument('<query>', 'Search query (e.g., \"slack\", \"database\", \"terraform\")')\n.option('-c,--category<category>','Filterbycategory(devops,data-platform,analytics)')\n.option('-v,--verified','Onlyshowverifiedservers')\n.option('-t,--transport<type>','Filterbytransport(stdio,sse)',undefined)\n.option('-r,--registry<registry>','Queryspecificregistry(official,glama,smithery,all)','all')\n.option('-l,--limit<n>','Maximumresults','20')\n.option('-o,--output<format>','Outputformat(table,json,yaml)','table')\n.option('-i,--interactive','Interactivebrowsingmode')\n.action(async(query,options)=>{\nconstconfig=awaitgetConfig();\nconstclient=newRegistryClient({\nsmitheryApiKey:config.smitheryApiKey,\nhttpProxy:config.httpProxy,\nhttpsProxy:config.httpsProxy,\ncaPath:config.caPath,\n});\n\nconstsearchOpts:SearchOptions={\nquery,\nlimit:parseInt(options.limit,10),\nverified:options.verified,\ntransport:options.transport,\ncategory:options.category,\nregistries:options.registry==='all'\n?undefined\n:[options.registry],\n};\n\nconstresults=awaitclient.search(searchOpts);\n\nif(results.length===0){\nconsole.log('Noserversfoundmatchingyourquery.');\nprocess.exitCode=2;\nreturn;\n}\n\nif(options.interactive){\nawaitrunInteractiveMode(results);\n}else{\noutputResults(results,options.output);\n}\n});\n}\n\nfunctionoutputResults(results:RegistryServer[],format:string):void{\nswitch(format){\ncase'json':\nconsole.log(JSON.stringify(results,null,2));\nbreak;\ncase'yaml':\n// Use yaml library\n import('yaml').then(yaml => console.log(yaml.stringify(results)));\n break;\n default:\n printTable(results);\n }\n}\n\nfunction printTable(results: RegistryServer[]): void {\n console.log('NAME'.padEnd(30) + 'DESCRIPTION'.padEnd(50) + 'PACKAGE'.padEnd(35) + 'TRANSPORT VERIFIED POPULARITY');\n console.log('-'.repeat(140));\n for (const s of results) {\n const pkg = s.packages.npm ?? s.packages.pypi ?? s.packages.docker ?? '-';\n const verified = s.verified ? chalk.green('✓') : '-';\n console.log(\n s.name.slice(0, 28).padEnd(30) +\n s.description.slice(0, 48).padEnd(50) +\n pkg.slice(0, 33).padEnd(35) +\n s.transport.padEnd(11) +\n verified.padEnd(10) +\n String(s.popularityScore)\n );\n }\n console.log(`\\nRun 'mcpctl install <name>' to set up a server.`);\n}\n\nasync function runInteractiveMode(results: RegistryServer[]): Promise<void> {\n const { selected } = await inquirer.prompt([{\n type: 'list',\n name: 'selected',\n message: 'Select an MCP server to install:',\n choices: results.map(s => ({\n name: `${s.name} - ${s.description.slice(0, 60)}`,\n value: s,\n })),\n }]);\n\n const { action } = await inquirer.prompt([{\n type: 'list',\n name: 'action',\n message: `What would you like to do with ${selected.name}?`,\n choices: [\n { name: 'Install and configure', value: 'install' },\n { name: 'View details', value: 'details' },\n { name: 'Cancel', value: 'cancel' },\n ],\n }]);\n\n if (action === 'install') {\n // Invoke install command programmatically\n const { execSync } = await import('node:child_process');\n execSync(`mcpctl install ${selecte
"testStrategy":"1. Unit tests (src/cli/tests/commands/discover.test.ts):\n - Test argument parsing: verify query is required, options have defaults\n - Test table output: mock RegistryClient, verify correct columns printed\n - Test JSON output: verify valid JSON with all fields\n - Test YAML output: verify valid YAML structure\n - Test --verified filter is passed to client\n - Test --registry parses correctly\n2. Integration tests:\n - Mock registry sources, run full discover command, verify output\n - Test exit code 2 when no results\n - Test exit code 1 on network error\n3. Interactive mode tests:\n - Mock inquirer responses, verify correct server selected\n - Verify install command invoked with correct name\n4. Run: pnpm --filter @mcpctl/cli test",
"priority":"high",
"dependencies":[
25
],
"status":"pending",
"subtasks":[]
},
{
"id":27,
"title":"Implement mcpctl install with LLM-Assisted Auto-Configuration",
"description":"Create the `mcpctl install <server-name>` command that uses a local LLM (Claude Code session, Ollama, or configured provider) to automatically analyze MCP server READMEs, generate envTemplate and setup guides, walk users through configuration, and register the server in mcpd.",
"details":"Create src/cli/src/commands/install.ts:\n\n```typescript\nimport { Command } from 'commander';\nimport { RegistryClient, type RegistryServer, type EnvVar } from '../registry/index.js';\nimport { getConfig } from '../config/index.js';\nimport { z } from 'zod';\nimport inquirer from 'inquirer';\n\n// Zod schema for validating LLM-generated envTemplate\nconst LLMEnvVarSchema = z.object({\n name: z.string().min(1),\n description: z.string(),\n isSecret: z.boolean(),\n setupUrl: z.string().url().optional(),\n defaultValue: z.string().optional(),\n});\n\nconst LLMConfigResponseSchema = z.object({\n envTemplate: z.array(LLMEnvVarSchema),\n setupGuide: z.array(z.string()),\n defaultProfiles: z.array(z.object({\n name: z.string(),\n permissions: z.array(z.string()),\n })).optional().default([]),\n});\n\nexport type LLMConfigResponse = z.infer<typeof LLMConfigResponseSchema>;\n\nexport function createInstallCommand(): Command {\n return new Command('install')\n .description('Install and configure an MCP server')\n .argument('<servers...>', 'Server name(s) from discover results')\n .option('--non-interactive', 'Use env vars for credentials (no prompts)')\n .option('--profile-name <name>', 'Name for the created profile')\n .option('--project <name>', 'Add to existing project after install')\n .option('--dry-run', 'Show configuration without applying')\n .option('--skip-llm', 'Skip LLM analysis, use registry metadata only')\n .action(async (servers, options) => {\n for (const serverName of servers) {\n await installServer(serverName, options);\n }\n });\n}\n\nasync function installServer(serverName: string, options: {\n nonInteractive?: boolean;\n profileName?: string;\n project?: string;\n dryRun?: boolean;\n skipLlm?: boolean;\n}): Promise<void> {\n const config = await getConfig();\n const client = new RegistryClient(config);\n\n // Step 1: Fetch server metadata from registry\n console.log(`Searching for ${serverName}...`);\n const results = await client.search({ query: serverName, limit: 10 });\n const server = results.find(s => \n s.name.toLowerCase() === serverName.toLowerCase() ||\n s.packages.npm?.includes(serverName)\n );\n\n if (!server) {\n console.error(`Server \"${serverName}\"notfound.Run'mcpctldiscover${serverName}'tosearch.`);\nprocess.exitCode=1;\nreturn;\n}\n\nconsole.log(`Found:${server.name}(${server.packages.npm??server.packages.docker??'N/A'})`);\n\n// Step 2: Determine envTemplate\n let envTemplate: EnvVar[] = server.envTemplate;\n let setupGuide: string[] = [];\n\n // Step 3: If envTemplate incomplete and LLM not skipped, use LLM\n if (envTemplate.length === 0 && !options.skipLlm && server.repositoryUrl) {\n console.log('Registry metadata incomplete. Analyzing README with LLM...');\n const llmConfig = await analyzeWithLLM(server.repositoryUrl, config);\n if (llmConfig) {\n envTemplate = llmConfig.envTemplate;\n setupGuide = llmConfig.setupGuide;\n }\n }\n\n // Step 4: Show setup guide if available\n if (setupGuide.length > 0) {\n console.log('\\n📋 Setup Guide:');\n setupGuide.forEach((step, i) => console.log(` ${i + 1}. ${step}`));\n console.log('');\n }\n\n if (options.dryRun) {\n console.log('Dry run - would configure:');\n console.log(JSON.stringify({ server, envTemplate }, null, 2));\n return;\n }\n\n // Step 5: Collect credentials\n const credentials: Record<string, string> = {};\n if (!options.nonInteractive) {\n for (const env of envTemplate) {\n const { value } = await inquirer.prompt([{\n type: env.isSecret ? 'password' : 'input',\n name: 'value',\n message: `${env.name}${env.description ? ` (${env.description})` : ''}:`,\n default: env.defaultValue,\n }]);\n credentials[env.name] = value;\n }\n } else {\n // Use environment variables\n for (const env of envTemplate) {\n credentials[env.name] = process.env[env.name] ?? env.defaultValue ?? '';\n }\n }\
"testStrategy":"1. Unit tests (src/cli/tests/commands/install.test.ts):\n - Test server lookup from registry results\n - Test LLMConfigResponseSchema validates correct JSON\n - Test LLMConfigResponseSchema rejects invalid JSON\n - Test sanitizeReadme() removes injection patterns\n - Test buildLLMPrompt() generates valid prompt structure\n - Test convertToRawReadmeUrl() for various GitHub URL formats\n - Test --dry-run outputs config without saving\n - Test --non-interactive uses env vars\n - Test batch install: multiple servers processed sequentially\n2. Security tests:\n - Test sanitizeReadme blocks 'ignore all instructions'\n - Test LLM response with extra fields is safely parsed\n - Test credentials are not logged\n3. Integration tests:\n - Mock registry client and LLM endpoint\n - Full install flow with mocked dependencies\n - Verify server config file created with correct structure\n4. Run: pnpm --filter @mcpctl/cli test",