Jake Shore f3c4cd817b Add all MCP servers + factory infra to MCPEngine — 2026-02-06
=== NEW SERVERS ADDED (7) ===
- servers/closebot — 119 tools, 14 modules, 4,656 lines TS (Stage 7)
- servers/google-console — Google Search Console MCP (Stage 7)
- servers/meta-ads — Meta/Facebook Ads MCP (Stage 8)
- servers/twilio — Twilio communications MCP (Stage 8)
- servers/competitor-research — Competitive intel MCP (Stage 6)
- servers/n8n-apps — n8n workflow MCP apps (Stage 6)
- servers/reonomy — Commercial real estate MCP (Stage 1)

=== FACTORY INFRASTRUCTURE ADDED ===
- infra/factory-tools — mcp-jest, mcp-validator, mcp-add, MCP Inspector
  - 60 test configs, 702 auto-generated test cases
  - All 30 servers score 100/100 protocol compliance
- infra/command-center — Pipeline state, operator playbook, dashboard config
- infra/factory-reviews — Automated eval reports

=== DOCS ADDED ===
- docs/MCP-FACTORY.md — Factory overview
- docs/reports/ — 5 pipeline evaluation reports
- docs/research/ — Browser MCP research

=== RULES ESTABLISHED ===
- CONTRIBUTING.md — All MCP work MUST go in this repo
- README.md — Full inventory of 37 servers + infra docs
- .gitignore — Updated for Python venvs

TOTAL: 37 MCP servers + full factory pipeline in one repo.
This is now the single source of truth for all MCP work.
2026-02-06 06:32:29 -05:00

157 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
// ============================================================================
// CloseBot MCP Server — Main Entry Point
// Lazy-loaded tool groups, 6 rich UI tool apps, ~55 tools
// ============================================================================
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { CloseBotClient } from "./client.js";
import type { ToolDefinition, ToolResult } from "./types.js";
// ---------------------------------------------------------------------------
// Lazy-loaded module registry
// ---------------------------------------------------------------------------
interface ToolModule {
tools: ToolDefinition[];
handle: (
client: CloseBotClient,
name: string,
args: Record<string, unknown>
) => Promise<ToolResult>;
}
interface LazyGroup {
/** File path (relative) for dynamic import */
path: string;
/** Cached module after first load */
module?: ToolModule;
/** Tool metadata — populated eagerly, handler loaded lazily */
toolNames: string[];
}
// Tool groups — we import metadata eagerly but handler code lazily
const groups: LazyGroup[] = [
{ path: "./tools/bot-management.js", toolNames: [] },
{ path: "./tools/source-management.js", toolNames: [] },
{ path: "./tools/lead-management.js", toolNames: [] },
{ path: "./tools/analytics.js", toolNames: [] },
{ path: "./tools/bot-testing.js", toolNames: [] },
{ path: "./tools/library.js", toolNames: [] },
{ path: "./tools/agency-billing.js", toolNames: [] },
{ path: "./tools/configuration.js", toolNames: [] },
// Apps
{ path: "./apps/bot-dashboard.js", toolNames: [] },
{ path: "./apps/analytics-dashboard.js", toolNames: [] },
{ path: "./apps/test-console.js", toolNames: [] },
{ path: "./apps/lead-manager.js", toolNames: [] },
{ path: "./apps/library-manager.js", toolNames: [] },
{ path: "./apps/leaderboard.js", toolNames: [] },
];
// Map: toolName → group index (for fast dispatch)
const toolToGroup = new Map<string, number>();
// All tool definitions (populated on init)
let allTools: ToolDefinition[] = [];
async function loadGroupMetadata(): Promise<void> {
const toolDefs: ToolDefinition[] = [];
for (let i = 0; i < groups.length; i++) {
const mod = (await import(groups[i].path)) as ToolModule;
groups[i].module = mod;
groups[i].toolNames = mod.tools.map((t) => t.name);
for (const tool of mod.tools) {
toolToGroup.set(tool.name, i);
toolDefs.push(tool);
}
}
allTools = toolDefs;
}
async function getHandler(
toolName: string
): Promise<ToolModule["handle"] | null> {
const idx = toolToGroup.get(toolName);
if (idx === undefined) return null;
const group = groups[idx];
if (!group.module) {
group.module = (await import(group.path)) as ToolModule;
}
return group.module.handle;
}
// ---------------------------------------------------------------------------
// Server setup
// ---------------------------------------------------------------------------
async function main(): Promise<void> {
// Init client (validates API key)
let client: CloseBotClient;
try {
client = new CloseBotClient();
} catch (e) {
console.error(
(e as Error).message ||
"Failed to initialize. Set CLOSEBOT_API_KEY env var."
);
process.exit(1);
}
// Load all tool metadata
await loadGroupMetadata();
const server = new Server(
{
name: "closebot-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// --- List Tools ---
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: allTools.map((t) => ({
name: t.name,
description: t.description,
inputSchema: t.inputSchema,
})),
};
});
// --- Call Tool ---
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const handler = await getHandler(name);
if (!handler) {
return {
content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
isError: true,
} as Record<string, unknown>;
}
const result = await handler(client, name, (args as Record<string, unknown>) || {});
return result as unknown as Record<string, unknown>;
});
// --- Connect stdio transport ---
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(
`CloseBot MCP server running — ${allTools.length} tools loaded across ${groups.length} modules`
);
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});