UNPKG

@henkey/postgres-mcp-server

Version:

A Model Context Protocol (MCP) server that provides comprehensive PostgreSQL database management capabilities for AI assistants

311 lines 13.7 kB
#!/usr/bin/env node import { program } from 'commander'; import fs from 'node:fs'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js'; import { zodToJsonSchema } from 'zod-to-json-schema'; // Tool implementations will be imported here later // For now, we'll define an empty list. // e.g. import { analyzeDatabaseTool } from './tools/analyze.js'; // import { getSetupInstructionsTool } from './tools/setup.js'; // ... and so on for all tools import { DatabaseConnection } from './utils/connection.js'; // Import the refactored tool import { analyzeDatabaseTool } from './tools/analyze.js'; // .js because TS will compile to JS // Import all refactored tools from functions.ts and add them to the allTools array. // Commented out for testing consolidated functions tool /* import { getFunctionsTool, createFunctionTool, dropFunctionTool, enableRLSTool, disableRLSTool, createRLSPolicyTool, dropRLSPolicyTool, editRLSPolicyTool, getRLSPoliciesTool } from './tools/functions.js'; // .js because TS will compile to JS */ // Import consolidated functions tool (for testing) import { manageFunctionsTool, // Now implemented // Commented out for RLS consolidation testing /* enableRLSTool, disableRLSTool, createRLSPolicyTool, dropRLSPolicyTool, editRLSPolicyTool, getRLSPoliciesTool */ // New consolidated RLS tool manageRLSTool } from './tools/functions.js'; // Import debug tool import { debugDatabaseTool } from './tools/debug.js'; // Import enum tools // Commented out for schema management consolidation /* import { getEnumsTool, createEnumTool } from './tools/enums.js'; */ // Enums now included in consolidated schema tool // Import migration tools import { exportTableDataTool, importTableDataTool, copyBetweenDatabasesTool } from './tools/migration.js'; // Import monitor tool import { monitorDatabaseTool } from './tools/monitor.js'; // Import schema tools // Commented out for schema management consolidation /* import { getSchemaInfoTool, createTableTool, alterTableTool } from './tools/schema.js'; */ // New consolidated schema management tool import { manageSchemaTools } from './tools/schema.js'; // Import setup tool import { getSetupInstructionsTool } from './tools/setup.js'; // Import trigger tools // Import trigger tools // Commented out for trigger management consolidation /* import { getTriggersTool, createTriggerTool, dropTriggerTool, setTriggerStateTool } from './tools/triggers.js'; */ // New consolidated trigger management tool import { manageTriggersTools } from './tools/triggers.js'; // Import index tools // Commented out for index management consolidation /* import { getIndexesTool, createIndexTool, dropIndexTool, reindexTool, analyzeIndexUsageTool } from './tools/indexes.js'; */ // New consolidated index management tool import { manageIndexesTool } from './tools/indexes.js'; // Import performance tools // Commented out for query management consolidation /* import { explainQueryTool, getSlowQueriesTool, getQueryStatsTool, resetQueryStatsTool } from './tools/performance.js'; */ // New consolidated query management tool import { manageQueryTool } from './tools/query.js'; // Import user management tools // Commented out for user management consolidation /* import { createUserTool, dropUserTool, alterUserTool, grantPermissionsTool, revokePermissionsTool, getUserPermissionsTool, listUsersTool } from './tools/users.js'; */ // New consolidated user management tool import { manageUsersTool } from './tools/users.js'; // Import constraint tools // Commented out for constraint management consolidation /* import { getConstraintsTool, createForeignKeyTool, dropForeignKeyTool, createConstraintTool, dropConstraintTool } from './tools/constraints.js'; */ // New consolidated constraint management tool import { manageConstraintsTool } from './tools/constraints.js'; // Import data query and mutation tools import { executeQueryTool, executeMutationTool, executeSqlTool } from './tools/data.js'; // Import comments management tool import { manageCommentsTool } from './tools/comments.js'; // Initialize commander program .version('1.0.5') .option('-cs, --connection-string <string>', 'PostgreSQL connection string') .option('-tc, --tools-config <path>', 'Path to tools configuration JSON file') .parse(process.argv); const options = program.opts(); // Helper function to get connection string (remains largely the same) // This function will be passed to each tool's execute method. function getConnectionString(connectionStringArg) { if (connectionStringArg) { return connectionStringArg; } const cliConnectionString = options.connectionString; if (cliConnectionString) { return cliConnectionString; } const envConnectionString = process.env.POSTGRES_CONNECTION_STRING; if (envConnectionString) { return envConnectionString; } throw new McpError(ErrorCode.InvalidParams, 'No connection string provided. Provide one in the tool arguments, via the --connection-string CLI option, or set the POSTGRES_CONNECTION_STRING environment variable.'); } // TOOL_DEFINITIONS array is removed. // Individual tool objects (PostgresTool) will be imported and collected. class PostgreSQLServer { server; availableToolsList; // Made public for stepwise refactor enabledTools; enabledToolsMap; constructor(initialTools = []) { this.availableToolsList = [...initialTools]; this.enabledTools = []; this.enabledToolsMap = {}; this.loadAndFilterTools(); this.server = new Server({ name: 'postgresql-mcp-server', version: '1.0.5', }, { capabilities: { tools: this.enabledTools.reduce((acc, tool) => { acc[tool.name] = { name: tool.name, description: tool.description, inputSchema: zodToJsonSchema(tool.inputSchema), }; return acc; }, {}), }, }); this.setupToolHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.cleanup(); process.exit(0); }); process.on('SIGTERM', async () => { await this.cleanup(); process.exit(0); }); } loadAndFilterTools() { let toolsToEnable = [...this.availableToolsList]; const toolsConfigPath = options.toolsConfig; if (toolsConfigPath) { try { const configContent = fs.readFileSync(toolsConfigPath, 'utf-8'); const config = JSON.parse(configContent); if (config && Array.isArray(config.enabledTools) && config.enabledTools.every((t) => typeof t === 'string')) { const enabledToolNames = new Set(config.enabledTools); toolsToEnable = this.availableToolsList.filter(tool => enabledToolNames.has(tool.name)); console.error(`[MCP Info] Loaded tools configuration from ${toolsConfigPath}. Enabled tools: ${toolsToEnable.map(t => t.name).join(', ')}`); for (const requestedName of enabledToolNames) { if (!this.availableToolsList.some(tool => tool.name === requestedName)) { console.warn(`[MCP Warning] Tool "${requestedName}" specified in config file but not found in available tools.`); } } } else { console.error(`[MCP Warning] Invalid tools configuration file format at ${toolsConfigPath}.`); } } catch (error) { console.error(`[MCP Warning] Could not read or parse tools configuration file at ${toolsConfigPath}. Error: ${error instanceof Error ? error.message : String(error)}.`); } } else { if (this.availableToolsList.length > 0) { console.error('[MCP Info] No tools configuration file provided. All available tools will be enabled.'); } else { console.error('[MCP Info] No tools configuration file provided and no tools loaded into availableToolsList.'); } } this.enabledTools = toolsToEnable; this.enabledToolsMap = toolsToEnable.reduce((acc, tool) => { acc[tool.name] = tool; return acc; }, {}); // Ensured no server.updateCapabilities() call here. } async cleanup() { console.error('Shutting down PostgreSQL MCP server...'); await DatabaseConnection.cleanupPools(); if (this.server) { await this.server.close(); } } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: this.enabledTools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: zodToJsonSchema(tool.inputSchema), })), })); // Casting the handler to 'any' to bypass persistent incorrect type inference by TypeScript for this specific SDK call. // The actual returned structure (ToolOutput) is compliant with CallToolResponse. // biome-ignore lint/suspicious/noExplicitAny: <explanation> this.server.setRequestHandler(CallToolRequestSchema, (async (request) => { try { const toolName = request.params.name; const tool = this.enabledToolsMap[toolName]; if (!tool) { const wasAvailable = this.availableToolsList.some(t => t.name === toolName); const message = wasAvailable ? `Tool "${toolName}" is available but not enabled by the current server configuration.` : `Tool '${toolName}' is not enabled or does not exist.`; throw new McpError(ErrorCode.MethodNotFound, message); } const result = await tool.execute(request.params.arguments, getConnectionString); return result; } catch (error) { console.error(`Error handling request for tool ${request.params.name}:`, error); let errorMessage = error instanceof Error ? error.message : String(error); if (error instanceof McpError) { errorMessage = error.message; } return { content: [{ type: 'text', text: `Error: ${errorMessage}` }], isError: true, }; } // biome-ignore lint/suspicious/noExplicitAny: <explanation> })); } async run() { if (this.availableToolsList.length === 0 && !options.toolsConfig) { console.warn("[MCP Warning] No tools loaded and no tools config provided. Server will start with no active tools."); } // Ensure tools are loaded and filtered before connecting server this.loadAndFilterTools(); // Server capabilities are set in constructor using this.enabledTools, which is now up-to-date. const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('PostgreSQL MCP server running on stdio'); } } // For the final refactored version, tools will be imported and passed to the constructor. // e.g.: // import { analyzeDatabaseTool } from './tools/analyze'; // import { getSetupInstructionsTool } from './tools/setup'; // const allTools = [analyzeDatabaseTool, getSetupInstructionsTool /*, ...all other tools */]; // const serverInstance = new PostgreSQLServer(allTools); // For now, initialize with an empty list. Tools will be refactored one by one. const allTools = [ analyzeDatabaseTool, // Commented out for testing consolidated functions tool /* getFunctionsTool, createFunctionTool, dropFunctionTool, */ // New consolidated functions tool manageFunctionsTool, manageRLSTool, debugDatabaseTool, // Add debug tool manageSchemaTools, // Add consolidated schema management tool (includes get_info, create_table, alter_table, get_enums, create_enum) exportTableDataTool, // Add exportTableData tool importTableDataTool, // Add importTableData tool copyBetweenDatabasesTool, // Add copyBetweenDatabases tool monitorDatabaseTool, // Add monitorDatabase tool getSetupInstructionsTool, // Add getSetupInstructions tool // Trigger Management Tools (consolidated) manageTriggersTools, // Add consolidated trigger management tool (includes get, create, drop, set_state) // Index Management Tools manageIndexesTool, // Query Performance Tools (consolidated) manageQueryTool, // User Management Tools manageUsersTool, // Constraint Management Tools manageConstraintsTool, // Data Query and Mutation Tools executeQueryTool, executeMutationTool, executeSqlTool, // Comments Management Tool (NEW FEATURE) manageCommentsTool ]; const serverInstance = new PostgreSQLServer(allTools); serverInstance.run().catch(error => { console.error('Failed to run the server:', error); process.exit(1); }); //# sourceMappingURL=index.js.map