--- name: bel-crm-db description: " Uses the mcp postgresql to read and write crm relevant data to the crm database: Its about: sales_opportunity (Verkaufschancen) person (Kontaktperson im Unternehmen) company_site (Ein Standort eines Unternehmens) event (Aktivität, TODO, ... insgesamt bilden die Aktivitäten die Historie und die Zukunft von company_site, person und sales_opportunity ab) data_files (Dateien: PDFs, Bilder, E-Mail-Anhänge, Office-Dokumente - verknüpft mit CRM-Entitäten)" --- ## CRITICAL: SQL Limitations - READ FIRST! **Before writing ANY SQL, know these PostgreSQL MCP server limitations:** | FORBIDDEN | WHY | USE INSTEAD | |-----------|-----|-------------| | `RETURNING` | Syntax error | Query ID separately with `read_query` | | `ON CONFLICT` | Not supported | Check existence first, then INSERT or UPDATE | | Multiple statements | Not allowed | One statement per tool call | **Example - CORRECT pattern for INSERT:** ```sql -- 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): | Spalte | Typ | Beschreibung | |--------------------|-----------|----------------------| | id | INTEGER | 🔑 Primary Key | | vorname | VARCHAR | Vorname | | nachname | VARCHAR | Nachname | | strasse_hausnummer | VARCHAR | Straße & Hausnummer | | plz | VARCHAR | Postleitzahl | | ort | VARCHAR | Ort | | email | VARCHAR | E-Mail | | mobil | VARCHAR | Mobilnummer | | tel | VARCHAR | Telefon | | erstellt_am | TIMESTAMP | Erstellungsdatum | | aktualisiert_am | TIMESTAMP | Aktualisierungsdatum | --- 2. company_site (Unternehmensstandorte) Firmeninformationen und Standorte: | Spalte | Typ | Beschreibung | |----------------------|-----------|--------------------------| | id | INTEGER | 🔑 Primary Key | | name | VARCHAR | ⚠️ NOT NULL - Firmenname | | address_street | VARCHAR | Straße | | address_city | VARCHAR | Stadt | | address_state | VARCHAR | Bundesland | | address_postal_code | VARCHAR | PLZ | | address_country | VARCHAR | Land | | industry | VARCHAR | Branche | | website | VARCHAR | Website | | linkedin_company_url | VARCHAR | LinkedIn-Profil | | company_size | VARCHAR | Unternehmensgröße | | annual_revenue | BIGINT | Jahresumsatz | | notes | TEXT | Notizen | | tags | JSONB | Tags (JSON) | | created_at | TIMESTAMP | Erstellt am | | updated_at | TIMESTAMP | Aktualisiert am | --- 3. person (Kontaktpersonen) Ansprechpartner in Unternehmen: | Spalte | Typ | Beschreibung | |-----------------|-----------|----------------------| | id | INTEGER | 🔑 Primary Key | | name | VARCHAR | ⚠️ NOT NULL - Name | | email | VARCHAR | E-Mail | | phone | VARCHAR | Telefon | | linkedin_url | VARCHAR | LinkedIn-Profil | | company_site_id | INTEGER | 🔗 FK → company_site | | job_title | VARCHAR | Jobtitel | | department | VARCHAR | Abteilung | | notes | TEXT | Notizen | | tags | JSONB | Tags (JSON) | | created_at | TIMESTAMP | Erstellt am | | updated_at | TIMESTAMP | Aktualisiert am | --- 4. sales_opportunity (Verkaufschancen) Sales-Pipeline und Opportunities: | Spalte | Typ | Beschreibung | |---------------------|-----------|--------------------------| | id | INTEGER | 🔑 Primary Key | | title | VARCHAR | ⚠️ NOT NULL - Titel | | value_eur | NUMERIC | Wert in EUR | | probability | INTEGER | Wahrscheinlichkeit (%) | | status | VARCHAR | Status (default: 'open') | | description | TEXT | Beschreibung | | expected_close_date | DATE | Erwarteter Abschluss | | actual_close_date | DATE | Tatsächlicher Abschluss | | person_id | INTEGER | 🔗 FK → person | | company_site_id | INTEGER | 🔗 FK → company_site | | source | VARCHAR | Quelle | | competitors | TEXT | Wettbewerber | | next_steps | TEXT | Nächste Schritte | | notes | TEXT | Notizen | | tags | JSONB | Tags (JSON) | | created_at | TIMESTAMP | Erstellt am | | updated_at | TIMESTAMP | Aktualisiert am | --- 5. event (Aktivitäten/Events) Aktivitätsverlauf (Meetings, Calls, etc.): | Spalte | Typ | Beschreibung | |-----------------|-----------|----------------------------| | id | INTEGER | 🔑 Primary Key | | type | VARCHAR | ⚠️ NOT NULL - Event-Typ | | description | TEXT | ⚠️ NOT NULL - Beschreibung | | event_date | TIMESTAMP | ⚠️ NOT NULL - Event-Datum | | person_id | INTEGER | 🔗 FK → person | | company_site_id | INTEGER | 🔗 FK → company_site | | opportunity_id | INTEGER | 🔗 FK → sales_opportunity | | metadata | JSONB | Zusatzdaten (JSON) | | created_at | TIMESTAMP | Erstellt am | --- 6. data_files (Dateien & E-Mail-Anhänge) Speichert Dateien (PDFs, Bilder, Office-Dokumente, E-Mail-Anhänge) mit Base64-Encoding: | Spalte | Typ | Beschreibung | |----------------------|-----------|---------------------------------------------------| | id | INTEGER | 🔑 Primary Key | | person_id | INTEGER | 🔗 FK → person | | company_site_id | INTEGER | 🔗 FK → company_site | | event_id | INTEGER | 🔗 FK → event | | sales_opportunity_id | INTEGER | 🔗 FK → sales_opportunity | | filename | VARCHAR | ⚠️ NOT NULL - Dateiname (oder E-Mail-Subject) | | file_type | VARCHAR | ⚠️ NOT NULL - pdf, image, docx, pptx, xlsx, **email**, other | | mime_type | VARCHAR | MIME-Type (z.B. application/pdf, message/rfc822) | | file_size_bytes | BIGINT | Dateigröße in Bytes (max 100MB) | | file_hash | VARCHAR | SHA-256 Hash (Duplikat-Erkennung) | | source | VARCHAR | ⚠️ NOT NULL - email_attachment, user_upload, agent_download, **email_message** | | source_email_id | VARCHAR | MS365 Message ID | | source_path | VARCHAR | Originaler Dateipfad | | file_data | TEXT | Base64-kodierter Dateiinhalt (NULL bei E-Mails ohne Anhang) | | email_metadata | JSONB | E-Mail-Metadaten (from, to, cc, subject, date, importance) | | email_body_text | TEXT | E-Mail-Body als Plain Text | | email_body_html | TEXT | E-Mail-Body als HTML | | extracted_text | TEXT | Extrahierter Text (für Volltextsuche) | | extraction_method | VARCHAR | pdf_text, ocr, docx_parse, email_body, etc. | | extraction_status | VARCHAR | pending, completed, failed, skipped | | extraction_error | TEXT | Fehlermeldung bei Extraktion | | description | TEXT | Benutzer-Beschreibung | | tags | JSONB | Tags (JSON) | | created_at | TIMESTAMP | Erstellt am | | updated_at | TIMESTAMP | Aktualisiert am | Beispiel - Komplette E-Mail speichern (mit Body): ```sql 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": "john@example.com"}, "to": [{"email": "you@company.com"}], "cc": [{"email": "legal@company.com"}], "received_at": "2025-01-15T10:30:00Z", "importance": "high", "hasAttachments": true, "conversationId": "AAQkAGI2..." }'::jsonb, 'Hallo, anbei der überarbeitete Vertragsentwurf...', -- Plain text '
Hallo, anbei der überarbeitete Vertragsentwurf...
', 'Hallo, anbei der überarbeitete Vertragsentwurf...', -- Für Suche 'email_body', 'completed' ) RETURNING id, filename; ``` Beispiel - E-Mail-Anhang speichern (verknüpft mit E-Mail): ```sql -- 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! '