Skip to main content
Glama
cesarvarela

PostgreSQL MCP Server

by cesarvarela
getSchema.ts5.59 kB
import { z, ZodRawShape } from "zod"; import { McpToolResponse, createMcpSuccessResponse, createMcpErrorResponse, createDatabaseUnavailableResponse, executePostgresQuery, getConnectionStatus, debug, } from "./utils.js"; // Zod schema for input validation export const getSchemaShape: ZodRawShape = { schema_name: z.string().optional().default("public"), table_pattern: z.string().optional(), include_columns: z.boolean().optional().default(true), include_constraints: z.boolean().optional().default(false), }; export const getSchemaSchema = z.object(getSchemaShape); interface TableInfo { table_name: string; table_schema: string; table_type: string; columns?: ColumnInfo[]; constraints?: ConstraintInfo[]; } interface ColumnInfo { column_name: string; data_type: string; is_nullable: string; column_default: string | null; character_maximum_length: number | null; numeric_precision: number | null; numeric_scale: number | null; } interface ConstraintInfo { constraint_name: string; constraint_type: string; column_name: string; foreign_table_name?: string; foreign_column_name?: string; } // Tool implementation export async function getSchema( rawParams: any ): McpToolResponse { try { // Validate and parse parameters const params = getSchemaSchema.parse(rawParams); // Check database connection status const connectionStatus = getConnectionStatus(); if (connectionStatus.status !== 'connected') { return createDatabaseUnavailableResponse("get database schema"); } const { schema_name, table_pattern, include_columns, include_constraints } = params; // Base query for tables let tablesQuery = ` SELECT table_name, table_schema, table_type FROM information_schema.tables WHERE table_schema = $1 `; const queryParams: any[] = [schema_name]; let paramIndex = 2; // Add table pattern filter if provided if (table_pattern) { tablesQuery += ` AND table_name LIKE $${paramIndex}`; queryParams.push(table_pattern); paramIndex++; } tablesQuery += ` ORDER BY table_name`; debug("Fetching schema information for schema: %s", schema_name); const tables = await executePostgresQuery<TableInfo>(tablesQuery, queryParams); // Initialize columns and constraints properties for all tables tables.forEach(table => { table.columns = []; table.constraints = []; }); // Enhance tables with column information if requested if (include_columns && tables.length > 0) { const columnsQuery = ` SELECT table_name, column_name, data_type, is_nullable, column_default, character_maximum_length, numeric_precision, numeric_scale, ordinal_position FROM information_schema.columns WHERE table_schema = $1 ORDER BY table_name, ordinal_position `; const columns = await executePostgresQuery<ColumnInfo & { table_name: string; ordinal_position: number }>( columnsQuery, [schema_name] ); // Group columns by table const columnsByTable = new Map<string, ColumnInfo[]>(); columns.forEach(col => { if (!columnsByTable.has(col.table_name)) { columnsByTable.set(col.table_name, []); } const { table_name, ordinal_position, ...columnInfo } = col; columnsByTable.get(col.table_name)!.push(columnInfo); }); // Add columns to tables tables.forEach(table => { table.columns = columnsByTable.get(table.table_name) || []; }); } // Enhance tables with constraint information if requested if (include_constraints && tables.length > 0) { const constraintsQuery = ` SELECT tc.table_name, tc.constraint_name, tc.constraint_type, kcu.column_name, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema LEFT JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema WHERE tc.table_schema = $1 ORDER BY tc.table_name, tc.constraint_name `; const constraints = await executePostgresQuery<ConstraintInfo & { table_name: string }>( constraintsQuery, [schema_name] ); // Group constraints by table const constraintsByTable = new Map<string, ConstraintInfo[]>(); constraints.forEach(constraint => { if (!constraintsByTable.has(constraint.table_name)) { constraintsByTable.set(constraint.table_name, []); } const { table_name, ...constraintInfo } = constraint; constraintsByTable.get(constraint.table_name)!.push(constraintInfo); }); // Add constraints to tables tables.forEach(table => { table.constraints = constraintsByTable.get(table.table_name) || []; }); } const response = { schema: schema_name, table_count: tables.length, tables: tables, generated_at: new Date().toISOString(), }; return createMcpSuccessResponse(response); } catch (error) { return createMcpErrorResponse("get schema", error); } }

Implementation Reference

Latest Blog Posts

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/cesarvarela/postgres-mcp'

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