Skip to content
bel-crm-db logo

Bel Crm DB

bel-crm-db

Uses the mcp postgresql to read and write crm relevant data to the crm database:

SKILL.md

Full skill instructions

CRITICAL: SQL Limitations - READ FIRST!

Before writing ANY SQL, know these PostgreSQL MCP server limitations:

FORBIDDENWHYUSE INSTEAD
RETURNINGSyntax errorQuery ID separately with read_query
ON CONFLICTNot supportedCheck existence first, then INSERT or UPDATE
Multiple statementsNot allowedOne statement per tool call

Example - CORRECT pattern for INSERT:

-- Step 1: write_query (NO RETURNING!)
INSERT INTO company_site (name, created_at, updated_at)
VALUES ('Acme GmbH', now(), now());

-- Step 2: read_query (get ID if needed)
SELECT id FROM company_site WHERE name = 'Acme GmbH' ORDER BY created_at DESC LIMIT 1;

For complete SQL rules, see the bel-crm-sql-rules skill.


You will probably only use those tools from postgresql mcp server:

  • mcp__postgresql__read_query,
  • mcp__postgresql__write_query

This is the SCHEMA you will work on:

Hier ist das vollständige Datenbankschema:

📊 Datenbank-Schema Übersicht

Die Datenbank enthält 6 Tabellen für ein CRM-System:


  1. adressen (Legacy-Adresstabelle)

Persönliche Kontaktdaten (deutschsprachig):

SpalteTypBeschreibung
idINTEGER🔑 Primary Key
vornameVARCHARVorname
nachnameVARCHARNachname
strasse_hausnummerVARCHARStraße & Hausnummer
plzVARCHARPostleitzahl
ortVARCHAROrt
emailVARCHARE-Mail
mobilVARCHARMobilnummer
telVARCHARTelefon
erstellt_amTIMESTAMPErstellungsdatum
aktualisiert_amTIMESTAMPAktualisierungsdatum

  1. company_site (Unternehmensstandorte)

Firmeninformationen und Standorte:

SpalteTypBeschreibung
idINTEGER🔑 Primary Key
nameVARCHAR⚠️ NOT NULL - Firmenname
address_streetVARCHARStraße
address_cityVARCHARStadt
address_stateVARCHARBundesland
address_postal_codeVARCHARPLZ
address_countryVARCHARLand
industryVARCHARBranche
websiteVARCHARWebsite
linkedin_company_urlVARCHARLinkedIn-Profil
company_sizeVARCHARUnternehmensgröße
annual_revenueBIGINTJahresumsatz
notesTEXTNotizen
tagsJSONBTags (JSON)
created_atTIMESTAMPErstellt am
updated_atTIMESTAMPAktualisiert am

  1. person (Kontaktpersonen)

Ansprechpartner in Unternehmen:

SpalteTypBeschreibung
idINTEGER🔑 Primary Key
nameVARCHAR⚠️ NOT NULL - Name
emailVARCHARE-Mail
phoneVARCHARTelefon
linkedin_urlVARCHARLinkedIn-Profil
company_site_idINTEGER🔗 FK → company_site
job_titleVARCHARJobtitel
departmentVARCHARAbteilung
notesTEXTNotizen
tagsJSONBTags (JSON)
created_atTIMESTAMPErstellt am
updated_atTIMESTAMPAktualisiert am

  1. sales_opportunity (Verkaufschancen)

Sales-Pipeline und Opportunities:

SpalteTypBeschreibung
idINTEGER🔑 Primary Key
titleVARCHAR⚠️ NOT NULL - Titel
value_eurNUMERICWert in EUR
probabilityINTEGERWahrscheinlichkeit (%)
statusVARCHARStatus (default: 'open')
descriptionTEXTBeschreibung
expected_close_dateDATEErwarteter Abschluss
actual_close_dateDATETatsächlicher Abschluss
person_idINTEGER🔗 FK → person
company_site_idINTEGER🔗 FK → company_site
sourceVARCHARQuelle
competitorsTEXTWettbewerber
next_stepsTEXTNächste Schritte
notesTEXTNotizen
tagsJSONBTags (JSON)
created_atTIMESTAMPErstellt am
updated_atTIMESTAMPAktualisiert am

  1. event (Aktivitäten/​Events)

