CRITICAL: PostgreSQL MCP Server SQL limitations and correct patterns. This skill MUST be consulted before writing any SQL for the CRM database. Documents what works and what DOES NOT work with mcp__postgresql__ tools.
The mcp__postgresql__ tools have specific limitations. Violating these rules causes SQL errors.
-- FORBIDDEN - WILL FAIL with syntax error
INSERT INTO person (name, email) VALUES ('John', '[email protected]') RETURNING id;
-- FORBIDDEN - WILL FAIL
UPDATE company_site SET name = 'New Name' WHERE id = 1 RETURNING *;
-- FORBIDDEN - WILL FAIL
DELETE FROM event WHERE id = 5 RETURNING id;
Why: The write_query tool parses SQL and rejects RETURNING clauses.
-- FORBIDDEN - WILL FAIL with "Only INSERT, UPDATE, or DELETE operations are allowed"
INSERT INTO company_site (name) VALUES ('Acme')
ON CONFLICT (name) DO UPDATE SET updated_at = CURRENT_TIMESTAMP;
-- FORBIDDEN - Even if column HAD a unique constraint
INSERT INTO person (email) VALUES ('[email protected]')
ON CONFLICT (email) DO NOTHING;
Why: The write_query tool does not support ON CONFLICT syntax.
Additional Note: The CRM tables do NOT have UNIQUE constraints on name columns anyway!
company_site.name is NOT uniqueperson.name is NOT uniquesales_opportunity.title is NOT unique-- FORBIDDEN - WILL FAIL
INSERT INTO company_site (name) VALUES ('A'); INSERT INTO company_site (name) VALUES ('B');
Why: Execute one statement per tool call.
-- FORBIDDEN
BEGIN; INSERT INTO...; COMMIT;
-- CORRECT - Simple INSERT
INSERT INTO company_site (name, address_city, created_at, updated_at)
VALUES ('Neue Firma GmbH', 'Berlin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
To get the ID after insert:
-- CORRECT - Query for the ID in a SEPARATE read_query call
SELECT id FROM company_site WHERE name = 'Neue Firma GmbH' ORDER BY created_at DESC LIMIT 1;
Step 1: Check if exists (read_query)
SELECT id, name FROM company_site WHERE name ILIKE '%Acme%' LIMIT 1;
Step 2a: If found - UPDATE (write_query)
UPDATE company_site SET updated_at = CURRENT_TIMESTAMP, notes = 'Updated info' WHERE id = 5;
Step 2b: If not found - INSERT (write_query)
INSERT INTO company_site (name, address_city, created_at, updated_at)
VALUES ('Acme GmbH', 'Munich', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
-- CORRECT
UPDATE person SET job_title = 'CEO', updated_at = CURRENT_TIMESTAMP WHERE id = 42;
-- CORRECT
DELETE FROM event WHERE id = 123;
Two-step process:
-- Step 1: INSERT (write_query)
INSERT INTO person (name, email, company_site_id, created_at, updated_at)
VALUES ('Max Mustermann', '[email protected]', 5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
-- Step 2: GET ID (read_query) - execute AFTER insert succeeds
SELECT id FROM person WHERE email = '[email protected]' ORDER BY created_at DESC LIMIT 1;
| Operation | Tool | Notes |
|---|---|---|
| SELECT | read_query | All SELECT statements |
| INSERT | write_query | No RETURNING, no ON CONFLICT |
| UPDATE | write_query | No RETURNING |
| DELETE | write_query | No RETURNING |
| Get ID after INSERT | read_query | Separate call after INSERT |
-- WRONG
INSERT INTO person (name) VALUES ('John') RETURNING id;
Fix:
-- Step 1: write_query
INSERT INTO person (name, created_at, updated_at) VALUES ('John', now(), now());
-- Step 2: read_query
SELECT id FROM person WHERE name = 'John' ORDER BY created_at DESC LIMIT 1;
-- WRONG
INSERT INTO company_site (name) VALUES ('Test')
ON CONFLICT (name) DO UPDATE SET updated_at = now();
Fix:
-- Step 1: read_query - Check existence
SELECT id FROM company_site WHERE name ILIKE '%Test%' LIMIT 1;
-- Step 2: write_query - INSERT if not found, UPDATE if found
-- If not found:
INSERT INTO company_site (name, created_at, updated_at) VALUES ('Test', now(), now());
-- If found (id=5):
UPDATE company_site SET updated_at = now() WHERE id = 5;
-- BOTH WORK - now() and CURRENT_TIMESTAMP are equivalent in PostgreSQL
INSERT INTO event (type, description, event_date, created_at)
VALUES ('call', 'Called customer', now(), CURRENT_TIMESTAMP);
Before executing SQL with write_query:
RETURNING clauseON CONFLICT clauseread_queryThis skill should be referenced by:
bel-crm-db - Main CRM database skillbel-crm-schema-write-db - Schema and SQL examplesbel-insert-file-to-crm-and-link-it - File insertionbel-download-file-from-crm-db - File retrieval