Skip to main content
Glama
HenkDz

PostgreSQL MCP Server

pg_manage_comments

Manage PostgreSQL database object comments - retrieve, add, update, or delete descriptive text on tables, columns, functions, and other database objects to document schema structure.

Instructions

Manage PostgreSQL object comments - get, set, remove comments on tables, columns, functions, and other database objects. Examples: operation="get" with objectType="table", objectName="users", operation="set" with comment text, operation="bulk_get" for discovery

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
connectionStringNoPostgreSQL connection string (optional)
operationYesOperation: get (retrieve comments), set (add/update comment), remove (delete comment), bulk_get (discovery mode)
objectTypeNoType of database object (required for get/set/remove)
objectNameNoName of the object (required for get/set/remove)
schemaNoSchema name (defaults to public, required for most object types)
columnNameNoColumn name (required when objectType is "column")
commentNoComment text (required for set operation)
includeSystemObjectsNoInclude system objects in bulk_get (defaults to false)
filterObjectTypeNoFilter by object type in bulk_get operation

Implementation Reference

  • Main tool handler for 'pg_manage_comments'. Defines the tool object with name, description, schema reference, and execute function that validates input and dispatches to specific operations: get, set, remove, bulk_get.
    export const manageCommentsTool: PostgresTool = {
      name: 'pg_manage_comments',
      description: 'Manage PostgreSQL object comments - get, set, remove comments on tables, columns, functions, and other database objects. Examples: operation="get" with objectType="table", objectName="users", operation="set" with comment text, operation="bulk_get" for discovery',
      inputSchema: ManageCommentsInputSchema,
      execute: async (args: unknown, getConnectionStringVal: GetConnectionStringFn): Promise<ToolOutput> => {
        const validationResult = ManageCommentsInputSchema.safeParse(args);
        if (!validationResult.success) {
          return { 
            content: [{ type: 'text', text: `Invalid input: ${validationResult.error.format()}` }], 
            isError: true 
          };
        }
    
        const input = validationResult.data;
    
        try {
          switch (input.operation) {
            case 'get': {
              const result = await executeGetComment(input, getConnectionStringVal);
              if (!result) {
                return { 
                  content: [{ type: 'text', text: `No comment found for ${input.objectType} ${input.objectName}` }] 
                };
              }
              return { 
                content: [
                  { type: 'text', text: `Comment for ${input.objectType} ${input.objectName}${input.columnName ? `.${input.columnName}` : ''}` },
                  { type: 'text', text: JSON.stringify(result, null, 2) }
                ] 
              };
            }
    
            case 'set': {
              const result = await executeSetComment(input, getConnectionStringVal);
              return { 
                content: [
                  { type: 'text', text: `Comment set successfully on ${result.objectType} ${result.objectName}${result.columnName ? `.${result.columnName}` : ''}` },
                  { type: 'text', text: JSON.stringify(result, null, 2) }
                ] 
              };
            }
    
            case 'remove': {
              const result = await executeRemoveComment(input, getConnectionStringVal);
              return { 
                content: [
                  { type: 'text', text: `Comment removed from ${result.objectType} ${result.objectName}${result.columnName ? `.${result.columnName}` : ''}` },
                  { type: 'text', text: JSON.stringify(result, null, 2) }
                ] 
              };
            }
    
            case 'bulk_get': {
              const result = await executeBulkGetComments(input, getConnectionStringVal);
              return { 
                content: [
                  { type: 'text', text: `Found ${result.length} comments in schema ${input.schema || 'public'}` },
                  { type: 'text', text: JSON.stringify(result, null, 2) }
                ] 
              };
            }
    
            default:
              return { 
                content: [{ type: 'text', text: `Error: Unknown operation "${input.operation}". Supported operations: get, set, remove, bulk_get` }], 
                isError: true 
              };
          }
    
        } catch (error) {
          const errorMessage = error instanceof McpError ? error.message : (error instanceof Error ? error.message : String(error));
          return { 
            content: [{ type: 'text', text: `Error executing ${input.operation} operation: ${errorMessage}` }], 
            isError: true 
          };
        }
      }
    }; 
  • Zod input schema defining parameters for the pg_manage_comments tool, including operation type and object identifiers.
    const ManageCommentsInputSchema = z.object({
      connectionString: z.string().optional().describe('PostgreSQL connection string (optional)'),
      operation: z.enum(['get', 'set', 'remove', 'bulk_get']).describe('Operation: get (retrieve comments), set (add/update comment), remove (delete comment), bulk_get (discovery mode)'),
      
      // Target object identification
      objectType: z.enum(['table', 'column', 'index', 'constraint', 'function', 'trigger', 'view', 'sequence', 'schema', 'database']).optional().describe('Type of database object (required for get/set/remove)'),
      objectName: z.string().optional().describe('Name of the object (required for get/set/remove)'),
      schema: z.string().optional().describe('Schema name (defaults to public, required for most object types)'),
      
      // Column-specific parameters
      columnName: z.string().optional().describe('Column name (required when objectType is "column")'),
      
      // Comment content
      comment: z.string().optional().describe('Comment text (required for set operation)'),
      
      // Bulk get parameters
      includeSystemObjects: z.boolean().optional().describe('Include system objects in bulk_get (defaults to false)'),
      filterObjectType: z.enum(['table', 'column', 'index', 'constraint', 'function', 'trigger', 'view', 'sequence', 'schema', 'database']).optional().describe('Filter by object type in bulk_get operation')
    });
  • src/index.ts:225-257 (registration)
    Registration of pg_manage_comments tool (imported as manageCommentsTool) in the central allTools array, which is passed to PostgreSQLServer constructor to make tools available.
    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
    ];
  • Helper function to retrieve (get) comment for a specific database object using dynamic SQL queries based on objectType.
    async function executeGetComment(
      input: ManageCommentsInput,
      getConnectionString: GetConnectionStringFn
    ): Promise<CommentInfo | null> {
      const resolvedConnectionString = getConnectionString(input.connectionString);
      const db = DatabaseConnection.getInstance();
      const { objectType, objectName, schema = 'public', columnName } = input;
    
      if (!objectType || !objectName) {
        throw new McpError(ErrorCode.InvalidParams, 'objectType and objectName are required for get operation');
      }
    
      try {
        await db.connect(resolvedConnectionString);
        
        let query: string;
        let params: (string | undefined)[];
    
        switch (objectType) {
          case 'table':
            query = `
              SELECT obj_description(c.oid, 'pg_class') AS comment
              FROM pg_class c
              JOIN pg_namespace n ON c.relnamespace = n.oid
              WHERE c.relname = $1 AND n.nspname = $2 AND c.relkind = 'r'
            `;
            params = [objectName, schema];
            break;
    
          case 'column':
            if (!columnName) {
              throw new McpError(ErrorCode.InvalidParams, 'columnName is required when objectType is "column"');
            }
            query = `
              SELECT col_description(c.oid, a.attnum) AS comment
              FROM pg_class c
              JOIN pg_namespace n ON c.relnamespace = n.oid
              JOIN pg_attribute a ON a.attrelid = c.oid
              WHERE c.relname = $1 AND n.nspname = $2 AND a.attname = $3 AND NOT a.attisdropped
            `;
            params = [objectName, schema, columnName];
            break;
    
          case 'index':
            query = `
              SELECT obj_description(c.oid, 'pg_class') AS comment
              FROM pg_class c
              JOIN pg_namespace n ON c.relnamespace = n.oid
              WHERE c.relname = $1 AND n.nspname = $2 AND c.relkind = 'i'
            `;
            params = [objectName, schema];
            break;
    
          case 'function':
            query = `
              SELECT obj_description(p.oid, 'pg_proc') AS comment
              FROM pg_proc p
              JOIN pg_namespace n ON p.pronamespace = n.oid
              WHERE p.proname = $1 AND n.nspname = $2
            `;
            params = [objectName, schema];
            break;
    
          case 'view':
            query = `
              SELECT obj_description(c.oid, 'pg_class') AS comment
              FROM pg_class c
              JOIN pg_namespace n ON c.relnamespace = n.oid
              WHERE c.relname = $1 AND n.nspname = $2 AND c.relkind = 'v'
            `;
            params = [objectName, schema];
            break;
    
          case 'sequence':
            query = `
              SELECT obj_description(c.oid, 'pg_class') AS comment
              FROM pg_class c
              JOIN pg_namespace n ON c.relnamespace = n.oid
              WHERE c.relname = $1 AND n.nspname = $2 AND c.relkind = 'S'
            `;
            params = [objectName, schema];
            break;
    
          case 'schema':
            query = `
              SELECT obj_description(n.oid, 'pg_namespace') AS comment
              FROM pg_namespace n
              WHERE n.nspname = $1
            `;
            params = [objectName];
            break;
    
          case 'database':
            query = `
              SELECT shobj_description(d.oid, 'pg_database') AS comment
              FROM pg_database d
              WHERE d.datname = $1
            `;
            params = [objectName];
            break;
    
          case 'constraint':
            query = `
              SELECT obj_description(con.oid, 'pg_constraint') AS comment
              FROM pg_constraint con
              JOIN pg_namespace n ON con.connamespace = n.oid
              WHERE con.conname = $1 AND n.nspname = $2
            `;
            params = [objectName, schema];
            break;
    
          case 'trigger':
            query = `
              SELECT obj_description(t.oid, 'pg_trigger') AS comment
              FROM pg_trigger t
              JOIN pg_class c ON t.tgrelid = c.oid
              JOIN pg_namespace n ON c.relnamespace = n.oid
              WHERE t.tgname = $1 AND n.nspname = $2
            `;
            params = [objectName, schema];
            break;
    
          default:
            throw new McpError(ErrorCode.InvalidParams, `Unsupported object type: ${objectType}`);
        }
    
        const result = await db.query(query, params);
        
        if (result.length === 0) {
          return null;
        }
    
        return {
          objectType,
          objectName,
          objectSchema: objectType !== 'database' && objectType !== 'schema' ? schema : undefined,
          columnName,
          comment: result[0].comment as string | null
        };
    
      } catch (error) {
        throw new McpError(ErrorCode.InternalError, `Failed to get comment: ${error instanceof Error ? error.message : String(error)}`);
      } finally {
        await db.disconnect();
      }
    }
  • Helper function to set/update comment on database objects using generated COMMENT ON SQL.
    async function executeSetComment(
      input: ManageCommentsInput,
      getConnectionString: GetConnectionStringFn
    ): Promise<{ objectType: string; objectName: string; schema?: string; columnName?: string; comment: string }> {
      const resolvedConnectionString = getConnectionString(input.connectionString);
      const db = DatabaseConnection.getInstance();
      const { objectType, objectName, schema = 'public', columnName, comment } = input;
    
      if (!objectType || !objectName || comment === undefined) {
        throw new McpError(ErrorCode.InvalidParams, 'objectType, objectName, and comment are required for set operation');
      }
    
      try {
        await db.connect(resolvedConnectionString);
        
        let sql: string;
        const escapedComment = comment.replace(/'/g, "''"); // Escape single quotes
    
        switch (objectType) {
          case 'table':
            sql = `COMMENT ON TABLE "${schema}"."${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'column':
            if (!columnName) {
              throw new McpError(ErrorCode.InvalidParams, 'columnName is required when objectType is "column"');
            }
            sql = `COMMENT ON COLUMN "${schema}"."${objectName}"."${columnName}" IS '${escapedComment}'`;
            break;
    
          case 'index':
            sql = `COMMENT ON INDEX "${schema}"."${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'function':
            // Note: This is simplified - in practice, you'd need to handle function overloads
            sql = `COMMENT ON FUNCTION "${schema}"."${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'view':
            sql = `COMMENT ON VIEW "${schema}"."${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'sequence':
            sql = `COMMENT ON SEQUENCE "${schema}"."${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'schema':
            sql = `COMMENT ON SCHEMA "${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'database':
            sql = `COMMENT ON DATABASE "${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'constraint':
            sql = `COMMENT ON CONSTRAINT "${objectName}" ON "${schema}"."${objectName}" IS '${escapedComment}'`;
            break;
    
          case 'trigger':
            // Note: PostgreSQL doesn't support COMMENT ON TRIGGER directly
            throw new McpError(ErrorCode.InvalidParams, 'PostgreSQL does not support comments on triggers');
    
          default:
            throw new McpError(ErrorCode.InvalidParams, `Unsupported object type: ${objectType}`);
        }
    
        await db.query(sql);
    
        return {
          objectType,
          objectName,
          schema: objectType !== 'database' && objectType !== 'schema' ? schema : undefined,
          columnName,
          comment
        };
    
      } catch (error) {
        throw new McpError(ErrorCode.InternalError, `Failed to set comment: ${error instanceof Error ? error.message : String(error)}`);
      } finally {
        await db.disconnect();
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It describes the core operations (get, set, remove, bulk_get) and provides examples, but doesn't mention important behavioral aspects like authentication requirements (connection string handling), error conditions, or whether operations are transactional/reversible. The examples help but don't fully compensate for the lack of annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with two sentences: one stating the purpose and scope, and another providing concrete examples. It's front-loaded with the core functionality and uses the examples efficiently to illustrate usage without unnecessary elaboration.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 9 parameters, no annotations, and no output schema, the description is somewhat incomplete. While it covers the basic purpose and provides examples, it doesn't address important contextual aspects like return values (especially critical with no output schema), error handling, or the relationship between parameters (e.g., which parameters are required for which operations beyond the schema's 'required' field).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the schema already documents all 9 parameters thoroughly. The description adds minimal value beyond what's in the schema - it mentions 'operation' examples and 'objectType' examples but doesn't provide additional semantic context about parameter interactions or usage patterns beyond what the schema descriptions already state.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('get, set, remove') and resources ('PostgreSQL object comments'), and distinguishes it from siblings by focusing exclusively on comment management rather than broader database operations like query execution, user management, or constraint handling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use the tool (managing comments on various database objects) and includes examples that illustrate different operations. However, it doesn't explicitly state when NOT to use it or name specific alternatives among the sibling tools for overlapping functionality.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/HenkDz/postgresql-mcp-server'

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