Aktivitätsverlauf (Meetings, Calls, etc.):

SpalteTypBeschreibung
idINTEGER🔑 Primary Key
typeVARCHAR⚠️ NOT NULL - Event-Typ
descriptionTEXT⚠️ NOT NULL - Beschreibung
event_dateTIMESTAMP⚠️ NOT NULL - Event-Datum
person_idINTEGER🔗 FK → person
company_site_idINTEGER🔗 FK → company_site
opportunity_idINTEGER🔗 FK → sales_opportunity
metadataJSONBZusatzdaten (JSON)
created_atTIMESTAMPErstellt am

  1. data_files (Dateien & E-Mail-Anhänge)

Speichert Dateien (PDFs, Bilder, Office-Dokumente, E-Mail-Anhänge) mit Base64-Encoding:

SpalteTypBeschreibung
idINTEGER🔑 Primary Key
person_idINTEGER🔗 FK → person
company_site_idINTEGER🔗 FK → company_site
event_idINTEGER🔗 FK → event
sales_opportunity_idINTEGER🔗 FK → sales_opportunity
filenameVARCHAR⚠️ NOT NULL - Dateiname (oder E-Mail-Subject)
file_typeVARCHAR⚠️ NOT NULL - pdf, image, docx, pptx, xlsx, email, other
mime_typeVARCHARMIME-Type (z.B. application/​pdf, message/​rfc822)
file_size_bytesBIGINTDateigröße in Bytes (max 100MB)
file_hashVARCHARSHA-256 Hash (Duplikat-Erkennung)
sourceVARCHAR⚠️ NOT NULL - email_attachment, user_upload, agent_download, email_message
source_email_idVARCHARMS365 Message ID
source_pathVARCHAROriginaler Dateipfad
file_dataTEXTBase64-kodierter Dateiinhalt (NULL bei E-Mails ohne Anhang)
email_metadataJSONBE-Mail-Metadaten (from, to, cc, subject, date, importance)
email_body_textTEXTE-Mail-Body als Plain Text
email_body_htmlTEXTE-Mail-Body als HTML
extracted_textTEXTExtrahierter Text (für Volltextsuche)
extraction_methodVARCHARpdf_text, ocr, docx_parse, email_body, etc.
extraction_statusVARCHARpending, completed, failed, skipped
extraction_errorTEXTFehlermeldung bei Extraktion
descriptionTEXTBenutzer-Beschreibung
tagsJSONBTags (JSON)
created_atTIMESTAMPErstellt am
updated_atTIMESTAMPAktualisiert am

Beispiel - Komplette E-Mail speichern (mit Body):

INSERT INTO data_files (
    person_id, filename, file_type, mime_type,
    source, source_email_id, email_metadata,
    email_body_text, email_body_html,
    extracted_text, extraction_method, extraction_status
) VALUES (
    1,
    'Re: Contract Draft',  -- Subject als filename
    'email',
    'message/​rfc822',
    'email_message',
    'AAMkAGI2TG93AAA=',
    '{
      "subject": "Re: Contract Draft",
      "from": {"name": "John Doe", "email": "[email protected]"},
      "to": [{"email": "[email protected]"}],
      "cc": [{"email": "[email protected]"}],
      "received_at": "2025-01-15T10:30:00Z",
      "importance": "high",
      "hasAttachments": true,
      "conversationId": "AAQkAGI2..."
    }'::jsonb,
    'Hallo, anbei der überarbeitete Vertragsentwurf...',  -- Plain text
    '<html><body><p>Hallo, anbei der überarbeitete Vertragsentwurf...</​p></​body></​html>',
    'Hallo, anbei der überarbeitete Vertragsentwurf...',  -- Für Suche
    'email_body',
    'completed'
) RETURNING id, filename;

