/**
* MCP Matching Module
*
* Matches detected technologies to recommended MCP servers.
* Uses tech→MCP mappings from the database catalog.
*/
import type {
DetectedStack,
MCPRecommendation,
MCPInstallConfigs,
MCPPriority,
} from "./types.js";
import { info } from "../../utils/logger.js";
// ============================================================================
// MCP TECH MAPPINGS (Local Lookup Table)
// ============================================================================
/**
* Local mapping of technologies to recommended MCPs.
* This is used when database is not available or for fast local matching.
*/
export const TECH_MCP_MAPPINGS: Array<{
techIdentifier: string;
mcpSlug: string;
priority: MCPPriority;
reason: string;
category: string;
}> = [
// ==========================================================================
// DATABASE SERVICES
// ==========================================================================
{
techIdentifier: "supabase",
mcpSlug: "supabase-mcp",
priority: "high",
reason: "Direct database access with RLS-aware queries",
category: "database",
},
{
techIdentifier: "neon",
mcpSlug: "neon-mcp",
priority: "high",
reason: "Branch management and serverless Postgres operations",
category: "database",
},
{
techIdentifier: "planetscale",
mcpSlug: "planetscale-mcp",
priority: "high",
reason: "Database branching and schema management",
category: "database",
},
{
techIdentifier: "mongodb",
mcpSlug: "mongodb-mcp",
priority: "high",
reason: "MongoDB Atlas operations and query assistance",
category: "database",
},
{
techIdentifier: "postgresql",
mcpSlug: "postgres-mcp",
priority: "medium",
reason: "PostgreSQL database operations and query building",
category: "database",
},
{
techIdentifier: "redis",
mcpSlug: "upstash-mcp",
priority: "medium",
reason: "Redis operations with rate limiting support",
category: "database",
},
{
techIdentifier: "upstash",
mcpSlug: "upstash-mcp",
priority: "high",
reason: "Upstash Redis and QStash management",
category: "database",
},
{
techIdentifier: "firebase",
mcpSlug: "firebase-mcp",
priority: "high",
reason: "Firestore, Auth, and Storage operations",
category: "database",
},
// ==========================================================================
// PAYMENT SERVICES
// ==========================================================================
{
techIdentifier: "stripe",
mcpSlug: "stripe-mcp",
priority: "high",
reason: "Payment processing, subscriptions, and webhook management",
category: "payments",
},
{
techIdentifier: "paddle",
mcpSlug: "paddle-mcp",
priority: "high",
reason: "Paddle billing, subscriptions, and tax handling",
category: "payments",
},
// ==========================================================================
// VERSION CONTROL
// ==========================================================================
{
techIdentifier: "github",
mcpSlug: "github-mcp",
priority: "high",
reason: "GitHub repository, issues, and PR management",
category: "version-control",
},
// ==========================================================================
// HOSTING PLATFORMS
// ==========================================================================
{
techIdentifier: "vercel",
mcpSlug: "vercel-mcp",
priority: "medium",
reason: "Deployment and project configuration",
category: "hosting",
},
{
techIdentifier: "cloudflare",
mcpSlug: "cloudflare-mcp",
priority: "medium",
reason: "Workers, KV, R2, and DNS management",
category: "hosting",
},
{
techIdentifier: "netlify",
mcpSlug: "netlify-mcp",
priority: "medium",
reason: "Site deployment and function management",
category: "hosting",
},
{
techIdentifier: "fly",
mcpSlug: "fly-mcp",
priority: "medium",
reason: "Fly.io deployment and scaling",
category: "hosting",
},
{
techIdentifier: "railway",
mcpSlug: "railway-mcp",
priority: "medium",
reason: "Railway deployment and database management",
category: "hosting",
},
// ==========================================================================
// PRODUCTIVITY & COLLABORATION
// ==========================================================================
{
techIdentifier: "notion",
mcpSlug: "notion-mcp",
priority: "medium",
reason: "Notion workspace operations",
category: "productivity",
},
{
techIdentifier: "slack",
mcpSlug: "slack-mcp",
priority: "medium",
reason: "Slack messaging and channel management",
category: "communication",
},
{
techIdentifier: "discord",
mcpSlug: "discord-mcp",
priority: "low",
reason: "Discord bot and server management",
category: "communication",
},
{
techIdentifier: "linear",
mcpSlug: "linear-mcp",
priority: "medium",
reason: "Linear issue tracking and project management",
category: "productivity",
},
// ==========================================================================
// MONITORING & ANALYTICS
// ==========================================================================
{
techIdentifier: "sentry",
mcpSlug: "sentry-mcp",
priority: "medium",
reason: "Error tracking and performance monitoring",
category: "monitoring",
},
{
techIdentifier: "posthog",
mcpSlug: "posthog-mcp",
priority: "low",
reason: "Product analytics and feature flags",
category: "analytics",
},
// ==========================================================================
// EMAIL SERVICES
// ==========================================================================
{
techIdentifier: "resend",
mcpSlug: "resend-mcp",
priority: "medium",
reason: "Transactional email management",
category: "email",
},
{
techIdentifier: "sendgrid",
mcpSlug: "sendgrid-mcp",
priority: "medium",
reason: "Email sending and template management",
category: "email",
},
// ==========================================================================
// AI/LLM SERVICES
// ==========================================================================
{
techIdentifier: "openai",
mcpSlug: "openai-mcp",
priority: "medium",
reason: "OpenAI API management and model selection",
category: "ai-llm",
},
{
techIdentifier: "anthropic",
mcpSlug: "anthropic-mcp",
priority: "medium",
reason: "Claude API management",
category: "ai-llm",
},
// ==========================================================================
// TESTING
// ==========================================================================
{
techIdentifier: "playwright",
mcpSlug: "playwright-mcp",
priority: "high",
reason: "Browser automation and E2E testing",
category: "testing",
},
{
techIdentifier: "puppeteer",
mcpSlug: "puppeteer-mcp",
priority: "medium",
reason: "Headless browser automation",
category: "testing",
},
// ==========================================================================
// CLOUD PROVIDERS
// ==========================================================================
{
techIdentifier: "aws",
mcpSlug: "aws-mcp",
priority: "medium",
reason: "AWS service management",
category: "hosting",
},
// ==========================================================================
// SEARCH
// ==========================================================================
{
techIdentifier: "elasticsearch",
mcpSlug: "elasticsearch-mcp",
priority: "medium",
reason: "Elasticsearch indexing and search operations",
category: "search",
},
{
techIdentifier: "meilisearch",
mcpSlug: "meilisearch-mcp",
priority: "medium",
reason: "Meilisearch index and search management",
category: "search",
},
// ==========================================================================
// DATABASE (MySQL/MariaDB) - For Laravel/PHP projects
// GitHub: https://github.com/benborla/mcp-server-mysql (618+ stars)
// ==========================================================================
{
techIdentifier: "mysql",
mcpSlug: "@benborla29/mcp-server-mysql",
priority: "high",
reason:
"MySQL database operations: schema inspection, query execution, read-only mode available",
category: "database",
},
];
/**
* Universal MCPs always recommended regardless of stack.
*/
const UNIVERSAL_MCPS_LOCAL: Array<{
slug: string;
name: string;
description: string;
priority: MCPPriority;
reason: string;
category: string;
githubUrl?: string;
}> = [
{
slug: "context7",
name: "Context7",
description: "Up-to-date documentation lookup for any library",
priority: "high",
reason: "Essential for accurate documentation lookup across any tech stack",
category: "documentation",
githubUrl: "https://github.com/upstash/context7",
},
{
slug: "sequential-thinking",
name: "Sequential Thinking",
description: "Better reasoning for complex multi-step tasks",
priority: "medium",
reason: "Improves reasoning quality for architecture decisions",
category: "ai-llm",
githubUrl: "https://github.com/modelcontextprotocol/servers",
},
];
// ============================================================================
// MCP INSTALL CONFIG GENERATORS
// ============================================================================
/**
* Generate install configurations for different MCP clients.
*/
export function generateInstallConfig(
mcps: MCPRecommendation[],
): MCPInstallConfigs {
const cursor: Record<string, unknown> = { mcpServers: {} };
const claudeDesktop: Record<string, unknown> = { mcpServers: {} };
const windsurf: Record<string, unknown> = { mcpServers: {} };
for (const mcp of mcps) {
const serverConfig = {
command: "npx",
args: ["-y", mcp.slug],
env: {},
};
(cursor.mcpServers as Record<string, unknown>)[mcp.slug] = serverConfig;
(claudeDesktop.mcpServers as Record<string, unknown>)[mcp.slug] =
serverConfig;
(windsurf.mcpServers as Record<string, unknown>)[mcp.slug] = serverConfig;
}
return { cursor, claudeDesktop, windsurf };
}
// ============================================================================
// MCP MATCHING
// ============================================================================
/**
* Match detected stack to recommended MCPs.
*/
export function matchMCPsForStack(
stack: DetectedStack,
options: {
includeInstalled?: boolean;
installedMcps?: string[];
} = {},
): MCPRecommendation[] {
const { includeInstalled = false, installedMcps = [] } = options;
const recommendations: MCPRecommendation[] = [];
const seenSlugs = new Set<string>();
// Helper to add recommendation if not already added
const addRecommendation = (
mapping: (typeof TECH_MCP_MAPPINGS)[0],
matchedTech: string,
): void => {
if (seenSlugs.has(mapping.mcpSlug)) return;
if (!includeInstalled && installedMcps.includes(mapping.mcpSlug)) return;
seenSlugs.add(mapping.mcpSlug);
recommendations.push({
slug: mapping.mcpSlug,
name: formatMCPName(mapping.mcpSlug),
description: mapping.reason,
priority: mapping.priority,
reason: mapping.reason,
matchedTech,
category: mapping.category,
installCommand: `npx -y ${mapping.mcpSlug}`,
});
};
// Match from detected stack categories
const stackCategories: Array<{ tech: string | undefined; source: string }> = [
{ tech: stack.frontend?.name, source: "frontend" },
{ tech: stack.backend?.name, source: "backend" },
{ tech: stack.database?.name, source: "database" },
{ tech: stack.orm?.name, source: "orm" },
{ tech: stack.auth?.name, source: "auth" },
{ tech: stack.hosting?.name, source: "hosting" },
{ tech: stack.payments?.name, source: "payments" },
];
for (const { tech, source } of stackCategories) {
if (!tech) continue;
const mappings = TECH_MCP_MAPPINGS.filter(
(m) => m.techIdentifier.toLowerCase() === tech.toLowerCase(),
);
for (const mapping of mappings) {
addRecommendation(mapping, `${tech} (${source})`);
}
}
// Match from detected services
for (const service of stack.services) {
const mappings = TECH_MCP_MAPPINGS.filter(
(m) => m.techIdentifier.toLowerCase() === service.name.toLowerCase(),
);
for (const mapping of mappings) {
addRecommendation(mapping, `${service.name} (service)`);
}
}
// Add universal MCPs
for (const universal of UNIVERSAL_MCPS_LOCAL) {
if (seenSlugs.has(universal.slug)) continue;
if (!includeInstalled && installedMcps.includes(universal.slug)) continue;
seenSlugs.add(universal.slug);
recommendations.push({
slug: universal.slug,
name: universal.name,
description: universal.description,
priority: universal.priority,
reason: universal.reason,
matchedTech: "universal",
category: universal.category,
githubUrl: universal.githubUrl,
installCommand: `npx -y @anthropic/mcp-server-${universal.slug.replace("-mcp", "")}`,
});
}
// Sort by priority (high > medium > low)
const priorityOrder: Record<MCPPriority, number> = {
high: 3,
medium: 2,
low: 1,
};
recommendations.sort(
(a, b) => priorityOrder[b.priority] - priorityOrder[a.priority],
);
info(`Matched ${recommendations.length} MCPs for detected stack`);
return recommendations;
}
/**
* Match technologies from project description/constraints to MCPs.
* Used by generate_mcp_kit when no repo is being analyzed.
*/
export function matchMCPsForTechnologies(
technologies: string[],
options: {
includeInstalled?: boolean;
installedMcps?: string[];
} = {},
): MCPRecommendation[] {
const { includeInstalled = false, installedMcps = [] } = options;
const recommendations: MCPRecommendation[] = [];
const seenSlugs = new Set<string>();
for (const tech of technologies) {
const normalizedTech = tech.toLowerCase().replace(/[^a-z0-9]/g, "");
const mappings = TECH_MCP_MAPPINGS.filter((m) => {
const normalizedIdentifier = m.techIdentifier
.toLowerCase()
.replace(/[^a-z0-9]/g, "");
return (
normalizedIdentifier === normalizedTech ||
normalizedIdentifier.includes(normalizedTech) ||
normalizedTech.includes(normalizedIdentifier)
);
});
for (const mapping of mappings) {
if (seenSlugs.has(mapping.mcpSlug)) continue;
if (!includeInstalled && installedMcps.includes(mapping.mcpSlug))
continue;
seenSlugs.add(mapping.mcpSlug);
recommendations.push({
slug: mapping.mcpSlug,
name: formatMCPName(mapping.mcpSlug),
description: mapping.reason,
priority: mapping.priority,
reason: mapping.reason,
matchedTech: tech,
category: mapping.category,
installCommand: `npx -y ${mapping.mcpSlug}`,
});
}
}
// Add universal MCPs
for (const universal of UNIVERSAL_MCPS_LOCAL) {
if (seenSlugs.has(universal.slug)) continue;
if (!includeInstalled && installedMcps.includes(universal.slug)) continue;
seenSlugs.add(universal.slug);
recommendations.push({
slug: universal.slug,
name: universal.name,
description: universal.description,
priority: universal.priority,
reason: universal.reason,
matchedTech: "universal",
category: universal.category,
githubUrl: universal.githubUrl,
});
}
// Sort by priority
const priorityOrder: Record<MCPPriority, number> = {
high: 3,
medium: 2,
low: 1,
};
recommendations.sort(
(a, b) => priorityOrder[b.priority] - priorityOrder[a.priority],
);
return recommendations;
}
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/**
* Format MCP slug into human-readable name.
*/
function formatMCPName(slug: string): string {
return slug
.replace(/-mcp$/, "")
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ")
.concat(" MCP");
}
/**
* Get MCP info by slug.
*/
export function getMCPInfo(
slug: string,
): (typeof TECH_MCP_MAPPINGS)[0] | undefined {
return TECH_MCP_MAPPINGS.find((m) => m.mcpSlug === slug);
}
/**
* Get all MCPs for a specific category.
*/
export function getMCPsByCategory(category: string): typeof TECH_MCP_MAPPINGS {
return TECH_MCP_MAPPINGS.filter((m) => m.category === category);
}
/**
* Get all supported technology identifiers.
*/
export function getSupportedTechnologies(): string[] {
const techs = new Set<string>();
for (const mapping of TECH_MCP_MAPPINGS) {
techs.add(mapping.techIdentifier);
}
return Array.from(techs).sort();
}
/**
* Get universal MCPs that should always be recommended.
*/
export function getUniversalMCPs(): typeof UNIVERSAL_MCPS_LOCAL {
return UNIVERSAL_MCPS_LOCAL;
}