Skip to main content
Glama
HenkDz

PostgreSQL MCP Server

pg_execute_mutation

Execute INSERT, UPDATE, DELETE, or UPSERT operations in PostgreSQL databases to modify table data using specified parameters and conditions.

Instructions

Execute data modification operations (INSERT/UPDATE/DELETE/UPSERT) - operation="insert/update/delete/upsert" with table and data. Examples: operation="insert", table="users", data={"name":"John","email":"john@example.com"}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
connectionStringNoPostgreSQL connection string (optional)
operationYesMutation operation: insert (add rows), update (modify rows), delete (remove rows), upsert (insert or update)
tableYesTable name for the operation
dataNoData object with column-value pairs (required for insert/update/upsert)
whereNoWHERE clause for update/delete operations (without WHERE keyword)
conflictColumnsNoColumns for conflict resolution in upsert (ON CONFLICT)
returningNoRETURNING clause to get back inserted/updated data
schemaNoSchema name (defaults to public)public

Implementation Reference

  • Handler function that processes tool arguments, performs basic validation, calls the executeMutation helper, and formats the response.
    execute: async (args: unknown, getConnectionStringVal: GetConnectionStringFn): Promise<ToolOutput> => { const { connectionString: connStringArg, operation, table, data, where, conflictColumns, returning, schema } = args as { connectionString?: string; operation: 'insert' | 'update' | 'delete' | 'upsert'; table: string; data?: Record<string, unknown>; where?: string; conflictColumns?: string[]; returning?: string; schema?: string; }; const resolvedConnString = getConnectionStringVal(connStringArg); try { // Input validation if (!table?.trim()) { return { content: [{ type: 'text', text: 'Error: table is required' }], isError: true }; } const result = await executeMutation({ connectionString: resolvedConnString, operation, table, data, where, conflictColumns, returning, schema: schema || 'public' } as ExecuteMutationInput, getConnectionStringVal); let responseText = `${operation.toUpperCase()} operation completed successfully. Rows affected: ${result.rowsAffected}`; if (result.returning && result.returning.length > 0) { responseText += `\n\nReturning data:\n${JSON.stringify(result.returning, null, 2)}`; } return { content: [{ type: 'text', text: responseText }] }; } catch (error) { return { content: [{ type: 'text', text: `Error executing ${operation} operation: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }
  • Core helper function that constructs and executes the specific SQL mutation (INSERT, UPDATE, DELETE, UPSERT) using dynamic SQL building and database connection.
    async function executeMutation( input: ExecuteMutationInput, getConnectionString: GetConnectionStringFn ): Promise<{ operation: string; rowsAffected: number; returning?: unknown[] }> { const resolvedConnectionString = getConnectionString(input.connectionString); const db = DatabaseConnection.getInstance(); const { operation, table, data, where, conflictColumns, returning, schema } = input; try { await db.connect(resolvedConnectionString); const schemaPrefix = (schema && schema !== 'public') ? `"${schema}".` : ''; const tableName = `${schemaPrefix}"${table}"`; switch (operation) { case 'insert': { if (!data || Object.keys(data).length === 0) { throw new McpError(ErrorCode.InvalidParams, 'Data object is required for insert operation'); } const columns = Object.keys(data); const values = Object.values(data); const placeholders = values.map((_, i) => `$${i + 1}`).join(', '); let insertSQL = `INSERT INTO ${tableName} (${columns.map(col => `"${col}"`).join(', ')}) VALUES (${placeholders})`; if (returning) { insertSQL += ` RETURNING ${returning}`; } const result = await db.query(insertSQL, values); return { operation: 'insert', rowsAffected: Array.isArray(result) ? result.length : 1, returning: returning ? result : undefined }; } case 'update': { if (!data || Object.keys(data).length === 0) { throw new McpError(ErrorCode.InvalidParams, 'Data object is required for update operation'); } if (!where) { throw new McpError(ErrorCode.InvalidParams, 'WHERE clause is required for update operation to prevent accidental full table updates'); } const columns = Object.keys(data); const values = Object.values(data); const setClause = columns.map((col, i) => `"${col}" = $${i + 1}`).join(', '); let updateSQL = `UPDATE ${tableName} SET ${setClause} WHERE ${where}`; if (returning) { updateSQL += ` RETURNING ${returning}`; } const result = await db.query(updateSQL, values); return { operation: 'update', rowsAffected: Array.isArray(result) ? result.length : 1, returning: returning ? result : undefined }; } case 'delete': { if (!where) { throw new McpError(ErrorCode.InvalidParams, 'WHERE clause is required for delete operation to prevent accidental full table deletion'); } let deleteSQL = `DELETE FROM ${tableName} WHERE ${where}`; if (returning) { deleteSQL += ` RETURNING ${returning}`; } const result = await db.query(deleteSQL); return { operation: 'delete', rowsAffected: Array.isArray(result) ? result.length : 1, returning: returning ? result : undefined }; } case 'upsert': { if (!data || Object.keys(data).length === 0) { throw new McpError(ErrorCode.InvalidParams, 'Data object is required for upsert operation'); } if (!conflictColumns || conflictColumns.length === 0) { throw new McpError(ErrorCode.InvalidParams, 'Conflict columns are required for upsert operation'); } const columns = Object.keys(data); const values = Object.values(data); const placeholders = values.map((_, i) => `$${i + 1}`).join(', '); const conflictCols = conflictColumns.map(col => `"${col}"`).join(', '); const updateClause = columns .filter(col => !conflictColumns.includes(col)) .map(col => `"${col}" = EXCLUDED."${col}"`) .join(', '); let upsertSQL = `INSERT INTO ${tableName} (${columns.map(col => `"${col}"`).join(', ')}) VALUES (${placeholders}) ON CONFLICT (${conflictCols})`; if (updateClause) { upsertSQL += ` DO UPDATE SET ${updateClause}`; } else { upsertSQL += ' DO NOTHING'; } if (returning) { upsertSQL += ` RETURNING ${returning}`; } const result = await db.query(upsertSQL, values); return { operation: 'upsert', rowsAffected: Array.isArray(result) ? result.length : 1, returning: returning ? result : undefined }; } default: throw new McpError(ErrorCode.InvalidParams, `Unknown operation: ${operation}`); } } catch (error) { throw new McpError(ErrorCode.InternalError, `Failed to execute ${operation}: ${error instanceof Error ? error.message : String(error)}`); } finally { await db.disconnect(); } }
  • Zod schema defining the input parameters for the pg_execute_mutation tool.
    const ExecuteMutationInputSchema = z.object({ connectionString: z.string().optional().describe('PostgreSQL connection string (optional)'), operation: z.enum(['insert', 'update', 'delete', 'upsert']).describe('Mutation operation: insert (add rows), update (modify rows), delete (remove rows), upsert (insert or update)'), table: z.string().describe('Table name for the operation'), data: z.record(z.unknown()).optional().describe('Data object with column-value pairs (required for insert/update/upsert)'), where: z.string().optional().describe('WHERE clause for update/delete operations (without WHERE keyword)'), conflictColumns: z.array(z.string()).optional().describe('Columns for conflict resolution in upsert (ON CONFLICT)'), returning: z.string().optional().describe('RETURNING clause to get back inserted/updated data'), schema: z.string().optional().default('public').describe('Schema name (defaults to public)') });
  • Definition and export of the PostgresTool object for pg_execute_mutation.
    export const executeMutationTool: PostgresTool = { name: 'pg_execute_mutation', description: 'Execute data modification operations (INSERT/UPDATE/DELETE/UPSERT) - operation="insert/update/delete/upsert" with table and data. Examples: operation="insert", table="users", data={"name":"John","email":"john@example.com"}', inputSchema: ExecuteMutationInputSchema, execute: async (args: unknown, getConnectionStringVal: GetConnectionStringFn): Promise<ToolOutput> => { const { connectionString: connStringArg, operation, table, data, where, conflictColumns, returning, schema } = args as { connectionString?: string; operation: 'insert' | 'update' | 'delete' | 'upsert'; table: string; data?: Record<string, unknown>; where?: string; conflictColumns?: string[]; returning?: string; schema?: string; }; const resolvedConnString = getConnectionStringVal(connStringArg); try { // Input validation if (!table?.trim()) { return { content: [{ type: 'text', text: 'Error: table is required' }], isError: true }; } const result = await executeMutation({ connectionString: resolvedConnString, operation, table, data, where, conflictColumns, returning, schema: schema || 'public' } as ExecuteMutationInput, getConnectionStringVal); let responseText = `${operation.toUpperCase()} operation completed successfully. Rows affected: ${result.rowsAffected}`; if (result.returning && result.returning.length > 0) { responseText += `\n\nReturning data:\n${JSON.stringify(result.returning, null, 2)}`; } return { content: [{ type: 'text', text: responseText }] }; } catch (error) { return { content: [{ type: 'text', text: `Error executing ${operation} operation: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } };
  • src/index.ts:225-257 (registration)
    Inclusion of executeMutationTool in the allTools array passed to the MCP server constructor for registration.
    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 ];

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