Skip to main content
Glama
cesarvarela

PostgreSQL MCP Server

by cesarvarela

get-table-info

Retrieve comprehensive PostgreSQL table details including columns, constraints, indexes, and statistics to analyze database structure and optimize queries.

Instructions

Get detailed information about a specific table including columns, constraints, indexes, and optionally statistics like row count and size.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
tableYes
schema_nameNopublic
include_statisticsNo

Implementation Reference

  • The primary handler function `getTableInfo` that executes SQL queries against PostgreSQL's information_schema and pg catalogs to fetch comprehensive table metadata including columns, constraints, indexes, and optional size/row statistics. Handles validation, sanitization, connection checks, and error responses.
    export async function getTableInfo( rawParams: any ): McpToolResponse { try { // Validate and parse parameters const params = getTableInfoSchema.parse(rawParams); // Check database connection status const connectionStatus = getConnectionStatus(); if (connectionStatus.status !== 'connected') { return createDatabaseUnavailableResponse("get table information"); } const { table, schema_name, include_statistics } = params; // Validate identifiers const sanitizedTable = sanitizeIdentifier(table); const sanitizedSchema = sanitizeIdentifier(schema_name); // Get basic table information const tableInfoQuery = ` SELECT t.table_name, t.table_schema, t.table_type, obj_description(c.oid) as table_comment FROM information_schema.tables t LEFT JOIN pg_class c ON c.relname = t.table_name LEFT JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = t.table_schema WHERE t.table_schema = $1 AND t.table_name = $2 `; const tableInfo = await executePostgresQuery(tableInfoQuery, [sanitizedSchema, sanitizedTable]); if (tableInfo.length === 0) { throw new Error(`Table ${sanitizedSchema}.${sanitizedTable} not found`); } // Get detailed column information const columnsQuery = ` SELECT c.column_name, c.data_type, c.is_nullable, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale, c.ordinal_position, col_description(pgc.oid, c.ordinal_position) as column_comment FROM information_schema.columns c LEFT JOIN pg_class pgc ON pgc.relname = c.table_name LEFT JOIN pg_namespace pgn ON pgn.oid = pgc.relnamespace AND pgn.nspname = c.table_schema WHERE c.table_schema = $1 AND c.table_name = $2 ORDER BY c.ordinal_position `; const columns = await executePostgresQuery(columnsQuery, [sanitizedSchema, sanitizedTable]); // Get constraints const constraintsQuery = ` SELECT tc.constraint_name, tc.constraint_type, kcu.column_name, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name, rc.match_option, rc.update_rule, rc.delete_rule 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 LEFT JOIN information_schema.referential_constraints rc ON tc.constraint_name = rc.constraint_name AND tc.table_schema = rc.constraint_schema WHERE tc.table_schema = $1 AND tc.table_name = $2 ORDER BY tc.constraint_type, tc.constraint_name `; const constraints = await executePostgresQuery(constraintsQuery, [sanitizedSchema, sanitizedTable]); // Get indexes const indexesQuery = ` SELECT i.relname as index_name, a.attname as column_name, ix.indisunique as is_unique, ix.indisprimary as is_primary, am.amname as index_type FROM pg_class i JOIN pg_index ix ON i.oid = ix.indexrelid JOIN pg_class t ON t.oid = ix.indrelid JOIN pg_namespace n ON n.oid = t.relnamespace JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) JOIN pg_am am ON i.relam = am.oid WHERE n.nspname = $1 AND t.relname = $2 ORDER BY i.relname, a.attnum `; const indexes = await executePostgresQuery(indexesQuery, [sanitizedSchema, sanitizedTable]); let statistics: TableStatistics | undefined; // Get table statistics if requested if (include_statistics) { const statsQuery = ` SELECT schemaname, tablename, attname, n_distinct, correlation FROM pg_stats WHERE schemaname = $1 AND tablename = $2 `; const sizeQuery = ` SELECT pg_stat_get_live_tuples(c.oid) as estimated_row_count, pg_total_relation_size(c.oid) as total_size_bytes, pg_size_pretty(pg_total_relation_size(c.oid)) as total_size_pretty, pg_relation_size(c.oid) as table_size_bytes, pg_size_pretty(pg_relation_size(c.oid)) as table_size_pretty, pg_total_relation_size(c.oid) - pg_relation_size(c.oid) as index_size_bytes, pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) as index_size_pretty FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = $1 AND c.relname = $2 `; try { const sizeResult = await executePostgresQuery(sizeQuery, [sanitizedSchema, sanitizedTable]); if (sizeResult.length > 0) { const rawStats = sizeResult[0]; statistics = { estimated_row_count: parseInt(rawStats.estimated_row_count) || 0, table_size_bytes: parseInt(rawStats.table_size_bytes) || 0, table_size_pretty: rawStats.table_size_pretty, index_size_bytes: parseInt(rawStats.index_size_bytes) || 0, index_size_pretty: rawStats.index_size_pretty, total_size_bytes: parseInt(rawStats.total_size_bytes) || 0, total_size_pretty: rawStats.total_size_pretty, }; } } catch (error) { debug("Failed to get table statistics: %o", error); } } const response = { table: tableInfo[0], columns: columns, constraints: constraints, indexes: indexes, ...(statistics && { statistics }), generated_at: new Date().toISOString(), }; return createMcpSuccessResponse(response); } catch (error) { return createMcpErrorResponse("get table info", error); } }
  • Zod schema defining input parameters for the tool: `table` (required string), `schema_name` (optional string, defaults to 'public'), `include_statistics` (optional boolean, defaults to true). Used for validation in the handler.
    export const getTableInfoShape: ZodRawShape = { table: z.string().min(1, "Table name is required"), schema_name: z.string().optional().default("public"), include_statistics: z.boolean().optional().default(true), }; export const getTableInfoSchema = z.object(getTableInfoShape);
  • index.ts:69-74 (registration)
    Tool registration in the MCP server using `server.tool(name, description, inputSchema, handlerFunction)`, linking to the schema and handler from tools/getTableInfo.ts.
    server.tool( "get-table-info", "Get detailed information about a specific table including columns, constraints, indexes, and optionally statistics like row count and size.", getTableInfoShape, getTableInfo );

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