PostgreSQL MCP Server
by HenkDz
Verified
import { DatabaseConnection } from '../utils/connection.js';
interface FunctionResult {
success: boolean;
message: string;
details: unknown;
}
interface FunctionInfo {
name: string;
language: string;
returnType: string;
arguments: string;
definition: string;
volatility: string;
owner: string;
}
/**
* Get information about database functions
*/
export async function getFunctions(
connectionString: string,
functionName?: string,
schema: string = 'public'
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
let query = `
SELECT
p.proname AS name,
l.lanname AS language,
pg_get_function_result(p.oid) AS "returnType",
pg_get_function_arguments(p.oid) AS "arguments",
CASE
WHEN p.provolatile = 'i' THEN 'IMMUTABLE'
WHEN p.provolatile = 's' THEN 'STABLE'
WHEN p.provolatile = 'v' THEN 'VOLATILE'
END AS volatility,
pg_get_functiondef(p.oid) AS definition,
a.rolname AS owner
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
JOIN pg_language l ON p.prolang = l.oid
JOIN pg_authid a ON p.proowner = a.oid
WHERE n.nspname = $1
`;
const params = [schema];
if (functionName) {
query += ' AND p.proname = $2';
params.push(functionName);
}
query += ' ORDER BY p.proname';
const functions = await db.query<FunctionInfo>(query, params);
return {
success: true,
message: functionName
? `Function information for ${functionName}`
: `Found ${functions.length} functions in schema ${schema}`,
details: functions
};
} catch (error) {
return {
success: false,
message: `Failed to get function information: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Create or replace a database function
*/
export async function createFunction(
connectionString: string,
functionName: string,
parameters: string,
returnType: string,
functionBody: string,
options: {
language?: 'sql' | 'plpgsql' | 'plpython3u';
volatility?: 'VOLATILE' | 'STABLE' | 'IMMUTABLE';
schema?: string;
security?: 'INVOKER' | 'DEFINER';
replace?: boolean;
} = {}
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
const language = options.language || 'plpgsql';
const volatility = options.volatility || 'VOLATILE';
const schema = options.schema || 'public';
const security = options.security || 'INVOKER';
const createOrReplace = options.replace ? 'CREATE OR REPLACE' : 'CREATE';
// Build function creation SQL
const sql = `
${createOrReplace} FUNCTION ${schema}.${functionName}(${parameters})
RETURNS ${returnType}
LANGUAGE ${language}
${volatility}
SECURITY ${security}
AS $function$
${functionBody}
$function$;
`;
await db.query(sql);
return {
success: true,
message: `Function ${functionName} created successfully`,
details: {
name: functionName,
schema,
returnType,
language,
volatility,
security
}
};
} catch (error) {
return {
success: false,
message: `Failed to create function: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Drop a database function
*/
export async function dropFunction(
connectionString: string,
functionName: string,
parameters?: string,
options: {
schema?: string;
ifExists?: boolean;
cascade?: boolean;
} = {}
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
const schema = options.schema || 'public';
const ifExists = options.ifExists ? 'IF EXISTS' : '';
const cascade = options.cascade ? 'CASCADE' : '';
// Build function drop SQL
let sql = `DROP FUNCTION ${ifExists} ${schema}.${functionName}`;
// Add parameters if provided
if (parameters) {
sql += `(${parameters})`;
}
// Add cascade if specified
if (cascade) {
sql += ` ${cascade}`;
}
await db.query(sql);
return {
success: true,
message: `Function ${functionName} dropped successfully`,
details: {
name: functionName,
schema
}
};
} catch (error) {
return {
success: false,
message: `Failed to drop function: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Enable Row-Level Security (RLS) on a table
*/
export async function enableRLS(
connectionString: string,
tableName: string,
schema: string = 'public'
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
await db.query(`ALTER TABLE ${schema}.${tableName} ENABLE ROW LEVEL SECURITY`);
return {
success: true,
message: `Row-Level Security enabled on ${schema}.${tableName}`,
details: {
table: tableName,
schema
}
};
} catch (error) {
return {
success: false,
message: `Failed to enable RLS: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Disable Row-Level Security (RLS) on a table
*/
export async function disableRLS(
connectionString: string,
tableName: string,
schema: string = 'public'
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
await db.query(`ALTER TABLE ${schema}.${tableName} DISABLE ROW LEVEL SECURITY`);
return {
success: true,
message: `Row-Level Security disabled on ${schema}.${tableName}`,
details: {
table: tableName,
schema
}
};
} catch (error) {
return {
success: false,
message: `Failed to disable RLS: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Create a Row-Level Security policy
*/
export async function createRLSPolicy(
connectionString: string,
tableName: string,
policyName: string,
using: string,
check?: string,
options: {
schema?: string;
command?: 'ALL' | 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE';
role?: string;
replace?: boolean;
} = {}
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
const schema = options.schema || 'public';
const command = options.command || 'ALL';
const createOrReplace = options.replace ? 'CREATE OR REPLACE' : 'CREATE';
// Build policy creation SQL
let sql = `
${createOrReplace} POLICY ${policyName}
ON ${schema}.${tableName}
FOR ${command}
`;
// Add role if specified
if (options.role) {
sql += ` TO ${options.role}`;
}
// Add USING expression
sql += ` USING (${using})`;
// Add WITH CHECK expression if provided
if (check) {
sql += ` WITH CHECK (${check})`;
}
await db.query(sql);
return {
success: true,
message: `Policy ${policyName} created successfully on ${schema}.${tableName}`,
details: {
table: tableName,
schema,
policy: policyName,
command
}
};
} catch (error) {
return {
success: false,
message: `Failed to create policy: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Drop a Row-Level Security policy
*/
export async function dropRLSPolicy(
connectionString: string,
tableName: string,
policyName: string,
options: {
schema?: string;
ifExists?: boolean;
} = {}
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
const schema = options.schema || 'public';
const ifExists = options.ifExists ? 'IF EXISTS' : '';
await db.query(`DROP POLICY ${ifExists} ${policyName} ON ${schema}.${tableName}`);
return {
success: true,
message: `Policy ${policyName} dropped successfully from ${schema}.${tableName}`,
details: {
table: tableName,
schema,
policy: policyName
}
};
} catch (error) {
return {
success: false,
message: `Failed to drop policy: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}
/**
* Get Row-Level Security policies for a table
*/
export async function getRLSPolicies(
connectionString: string,
tableName?: string,
schema: string = 'public'
): Promise<FunctionResult> {
const db = DatabaseConnection.getInstance();
try {
await db.connect(connectionString);
let query = `
SELECT
schemaname,
tablename,
policyname,
roles,
cmd,
qual as "using",
with_check as "check"
FROM pg_policies
WHERE schemaname = $1
`;
const params = [schema];
if (tableName) {
query += ' AND tablename = $2';
params.push(tableName);
}
query += ' ORDER BY tablename, policyname';
const policies = await db.query(query, params);
return {
success: true,
message: tableName
? `Policies for table ${schema}.${tableName}`
: `All policies in schema ${schema}`,
details: policies
};
} catch (error) {
return {
success: false,
message: `Failed to get policies: ${error instanceof Error ? error.message : String(error)}`,
details: null
};
} finally {
await db.disconnect();
}
}