Beispiel - E-Mail-Anhang speichern (verknüpft mit E-Mail):

-- Erst E-Mail speichern, dann Anhänge mit gleicher source_email_id
INSERT INTO data_files (
    person_id, filename, file_type, mime_type, file_size_bytes,
    source, source_email_id, file_data, email_metadata
) VALUES (
    1, 'contract.pdf', 'pdf', 'application/​pdf', 245678,
    'email_attachment', 'AAMkAGI2TG93AAA=',  -- Gleiche ID wie E-Mail!
    '<base64-encoded-content>',
    '{"subject": "Re: Contract Draft", "from": {"email": "[email protected]"}}'::jsonb
) RETURNING id, filename;

Alle Dateien einer E-Mail finden (E-Mail + Anhänge):

SELECT id, filename, file_type, source, file_size_bytes
FROM data_files
WHERE source_email_id = 'AAMkAGI2TG93AAA='
ORDER BY file_type = 'email' DESC, filename;

Beispiel - Datei aus _DATA_FROM_USER hochladen:

INSERT INTO data_files (
    company_site_id, filename, file_type, mime_type, file_size_bytes,
    source, source_path, file_data
) VALUES (
    5, 'proposal.pdf', 'pdf', 'application/​pdf', 512000,
    'user_upload', '_DATA_FROM_USER/​proposal.pdf',
    '<base64-encoded-content>'
) RETURNING id, filename;

Volltextsuche in extrahiertem Text:

SELECT id, filename, person_id, company_site_id
FROM data_files
WHERE to_tsvector('german', extracted_text) @@ to_tsquery('german', 'Vertrag & Angebot');

  1. schema_migrations (System-Tabelle)

Datenbank-Migrationen:

SpalteTypBeschreibung
idBIGINTMigrations-ID
appliedTIMESTAMPAusführungsdatum
descriptionVARCHARBeschreibung

🔗 Beziehungen (Foreign Keys)

company_site (1) ──┬── (N) person ├── (N) sales_opportunity ├── (N) event └── (N) data_files

person (1) ────────┬── (N) sales_opportunity ├── (N) event └── (N) data_files

sales_opportunity (1) ─┬── (N) event └── (N) data_files

event (1) ──────────── (N) data_files


⚠️ WICHTIG: KEINE UNIQUE CONSTRAINTS AUF name!

Die Tabellen haben KEINE UNIQUE-Constraints auf name:

  • company_site.name ist NICHT unique
  • person.name ist NICHT unique
  • sales_opportunity.title ist NICHT unique

NIEMALS ON CONFLICT (name) verwenden! Das führt zu Fehlern!

Stattdessen:

  1. ERST prüfen ob Eintrag existiert: SELECT id FROM company_site WHERE name ILIKE '%..%'
  2. DANN entweder UPDATE (wenn gefunden) oder INSERT (wenn nicht gefunden)

Beispiel - Firma erstellen oder finden:

-- SCHRITT 1: Prüfen ob existiert
SELECT id, name FROM company_site WHERE name ILIKE '%Kölner Stadt%' LIMIT 1;

-- SCHRITT 2a: Falls gefunden → UPDATE
UPDATE company_site SET updated_at = CURRENT_TIMESTAMP, notes = 'Updated...' WHERE id = <gefundene_id>;

