Supabase MCP Server

by adiletD
Verified
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { createClient } from '@supabase/supabase-js'; import 'dotenv/config'; import * as fs from 'fs'; import * as path from 'path'; // Set up logging const logFile = path.join(process.cwd(), 'mcp-server.log'); const logger = { log: (message: string) => { const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] INFO: ${message}\n`; console.log(message); fs.appendFileSync(logFile, logMessage); }, error: (message: string, error?: any) => { const timestamp = new Date().toISOString(); const errorDetails = error ? `\n${JSON.stringify(error, null, 2)}` : ''; const logMessage = `[${timestamp}] ERROR: ${message}${errorDetails}\n`; console.error(message, error || ''); fs.appendFileSync(logFile, logMessage); } }; logger.log('MCP Server starting up...'); logger.log(`Current working directory: ${process.cwd()}`); logger.log(`Log file location: ${logFile}`); // Check if Supabase environment variables are set if (!process.env.SUPABASE_URL || !process.env.SUPABASE_ANON_KEY) { logger.error("SUPABASE_URL and SUPABASE_ANON_KEY must be set for Supabase integration."); process.exit(1); } logger.log("Supabase environment variables found"); // Create Supabase client let supabase; try { supabase = createClient( process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY ); logger.log("Supabase client created successfully"); } catch (error) { logger.error("Failed to create Supabase client", error); process.exit(1); } // Create an MCP server const server = new McpServer({ name: "SupabaseMCP", version: "1.0.0" }); logger.log("MCP Server instance created"); // Add a tool to query only the feature_suggestions table server.tool( "query_feature_suggestions", { limit: z.number().optional().describe("Maximum number of records to return") }, async ({ limit = 100 }) => { const table_name = "feature_suggestions"; try { logger.log(`Querying feature_suggestions table with limit: ${limit}`); const { data, error } = await supabase .from(table_name) .select('*') .limit(limit); if (error) { logger.error(`Error querying feature_suggestions table:`, error); return { content: [{ type: "text", text: `Error querying feature_suggestions table: ${error.message}` }] }; } // Log the raw data for debugging logger.log(`Raw data from feature_suggestions: ${JSON.stringify(data)}`); // Ensure data is properly formatted const formattedData = Array.isArray(data) ? data : []; logger.log(`Successfully retrieved ${formattedData.length} records from feature_suggestions`); return { content: [{ type: "text", text: JSON.stringify(formattedData, null, 2) }] }; } catch (error) { logger.error(`Error in query_feature_suggestions tool for feature_suggestions table:`, error); return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }] }; } } ); logger.log("Feature suggestions query tool registered"); // Start the server async function startServer() { try { logger.log("Starting Supabase MCP server..."); const transport = new StdioServerTransport(); logger.log("StdioServerTransport created"); await server.connect(transport); logger.log("Supabase MCP server started and ready to receive commands."); // Add global error handlers process.on('uncaughtException', (err: Error) => { logger.error('Uncaught exception:', err); }); process.on('unhandledRejection', (reason: unknown) => { logger.error('Unhandled rejection:', reason); }); } catch (error) { logger.error("Failed to start MCP server:", error); process.exit(1); } } startServer();