// tools/discover-mcps.ts — Discover available MCP servers
// Reads MCP config files (Claude, Cursor, etc.) and catalogs installed MCPs
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { homedir } from 'node:os';
import { t } from '../i18n/index.js';
import { knowledgeStore } from '../storage/index.js';
// Known MCP config file locations
const MCP_CONFIG_PATHS = [
    // Claude Desktop
    { path: join(homedir(), '.claude', 'claude_desktop_config.json'), name: 'Claude Desktop' },
    {
        path: join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
        name: 'Claude Desktop (macOS)',
    },
    // Claude Code
    { path: '.claude.json', name: 'Claude Code (project)' },
    { path: join(homedir(), '.claude.json'), name: 'Claude Code (global)' },
    // Cursor
    { path: '.cursor/mcp.json', name: 'Cursor (project)' },
    { path: join(homedir(), '.cursor', 'mcp.json'), name: 'Cursor (global)' },
    // Windsurf
    { path: join(homedir(), '.codeium', 'windsurf', 'mcp_config.json'), name: 'Windsurf' },
];
export async function handleDiscoverMcps(args) {
    const { configPath, projectId } = args;
    const mcpServers = [];
    const discoveryLog = [];
    // 1. Check explicit config path if provided
    if (configPath) {
        const found = await parseConfigFile(configPath);
        if (found.length > 0) {
            mcpServers.push(...found);
            discoveryLog.push(`Found ${found.length} MCP(s) in ${configPath}`);
        }
        else {
            discoveryLog.push(`No MCPs found in ${configPath}`);
        }
    }
    // 2. Scan known config locations
    for (const loc of MCP_CONFIG_PATHS) {
        try {
            const found = await parseConfigFile(loc.path);
            if (found.length > 0) {
                // Avoid duplicates
                for (const server of found) {
                    if (!mcpServers.some((existing) => existing.name === server.name)) {
                        mcpServers.push(server);
                    }
                }
                discoveryLog.push(`Found ${found.length} MCP(s) in ${loc.name} (${loc.path})`);
            }
        }
        catch {
            // Config file does not exist, skip silently
        }
    }
    // 3. Check project knowledge for previously detected MCPs
    if (projectId) {
        const knowledge = await knowledgeStore.getKnowledge(projectId);
        if (knowledge?.availableMcps) {
            for (const mcp of knowledge.availableMcps) {
                if (!mcpServers.some((existing) => existing.name === mcp.name)) {
                    mcpServers.push(mcp);
                    discoveryLog.push(`Found ${mcp.name} from project knowledge`);
                }
            }
        }
    }
    const result = {
        mcpServers,
        message: mcpServers.length > 0
            ? `Discovered ${mcpServers.length} MCP server(s).`
            : 'No MCP servers discovered. Configure MCPs in your agent config file.',
    };
    // Update project knowledge with discovered MCPs
    if (projectId && mcpServers.length > 0) {
        const knowledge = await knowledgeStore.getKnowledge(projectId);
        if (knowledge) {
            await knowledgeStore.updateKnowledge(projectId, {
                availableMcps: mcpServers,
            });
        }
    }
    // Format output
    const lines = [
        `## ${t('tools.discover_mcps.description')}`,
        '',
        `**${result.message}**`,
        '',
    ];
    if (discoveryLog.length > 0) {
        lines.push('### Discovery Log');
        lines.push('');
        for (const entry of discoveryLog) {
            lines.push(`- ${entry}`);
        }
        lines.push('');
    }
    if (mcpServers.length > 0) {
        // SPEC-025 tech-debt: build category summary
        const categoryCounts = new Map();
        for (const server of mcpServers) {
            const category = classifyMcpCategory(server.relevantFor);
            categoryCounts.set(category, (categoryCounts.get(category) ?? 0) + 1);
        }
        const categorySummary = Array.from(categoryCounts.entries())
            .map(([cat, count]) => `${count} ${cat}`)
            .join(', ');
        lines.push('### Discovered MCP Servers');
        lines.push('');
        lines.push(`Found ${mcpServers.length} MCPs: ${categorySummary}`);
        lines.push('');
        lines.push('| Name | Command | Tools | Category |');
        lines.push('|------|---------|-------|----------|');
        for (const server of mcpServers) {
            const toolsList = server.tools.length > 3
                ? server.tools.slice(0, 3).join(', ') + ` (+${server.tools.length - 3} more)`
                : server.tools.join(', ');
            const category = classifyMcpCategory(server.relevantFor);
            lines.push(`| ${server.name} | \`${server.command}\` | ${toolsList || 'N/A'} | ${category} |`);
        }
        lines.push('');
        // Detailed per-server info
        for (const server of mcpServers) {
            const category = classifyMcpCategory(server.relevantFor);
            lines.push(`#### ${server.name}`);
            lines.push('');
            lines.push(`- **Command:** \`${server.command}\``);
            lines.push(`- **Category:** ${category}`);
            if (server.tools.length > 0) {
                lines.push(`- **Tools:** ${server.tools.join(', ')}`);
            }
            if (server.relevantFor.length > 0) {
                lines.push(`- **Relevant for:** ${server.relevantFor.join(', ')}`);
            }
            lines.push('');
        }
    }
    else {
        lines.push('### How to Add MCPs');
        lines.push('');
        lines.push('Configure MCP servers in your agent config:');
        lines.push('');
        lines.push('- **Claude Desktop:** `~/.claude/claude_desktop_config.json`');
        lines.push('- **Claude Code:** `.claude.json` in project root');
        lines.push('- **Cursor:** `.cursor/mcp.json` in project root');
        lines.push('');
        lines.push('Example config:');
        lines.push('```json');
        lines.push('{');
        lines.push('  "mcpServers": {');
        lines.push('    "my-server": {');
        lines.push('      "command": "npx",');
        lines.push('      "args": ["@my/mcp-server"]');
        lines.push('    }');
        lines.push('  }');
        lines.push('}');
        lines.push('```');
    }
    return {
        content: [{ type: 'text', text: lines.join('\n') }],
    };
}
// === Internal ===
async function parseConfigFile(filePath) {
    const servers = [];
    try {
        const raw = await readFile(filePath, 'utf-8');
        const config = JSON.parse(raw);
        if (!config.mcpServers) {
            return servers;
        }
        for (const [name, entry] of Object.entries(config.mcpServers)) {
            const command = entry.command ? [entry.command, ...(entry.args ?? [])].join(' ') : name;
            servers.push({
                name,
                command,
                tools: [], // Tools are discovered at runtime, not from config
                relevantFor: inferRelevance(name),
            });
        }
    }
    catch {
        // File does not exist or is invalid JSON
    }
    return servers;
}
function inferRelevance(mcpName) {
    const relevanceMap = {
        supabase: ['database', 'auth', 'storage'],
        postgres: ['database'],
        sqlite: ['database'],
        github: ['version-control', 'issues', 'prs'],
        linear: ['project-management'],
        slack: ['communication'],
        sentry: ['monitoring', 'error-tracking'],
        stripe: ['payments'],
        // SPEC-025 tech-debt: design MCPs
        figma: ['design', 'ui', 'mockup'],
        pencil: ['design', 'ui', 'wireframe'],
        'claude-ai-figma': ['design', 'ui'],
        puppeteer: ['testing', 'browser-automation'],
        playwright: ['testing', 'browser-automation'],
        railway: ['deployment'],
        vercel: ['deployment'],
    };
    const nameLower = mcpName.toLowerCase();
    for (const [key, relevance] of Object.entries(relevanceMap)) {
        if (nameLower.includes(key)) {
            return relevance;
        }
    }
    return [];
}
// SPEC-025 tech-debt: category classification from relevance domains
function classifyMcpCategory(relevantFor) {
    const domainCategoryMap = {
        database: 'database',
        auth: 'database',
        storage: 'database',
        deployment: 'deployment',
        infrastructure: 'deployment',
        design: 'design',
        ui: 'design',
        mockup: 'design',
        wireframe: 'design',
        monitoring: 'monitoring',
        'error-tracking': 'monitoring',
        observability: 'monitoring',
        communication: 'communication',
        'version-control': 'version-control',
        issues: 'version-control',
        prs: 'version-control',
        'project-management': 'project-management',
        ai: 'ai',
        testing: 'testing',
        'browser-automation': 'testing',
        payments: 'other',
    };
    for (const domain of relevantFor) {
        const category = domainCategoryMap[domain];
        if (category) {
            return category;
        }
    }
    return 'other';
}
//# sourceMappingURL=discover-mcps.js.map