-- SCHRITT 2b: Falls nicht gefunden → INSERT
INSERT INTO company_site (name, address_city, created_at, updated_at)
VALUES ('Neue Firma GmbH', 'Berlin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
RETURNING id;

💡 Besonderheiten

  • JSONB-Felder: tags, metadata, email_metadata für flexible Erweiterungen
  • Timestamps: Auto-Update via CURRENT_TIMESTAMP
  • Dual-System: Alte adressen-Tabelle + neues CRM-Schema
  • Multi-Entity Events: Events können mit Person, Firma ODER Opportunity verknüpft sein
  • Dateispeicherung: Base64-kodiert in TEXT-Feld (max 100MB)
  • Volltextsuche: GIN-Index auf extracted_text für deutsche Suche
  • Duplikat-Erkennung: SHA-256 Hash auf file_hash

📎 Datei-Workflows

E-Mail-Anhänge extrahieren (MS365)

  1. E-Mails suchen:

    mcp__ms365__list-mail-messages mit filter oder search
    
  2. Anhänge auflisten:

    mcp__ms365__list-mail-attachments(messageId)
    
  3. Anhang-Inhalt holen (bereits Base64!):

    mcp__ms365__get-mail-attachment(messageId, attachmentId)
    → contentBytes ist bereits Base64-kodiert
    
  4. In CRM speichern:

    INSERT INTO data_files (person_id, filename, file_type, source, file_data, email_metadata)
    VALUES (...) RETURNING id;
    

User-Upload aus _DATA_FROM_USER

  1. Datei lesen und Base64-kodieren (in Python/​Node)
  2. SHA-256 Hash berechnen für Duplikat-Check
  3. In CRM mit source = 'user_upload' speichern

Text-Extraktion

DateitypMethodeextraction_method
PDFText-Extraktion (pypdf)pdf_text
PDF (gescannt)OCR (pytesseract)pdf_ocr
BildOCR (pytesseract)ocr
DOCXpython-docxdocx_parse
PPTXpython-pptxpptx_parse
XLSXopenpyxlxlsx_parse

More Sales & marketing skills

revops logo
Sales & marketing

revops

Design and optimize systems connecting marketing, sales, and customer success into a unified revenue engine.

53.3K 104.9K
View
lead-magnets logo
Sales & marketing

lead-magnets

Strategic planning and optimization for email capture offers and gated content.

53.3K 103.4K
View
directory-submissions logo
Sales & marketing

directory-submissions

Directory submissions: the foundation layer of distribution that passes backlinks and discovery surface area into your marketing pages.

53.3K 76.3K
View
gsap-timeline logo
Sales & marketing

gsap-timeline

Sequence and choreograph multi-step animations with precise timing control and nested timelines.

15.9K 58.4K
View
launch-strategy logo
Sales & marketing

launch-strategy

Structured framework for planning product launches, feature announcements, and go-to-market strategies.

53.3K 56.8K
View
gws-drive-upload logo
Sales & marketing

gws-drive-upload

Upload files to Google Drive with automatic MIME type detection and metadata.

31.2K 47.2K
View
ci-cd-and-automation logo
Sales & marketing

ci-cd-and-automation

Automates CI/CD pipeline setup. Use when setting up or modifying build and deployment pipelines. Use when you need to automate quality gates, configure test runners in CI, or establish deployment strategies.

101.4K 43.2K
View
persona-team-lead logo
Sales & marketing

persona-team-lead

Team leadership persona with standup coordination, meeting prep, task delegation, and team communication.

31.2K 29.3K
View
recipe-find-large-files logo
Sales & marketing

recipe-find-large-files

Identify large Google Drive files consuming storage quota.

31.2K 29.2K
View

Sales & marketing AI tools

KreadoAI logo
Sales & marketing

KreadoAI

Revolutionize Video Creation with AI-Generated Avatars and Voices

Paid
View
Followr logo
Sales & marketing

Followr

Social media on autopilot

Paid
View
Logodiffusion logo
Sales & marketing

Logodiffusion

Design custom logos instantly with AI.

Paid
View
I
Sales & marketing

Ideogram

Text-heavy marketing and design images

Freemium
View
Prompt Silo logo
Sales & marketing

Prompt Silo

Boost Your SEO with Content and SEO Silos

Free
View
Marketing Blocks logo
Sales & marketing

Marketing Blocks

Revolutionize Your Marketing with AI-Powered MarketingBlocks!

Paid
View
Scrip AI logo
Sales & marketing

Scrip AI

10X faster & free AI content creation for 100+ use cases

Free
View
Tribescaler logo
Sales & marketing

Tribescaler

Tribescaler | Make Your Tweets Go Viral

Paid
View