AI Meta MCP Server

by alxspiker
Verified
#!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { VM } from "vm2"; import fs from "fs/promises"; import path from "path"; import { execSync, exec } from "child_process"; import { promisify } from "util"; // Configuration from environment variables const ALLOW_JS_EXECUTION = process.env.ALLOW_JS_EXECUTION !== "false"; const ALLOW_PYTHON_EXECUTION = process.env.ALLOW_PYTHON_EXECUTION === "true"; const ALLOW_SHELL_EXECUTION = process.env.ALLOW_SHELL_EXECUTION === "true"; const PERSIST_TOOLS = process.env.PERSIST_TOOLS !== "false"; const TOOLS_DB_PATH = process.env.TOOLS_DB_PATH || "./tools.json"; // Type definition for stored tools type StoredTool = { name: string; description: string; inputSchema: z.ZodObject<any> | z.ZodRawShape | Record<string, any> | string; implementation: string; executionEnvironment: "javascript" | "python" | "shell"; createdAt: Date; updatedAt: Date; }; // Global registry of custom tools let customTools: Record<string, StoredTool> = {}; // Create an MCP server const server = new McpServer({ name: "ai-meta-mcp-server", version: "1.0.0", }); // Initialize tools database async function initializeToolsDatabase() { if (!PERSIST_TOOLS) return; try { const data = await fs.readFile(TOOLS_DB_PATH, "utf-8"); customTools = JSON.parse(data); console.error(`Loaded ${Object.keys(customTools).length} custom tools from ${TOOLS_DB_PATH}`); // Register all loaded tools with the server for (const [name, toolDef] of Object.entries(customTools)) { registerToolWithServer(toolDef); } } catch (err) { if ((err as NodeJS.ErrnoException).code !== "ENOENT") { console.error("Error loading tools database:", err); } } } // Save tools database async function saveToolsDatabase() { if (!PERSIST_TOOLS) return; try { await fs.mkdir(path.dirname(TOOLS_DB_PATH), { recursive: true }); await fs.writeFile(TOOLS_DB_PATH, JSON.stringify(customTools, null, 2)); } catch (err) { console.error("Error saving tools database:", err); } } // Execute JavaScript code in a sandbox async function executeJavaScript(code: string, params: Record<string, any>): Promise<any> { if (!ALLOW_JS_EXECUTION) { throw new Error("JavaScript execution is not allowed in this environment"); } const vm = new VM({ sandbox: { params, console: { log: (...args: any[]) => console.error(...args), } }, timeout: 5000, // 5 second timeout for safety }); return vm.run(`(async () => { ${code} })()`);; } // Execute Python code async function executePython(code: string, params: Record<string, any>): Promise<any> { if (!ALLOW_PYTHON_EXECUTION) { throw new Error("Python execution is not allowed in this environment"); } const tmpDir = await fs.mkdtemp("ai-meta-mcp-"); const scriptPath = path.join(tmpDir, "script.py"); const paramsPath = path.join(tmpDir, "params.json"); try { // Write Python script await fs.writeFile(scriptPath, code); // Write parameters as JSON file await fs.writeFile(paramsPath, JSON.stringify(params)); // Run Python script with parameters const execPromise = promisify(exec); const { stdout } = await execPromise(`python -c "import json; with open('${paramsPath}') as f: params = json.load(f); exec(open('${scriptPath}').read())"`); return JSON.parse(stdout.trim()); } finally { // Clean up temp files await fs.rm(tmpDir, { recursive: true, force: true }); } } // Execute shell commands async function executeShell(code: string, params: Record<string, any>): Promise<any> { if (!ALLOW_SHELL_EXECUTION) { throw new Error("Shell execution is not allowed in this environment"); } // Very limited implementation - for demonstration purposes only const paramStr = Object.entries(params) .map(([key, value]) => `${key}=${JSON.stringify(value)}`) .join(" "); const result = execSync(`${code} ${paramStr}`, { encoding: "utf-8" }); return result.trim(); } // Register a tool with the MCP server function registerToolWithServer(toolDef: StoredTool) { try { let paramsSchema: z.ZodRawShape = {}; // Handle both string schemas and ZodType objects if (typeof toolDef.inputSchema === "string") { try { paramsSchema = JSON.parse(toolDef.inputSchema as string); } catch (e) { console.error(`Failed to parse schema for tool ${toolDef.name}:`, e); // Keep empty object as default } } else if (toolDef.inputSchema instanceof z.ZodObject) { // If it's a ZodObject, extract its shape which is a ZodRawShape paramsSchema = toolDef.inputSchema.shape; } else if (typeof toolDef.inputSchema === "object" && toolDef.inputSchema !== null) { // Assume it's a raw object schema paramsSchema = toolDef.inputSchema as Record<string, any>; } // Register the tool with the server server.tool( toolDef.name, toolDef.description, paramsSchema, async (params) => { console.error(`Executing custom tool ${toolDef.name} with parameters:`, params); try { let result; switch (toolDef.executionEnvironment) { case "javascript": result = await executeJavaScript(toolDef.implementation, params); break; case "python": result = await executePython(toolDef.implementation, params); break; case "shell": result = await executeShell(toolDef.implementation, params); break; default: throw new Error(`Unsupported execution environment: ${toolDef.executionEnvironment}`); } // Ensure we return a proper CallToolResult return { content: [ { type: "text", text: typeof result === "string" ? result : JSON.stringify(result, null, 2), }, ], }; } catch (error) { console.error(`Error executing tool ${toolDef.name}:`, error); return { isError: true, content: [ { type: "text", text: `Error executing tool: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } ); console.error(`Registered custom tool: ${toolDef.name}`); } catch (error) { console.error(`Failed to register tool ${toolDef.name}:`, error); } } // Meta-function for defining new tools server.tool( "define_function", "Create a new custom MCP function that the AI can use", { name: z.string().min(1).describe("Name of the new function"), description: z.string().describe("Description of what the function does"), parameters_schema: z.record(z.any()).describe("JSON Schema for parameters"), implementation_code: z.string().min(1).describe("Code to implement the function"), execution_environment: z.enum(["javascript", "python", "shell"]).default("javascript").describe("Environment to execute the code in"), }, async ({ name, description, parameters_schema, implementation_code, execution_environment }) => { console.error(`Defining new function: ${name}`); // Check if function already exists if (customTools[name]) { return { isError: true, content: [ { type: "text", text: `A function named "${name}" already exists. Use update_function to modify it.`, }, ], }; } // Validate execution environment if (execution_environment === "javascript" && !ALLOW_JS_EXECUTION) { return { isError: true, content: [ { type: "text", text: "JavaScript execution is not allowed in this environment.", }, ], }; } if (execution_environment === "python" && !ALLOW_PYTHON_EXECUTION) { return { isError: true, content: [ { type: "text", text: "Python execution is not allowed in this environment.", }, ], }; } if (execution_environment === "shell" && !ALLOW_SHELL_EXECUTION) { return { isError: true, content: [ { type: "text", text: "Shell execution is not allowed in this environment.", }, ], }; } // Create a new tool definition const now = new Date(); const toolDef: StoredTool = { name, description, inputSchema: parameters_schema, implementation: implementation_code, executionEnvironment: execution_environment, createdAt: now, updatedAt: now, }; // Register the tool try { registerToolWithServer(toolDef); // Store the tool customTools[name] = toolDef; await saveToolsDatabase(); return { content: [ { type: "text", text: `Successfully created new function "${name}". You can now use it as a tool.`, }, ], }; } catch (error) { return { isError: true, content: [ { type: "text", text: `Error creating function: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } ); // Update an existing function server.tool( "update_function", "Update an existing custom MCP function", { name: z.string().min(1).describe("Name of the function to update"), description: z.string().optional().describe("New description of what the function does"), parameters_schema: z.record(z.any()).optional().describe("New JSON Schema for parameters"), implementation_code: z.string().optional().describe("New code to implement the function"), execution_environment: z.enum(["javascript", "python", "shell"]).optional().describe("New environment to execute the code in"), }, async ({ name, description, parameters_schema, implementation_code, execution_environment }) => { console.error(`Updating function: ${name}`); // Check if function exists if (!customTools[name]) { return { isError: true, content: [ { type: "text", text: `No function named "${name}" exists. Use define_function to create it.`, }, ], }; } // Validate execution environment if changing if (execution_environment === "javascript" && !ALLOW_JS_EXECUTION) { return { isError: true, content: [ { type: "text", text: "JavaScript execution is not allowed in this environment.", }, ], }; } if (execution_environment === "python" && !ALLOW_PYTHON_EXECUTION) { return { isError: true, content: [ { type: "text", text: "Python execution is not allowed in this environment.", }, ], }; } if (execution_environment === "shell" && !ALLOW_SHELL_EXECUTION) { return { isError: true, content: [ { type: "text", text: "Shell execution is not allowed in this environment.", }, ], }; } // Update the tool definition const updatedTool = { ...customTools[name] }; if (description !== undefined) updatedTool.description = description; if (parameters_schema !== undefined) updatedTool.inputSchema = parameters_schema; if (implementation_code !== undefined) updatedTool.implementation = implementation_code; if (execution_environment !== undefined) updatedTool.executionEnvironment = execution_environment; updatedTool.updatedAt = new Date(); // Register the updated tool try { // The server doesn't have a way to update tools, so we'll just re-register it registerToolWithServer(updatedTool); // Store the updated tool customTools[name] = updatedTool; await saveToolsDatabase(); return { content: [ { type: "text", text: `Successfully updated function "${name}".`, }, ], }; } catch (error) { return { isError: true, content: [ { type: "text", text: `Error updating function: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } ); // Delete a function server.tool( "delete_function", "Delete a custom MCP function", { name: z.string().min(1).describe("Name of the function to delete"), }, async ({ name }) => { console.error(`Deleting function: ${name}`); // Check if function exists if (!customTools[name]) { return { isError: true, content: [ { type: "text", text: `No function named "${name}" exists.`, }, ], }; } // Delete the tool try { delete customTools[name]; await saveToolsDatabase(); return { content: [ { type: "text", text: `Successfully deleted function "${name}".`, }, ], }; } catch (error) { return { isError: true, content: [ { type: "text", text: `Error deleting function: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } ); // List all functions server.tool( "list_functions", "List all custom MCP functions", {}, async () => { console.error(`Listing all functions`); const functionList = Object.entries(customTools).map(([name, tool]) => ({ name, description: tool.description, executionEnvironment: tool.executionEnvironment, createdAt: tool.createdAt, updatedAt: tool.updatedAt, })); return { content: [ { type: "text", text: JSON.stringify(functionList, null, 2), }, ], }; } ); // Get details of a function server.tool( "get_function_details", "Get details of a custom MCP function", { name: z.string().min(1).describe("Name of the function to get details for"), }, async ({ name }) => { console.error(`Getting details for function: ${name}`); // Check if function exists if (!customTools[name]) { return { isError: true, content: [ { type: "text", text: `No function named "${name}" exists.`, }, ], }; } const tool = customTools[name]; return { content: [ { type: "text", text: JSON.stringify({ name: tool.name, description: tool.description, parameters_schema: tool.inputSchema, execution_environment: tool.executionEnvironment, implementation_code: tool.implementation, created_at: tool.createdAt, updated_at: tool.updatedAt, }, null, 2), }, ], }; } ); // Main function async function main() { // Initialize tools database await initializeToolsDatabase(); // Start receiving messages on stdin and sending messages on stdout console.error("AI Meta MCP Server starting..."); const transport = new StdioServerTransport(); await server.connect(transport); console.error("AI Meta MCP Server connected."); } main().catch(err => { console.error("Fatal error:", err); process.exit(1); });