Skip to main content
Glama

PostgreSQL MCP Server

AGPL 3.0
794
122
  • Linux
  • Apple
index.ts9.27 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'; // Import tool types import type { PostgresTool, ToolOutput } from './types/tool.js'; import { DatabaseConnection } from './utils/connection.js'; // Import all tool implementations import { analyzeDatabaseTool } from './tools/analyze.js'; import { manageFunctionsTool, manageRLSTool } from './tools/functions.js'; import { debugDatabaseTool } from './tools/debug.js'; import { exportTableDataTool, importTableDataTool, copyBetweenDatabasesTool } from './tools/migration.js'; import { monitorDatabaseTool } from './tools/monitor.js'; import { manageSchemaTools } from './tools/schema.js'; import { manageTriggersTools } from './tools/triggers.js'; import { manageIndexesTool } from './tools/indexes.js'; import { manageQueryTool } from './tools/query.js'; import { manageUsersTool } from './tools/users.js'; import { manageConstraintsTool } from './tools/constraints.js'; import { executeQueryTool, executeMutationTool, executeSqlTool } from './tools/data.js'; 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(); /** * Get connection string from various sources in order of precedence: * 1. Function argument (tool-specific) * 2. CLI --connection-string option * 3. POSTGRES_CONNECTION_STRING environment variable */ function getConnectionString(connectionStringArg?: string): string { 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.' ); } class PostgreSQLServer { private server: Server; public availableToolsList: PostgresTool[]; private enabledTools: PostgresTool[]; private enabledToolsMap: Record<string, PostgresTool>; constructor(initialTools: PostgresTool[] = []) { 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; }, {} as Record<string, { name: string; description: string; inputSchema: object }>), }, } ); this.setupToolHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); // Handle graceful shutdown process.on('SIGINT', async () => { await this.cleanup(); process.exit(0); }); process.on('SIGTERM', async () => { await this.cleanup(); process.exit(0); }); } /** * Load tools configuration and filter enabled tools */ private loadAndFilterTools(): void { 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: unknown) => typeof t === 'string')) { const enabledToolNames = new Set(config.enabledTools as string[]); 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(', ')}`); // Warn about tools specified in config but not available 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; }, {} as Record<string, PostgresTool>); } /** * Clean up resources on shutdown */ private async cleanup(): Promise<void> { console.error('Shutting down PostgreSQL MCP server...'); await DatabaseConnection.cleanupPools(); if (this.server) { await this.server.close(); } } /** * Setup MCP request handlers */ private setupToolHandlers(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: this.enabledTools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: zodToJsonSchema(tool.inputSchema), })), })); // Handle tool execution requests // biome-ignore lint/suspicious/noExplicitAny: MCP SDK type inference issue this.server.setRequestHandler(CallToolRequestSchema, (async (request: any): Promise<ToolOutput> => { 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: ToolOutput = 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, } as ToolOutput; } // biome-ignore lint/suspicious/noExplicitAny: MCP SDK type inference issue }) as any); } 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."); } this.loadAndFilterTools(); const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('PostgreSQL MCP server running on stdio'); } } /** * All available PostgreSQL MCP tools * Organized by category for maintainability */ const allTools: PostgresTool[] = [ // Core Analysis & Debugging analyzeDatabaseTool, debugDatabaseTool, // Schema & Structure Management (Meta-Tools) manageSchemaTools, manageFunctionsTool, manageTriggersTools, manageIndexesTool, manageConstraintsTool, manageRLSTool, // User & Security Management manageUsersTool, // Query & Performance Management manageQueryTool, // Data Operations (Enhancement Tools) executeQueryTool, executeMutationTool, executeSqlTool, // Documentation & Metadata manageCommentsTool, // Data Migration & Monitoring exportTableDataTool, importTableDataTool, copyBetweenDatabasesTool, monitorDatabaseTool ]; const serverInstance = new PostgreSQLServer(allTools); serverInstance.run().catch(error => { console.error('Failed to run the server:', error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HenkDz/postgresql-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server