#!/usr/bin/env node
/**
* code2mcp - Code Mode MCP Server
*
* Implements Cloudflare's Code Mode pattern: convert MCP tools to TypeScript APIs
* and have LLMs write code instead of making direct tool calls.
*
* Benefits:
* - 98% token reduction for complex workflows
* - Better tool understanding (LLMs trained on TypeScript)
* - Secure sandbox execution
* - API keys hidden from LLM-generated code
*/
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 { MCPOrchestrator } from "./orchestrator/MCPOrchestrator.js";
import { TypeScriptGenerator } from "./generator/TypeScriptGenerator.js";
import { CodeSandbox } from "./sandbox/CodeSandbox.js";
import type { MCPServerConfig } from "./types/index.js";
import { logInfo, logError } from "./utils/logger.js";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import { readFileSync } from "fs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Load configuration from mcp-servers.config.js
const configPath = join(__dirname, "..", "mcp-servers.config.js");
let serverConfig: any;
try {
let configModule = readFileSync(configPath, "utf-8");
console.log("[DEBUG] Raw config length:", configModule.length);
// Strip out JavaScript comments for eval compatibility
configModule = configModule.replace(/\/\*[\s\S]*?\*\//g, ''); // Remove block comments
configModule = configModule.replace(/\/\/.*$/gm, ''); // Remove line comments
console.log("[DEBUG] After removing comments:", configModule.length);
// Replace process.env references with actual values or defaults
configModule = configModule.replace(/process\.env\.(\w+)\s*\|\|\s*["']([^"']*)["']/g, (_match, envVar, defaultVal) => {
return `"${process.env[envVar] || defaultVal}"`;
});
configModule = configModule.replace(/process\.env\.(\w+)/g, (_match, envVar) => {
return `"${process.env[envVar] || ''}"`;
});
// Remove "module.exports = " and trim whitespace
let configStr = configModule.replace("module.exports =", "").trim();
// Remove all semicolons (they're not needed in object literals)
configStr = configStr.replace(/;/g, '');
// Split into lines, trim each, and rejoin
const lines = configStr.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0);
configStr = lines.join('\n');
console.log("[DEBUG] Final config string length:", configStr.length);
console.log("[DEBUG] Config:\n", configStr);
// Wrap in parentheses to make it a valid expression and evaluate
try {
serverConfig = eval(configStr);
} catch (e1: any) {
console.log("[DEBUG] First eval failed, trying with parentheses:", e1?.message);
serverConfig = eval("(" + configStr + ")");
}
logInfo("Successfully loaded mcp-servers.config.js", {});
} catch (error) {
console.log("[DEBUG] Config load error:", error instanceof Error ? error.message : String(error));
logError("Failed to load mcp-servers.config.js, using defaults", {
error: error instanceof Error ? error.message : String(error),
});
serverConfig = {
servers: {
context7: { enabled: true, command: "npx", args: ["-y", "@upstash/context7-mcp"] },
playwright: { enabled: true, command: "npx", args: ["@playwright/mcp@latest"] },
brightData: { enabled: true, command: "npx", args: ["@brightdata/mcp"], env: {} },
chromeDevtools: { enabled: true, command: "npx", args: ["chrome-devtools-mcp@latest"] },
firecrawl: { enabled: true, command: "npx", args: ["-y", "firecrawl-mcp"], env: {} },
shadcn: { enabled: true, command: "npx", args: ["-y", "mcp-remote", "https://www.shadcn.io/api/mcp"] },
},
};
}
// Build MCP_SERVERS array from config (only enabled servers)
const MCP_SERVERS: MCPServerConfig[] = [];
// Map config keys to server names for MCP protocol
const serverNameMap: Record<string, string> = {
context7: "context7",
playwright: "playwright",
brightData: "bright-data",
chromeDevtools: "chrome-devtools",
firecrawl: "firecrawl-mcp",
shadcn: "shadcn",
};
for (const [key, config] of Object.entries(serverConfig.servers)) {
const cfg = config as any; // Type assertion for dynamic config
if (cfg.enabled) {
const serverName = serverNameMap[key] || key;
MCP_SERVERS.push({
name: serverName,
transport: "stdio",
command: cfg.command,
args: cfg.args,
env: cfg.env || {},
});
}
}
logInfo("Loaded MCP server configuration", {
totalServers: Object.keys(serverConfig.servers).length,
enabledServers: MCP_SERVERS.length,
servers: MCP_SERVERS.map((s) => s.name),
});
async function main() {
logInfo("Starting code2mcp server");
// Initialize components
const orchestrator = new MCPOrchestrator();
const generator = new TypeScriptGenerator();
// Connect to MCP servers
logInfo("Connecting to MCP servers", { count: MCP_SERVERS.length });
for (const serverConfig of MCP_SERVERS) {
try {
await orchestrator.connectServer(serverConfig);
} catch (error) {
logError("Failed to connect to MCP server, continuing without it", {
server: serverConfig.name,
error: error instanceof Error ? error.message : String(error),
});
}
}
// Get all tools
const allTools = orchestrator.getAllTools();
logInfo("Fetched tools from MCP servers", {
toolCount: allTools.length,
servers: orchestrator.getServerNames(),
});
// Generate TypeScript APIs
if (allTools.length > 0) {
await generator.generateAPIs(allTools, "generated");
logInfo("Generated TypeScript APIs");
} else {
logInfo("No tools available, skipping API generation");
}
// Initialize sandbox
const sandbox = new CodeSandbox(orchestrator);
// Create MCP server
const server = new Server(
{
name: "code2mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
},
);
// List tools handler - expose only execute_code
server.setRequestHandler(ListToolsRequestSchema, async () => {
const apiDocs = generateAPIDocumentation(allTools);
return {
tools: [
{
name: "execute_code",
description: `Execute TypeScript code with access to MCP tool APIs.
**Available APIs:**
${apiDocs}
The code will run in a secure sandbox with:
- No network access
- No filesystem access
- Access only to MCP tool APIs
- console.log() output will be returned to you
**Example Usage:**
\`\`\`typescript
// Example: Call a single tool
const result = await __mcp_call('server__tool_name', {
param: 'value'
});
console.log('Result:', result);
\`\`\`
**Multi-step workflow example:**
\`\`\`typescript
// Fetch data from one service
const data = await __mcp_call('service1__get_data', {
id: '123'
});
// Process and send to another service
await __mcp_call('service2__update', {
value: data.content
});
console.log('Workflow complete');
\`\`\`
**Note:** Intermediate data stays in the sandbox (doesn't enter your context), saving massive amounts of tokens for complex workflows.`,
inputSchema: {
type: "object",
properties: {
code: {
type: "string",
description: "TypeScript code to execute in the sandbox",
},
timeout: {
type: "number",
description: "Timeout in milliseconds (default: 30000)",
default: 30000,
},
},
required: ["code"],
},
},
],
};
});
// Execute code handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name !== "execute_code") {
throw new Error(`Unknown tool: ${name}`);
}
const { code, timeout } = args as { code: string; timeout?: number };
if (!code || typeof code !== "string") {
throw new Error("Code parameter is required and must be a string");
}
logInfo("Executing code", {
codeLength: code.length,
timeout: timeout || 30000,
});
try {
const result = await sandbox.execute(code, { timeout });
if (result.error) {
const errorOutput = [
"=== Execution Error ===",
result.error,
"",
"=== Logs ===",
...result.logs,
"",
`Execution time: ${result.executionTime}ms`,
].join("\n");
return {
content: [
{
type: "text",
text: errorOutput,
},
],
isError: true,
};
}
const output = [
"=== Execution Logs ===",
...result.logs,
"",
"=== Result ===",
result.result !== undefined
? JSON.stringify(result.result, null, 2)
: "(undefined)",
"",
`Execution time: ${result.executionTime}ms`,
].join("\n");
return {
content: [
{
type: "text",
text: output,
},
],
};
} catch (error) {
logError("Sandbox execution failed", {
error: error instanceof Error ? error.message : String(error),
});
return {
content: [
{
type: "text",
text: `Sandbox Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
logInfo("code2mcp server started successfully", {
toolCount: allTools.length,
servers: orchestrator.getServerNames(),
});
// Graceful shutdown
process.on("SIGINT", async () => {
logInfo("Shutting down...");
await orchestrator.disconnect();
process.exit(0);
});
process.on("SIGTERM", async () => {
logInfo("Shutting down...");
await orchestrator.disconnect();
process.exit(0);
});
}
/**
* Generate API documentation for the tool description
* Now includes parameter information to help Claude understand tool signatures
*/
function generateAPIDocumentation(tools: any[]): string {
if (tools.length === 0) {
return "No MCP servers connected. Configure servers in src/index.ts";
}
// Group tools by server
const byServer = new Map<string, any[]>();
for (const tool of tools) {
const parts = tool.name.split("__");
if (parts.length < 2) continue;
const serverName = parts[0];
if (!byServer.has(serverName)) {
byServer.set(serverName, []);
}
byServer.get(serverName)!.push(tool);
}
let doc = "";
for (const [serverName, serverTools] of byServer) {
doc += `\n**${serverName}**:\n`;
for (const tool of serverTools) {
// Extract parameter information from input schema
const params = extractParameters(tool.inputSchema);
doc += ` - \`__mcp_call('${tool.name}', {${params
.map((p) => {
const paramName = p.split(":")[0].trim();
return `${paramName}: value`;
})
.join(", ")}})\`\n`;
doc += ` ${tool.description || "No description"}\n`;
if (params.length > 0) {
doc += ` Parameters: ${params.join(", ")}\n`;
}
}
}
return doc;
}
/**
* Extract parameter information from JSON Schema
*/
function extractParameters(schema: any): string[] {
if (!schema || !schema.properties) {
return [];
}
const params: string[] = [];
const required = schema.required || [];
for (const [name, prop] of Object.entries(schema.properties)) {
const propSchema = prop as any;
const type = propSchema.type || "any";
const isRequired = required.includes(name);
const optional = isRequired ? "" : "?";
params.push(`${name}${optional}: ${type}`);
}
return params;
}
// Run the server
main().catch((error) => {
logError("Fatal error", {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
process.exit(1);
});