> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fynn.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP-Server

> Verbinde KI-Assistenten wie Claude direkt mit Fynn: Kunden suchen, Rechnungen abrufen, Angebote erstellen und DATEV-Exporte starten, alles über natürliche Sprache.

Der Fynn MCP-Server setzt das [Model Context Protocol](https://modelcontextprotocol.io) um und macht die wichtigsten Fynn-Funktionen für KI-Assistenten wie Claude direkt zugänglich. Statt eine Benutzeroberfläche zu öffnen, beschreibst du, was du brauchst, und der Assistent ruft die passenden Tools auf.

Typische Einsatzfälle:

* Kunden- und Rechnungsdaten per Sprache abfragen
* Angebote automatisiert befüllen, inklusive Preisrecherche und Bildern
* DATEV-Cloud-Export per Bestätigungsdialog starten

## Verbindung einrichten

### Endpunkt

```
POST https://coreapi.io/portal/mcp
```

Für die Sandbox-Umgebung ersetze den Host durch `preview.coreapi.io`.

### Transport

Der Server verwendet **Streamable HTTP** (MCP-Spezifikation 2025-03-26). MCP-Clients, die dieses Transport-Protokoll unterstützen, verbinden sich direkt mit dem Endpunkt oben.

### Authentifizierung

Jede Anfrage benötigt zwei Header:

| Header             | Wert                     |
| ------------------ | ------------------------ |
| `Authorization`    | `Bearer <API-Schlüssel>` |
| `X-Fynn-Tenant-Id` | UUID der Organisation    |

Den API-Schlüssel erstellst du unter [Einstellungen > API-Schlüssel](https://app.fynn.eu/settings/api-tokens). Die Organisations-ID findest du in den Einstellungen deiner Organisation.

### Konfigurationsbeispiel

Die meisten MCP-Clients (z. B. Claude Desktop) nehmen eine JSON-Konfiguration. Hier ein Beispiel:

```json theme={null}
{
  "mcpServers": {
    "fynn": {
      "type": "http",
      "url": "https://coreapi.io/portal/mcp",
      "headers": {
        "Authorization": "Bearer <API-SCHLÜSSEL>",
        "X-Fynn-Tenant-Id": "<ORGANISATIONS-ID>"
      }
    }
  }
}
```

## Berechtigungen

Alle Tools respektieren die Rollen-Berechtigungen des verbundenen Nutzers. Wer in Fynn keine Rechnung lesen darf, bekommt über den MCP-Server auch keine. Die Anfrage schlägt in dem Fall mit einer klaren Fehlermeldung fehl, statt leere Daten zu liefern.

***

## Tool-Referenz

Die folgenden 31 Tools stehen zur Verfügung, gruppiert nach Funktionsbereich.

### Allgemein

#### `fynn_ping`

Prüft die Verbindung, Authentifizierung und den Organisations-Kontext. Gibt `ok`, die Organisations-ID und einen Zeitstempel zurück. Nimmt keine Parameter entgegen.

***

### Kunden

#### `customer_search`

Sucht nach Kunden anhand eines Suchbegriffs oder einer Abonummer.

| Parameter            | Typ     | Pflicht | Beschreibung                                                        |
| -------------------- | ------- | ------- | ------------------------------------------------------------------- |
| `search`             | string  |         | Teiltreffer auf Name, Firma, Kundennummer oder DATEV-Debitorenkonto |
| `subscriptionNumber` | string  |         | Abonummer, über die der zugehörige Kunde direkt ermittelt wird      |
| `page`               | integer |         | Seitennummer (1-basiert, Standard 1)                                |
| `limit`              | integer |         | Treffer pro Seite (max. 50, Standard 20)                            |

Gibt Identitätsfelder zurück (ID, Kundennummer, Anzeigename, Typ, Status), keine Adressen oder Kontaktdaten.

***

#### `customer_balance`

Liefert den Debitorensaldo eines Kunden. Ohne Zeitraum den aktuellen Gesamtsaldo, mit Zeitraum zusätzlich Soll-/Haben-Summen. Beträge sind in Cent mit Währungsangabe.

| Parameter    | Typ    | Pflicht | Beschreibung                                                 |
| ------------ | ------ | ------- | ------------------------------------------------------------ |
| `customerId` | string | ja      | UUID des Kunden                                              |
| `yearMonth`  | string |         | Monat im Format `YYYY-MM`, z. B. `2026-01`                   |
| `from`       | string |         | Untere Datumsgrenze `YYYY-MM-DD` (alternativ zu `yearMonth`) |
| `to`         | string |         | Obere Datumsgrenze `YYYY-MM-DD` (alternativ zu `yearMonth`)  |

***

#### `customer_payments`

Listet Zahlungstransaktionen eines Kunden, neueste zuerst. Enthält Datum, Betrag (Cent), Status, Zahlungsart und Belegnummer, jedoch keine Bankdaten oder IBAN.

| Parameter    | Typ     | Pflicht | Beschreibung                              |
| ------------ | ------- | ------- | ----------------------------------------- |
| `customerId` | string  | ja      | UUID des Kunden                           |
| `dateFrom`   | string  |         | Untere Datumsgrenze `YYYY-MM-DD`          |
| `dateTo`     | string  |         | Obere Datumsgrenze `YYYY-MM-DD`           |
| `page`       | integer |         | Seitennummer (Standard 1)                 |
| `limit`      | integer |         | Treffer pro Seite (max. 100, Standard 25) |

***

### Rechnungen

#### `invoice_search`

Sucht Belege (Rechnungen, Gutschriften, Stornos). Filterbar nach Status, Belegtyp, Kunde und Finalisierungszeitraum. Beträge in Cent, Daten als ISO-8601.

| Parameter         | Typ     | Pflicht | Beschreibung                                                                   |
| ----------------- | ------- | ------- | ------------------------------------------------------------------------------ |
| `search`          | string  |         | Teiltreffer auf Belegnummer oder Kundennummer                                  |
| `statuses`        | array   |         | Belegstatus, z. B. `["STATUS_PAID", "STATUS_UNPAID"]`                          |
| `type`            | string  |         | Belegtyp: `TYPE_INVOICE`, `TYPE_CREDIT`, `TYPE_CANCEL`, `TYPE_REFUND`          |
| `customerId`      | string  |         | UUID des Kunden                                                                |
| `finalizedAfter`  | string  |         | Untere Datumsgrenze `YYYY-MM-DD`                                               |
| `finalizedBefore` | string  |         | Obere Datumsgrenze `YYYY-MM-DD`                                                |
| `sort`            | string  |         | Sortierfeld: `finalizationDate`, `number`, `dueDate`, `creationDate`, `status` |
| `sortDirection`   | string  |         | `asc` oder `desc` (Standard `desc`)                                            |
| `page`            | integer |         | Seitennummer (Standard 1)                                                      |
| `limit`           | integer |         | Treffer pro Seite (max. 100, Standard 20)                                      |

***

#### `invoice_reference_chain`

Löst die Storno-/Referenzkette eines Belegs auf. Gibt den Beleg selbst, den referenzierten Ursprungsbeleg sowie alle Stornos/Gutschriften zurück, die auf diesen Beleg verweisen.

| Parameter       | Typ    | Pflicht | Beschreibung                            |
| --------------- | ------ | ------- | --------------------------------------- |
| `invoiceId`     | string |         | UUID des Belegs                         |
| `invoiceNumber` | string |         | Belegnummer (alternativ zu `invoiceId`) |

***

#### `invoice_pdf_download`

Erstellt einen zeitlich begrenzten Download-Link für die PDF-Datei eines Belegs. Gibt `url`, `filename` und `expiresAt` zurück, keine Binärdaten.

| Parameter      | Typ     | Pflicht | Beschreibung                                             |
| -------------- | ------- | ------- | -------------------------------------------------------- |
| `invoiceId`    | string  | ja      | UUID des Belegs                                          |
| `secondsValid` | integer |         | Gültigkeitsdauer in Sekunden (60 bis 3600, Standard 300) |

***

#### `invoices_zip_download`

Bündelt die PDFs mehrerer Belege in ein ZIP-Archiv und gibt einen signierten Download-Link zurück. Belege ohne PDF werden übersprungen.

| Parameter         | Typ    | Pflicht | Beschreibung                                     |
| ----------------- | ------ | ------- | ------------------------------------------------ |
| `finalizedAfter`  | string |         | Untere Datumsgrenze `YYYY-MM-DD`                 |
| `finalizedBefore` | string |         | Obere Datumsgrenze `YYYY-MM-DD`                  |
| `statuses`        | array  |         | Belegstatus-Filter                               |
| `type`            | string |         | Belegtyp-Filter                                  |
| `customerId`      | string |         | UUID des Kunden                                  |
| `zipName`         | string |         | Optionaler Name des ZIP-Archivs ohne Dateiendung |

### Rechnungen erstellen und bearbeiten

Die schreibenden Rechnungs-Tools erfordern die Berechtigung `invoice:write`. Eine Rechnung durchläuft dabei einen klaren Lebenszyklus: Anlegen als Entwurf, Positionen hinzufügen und bearbeiten, und schließlich Finalisieren oder Schließen. Eine finalisierte Rechnung ist unveränderlich und rechtlich bindend; für die Finalisierung ist daher eine manuelle Freigabe durch einen Menschen erforderlich.

#### `invoice_create`

Legt eine neue Rechnung im Status Entwurf an. Positionen werden nicht hier, sondern anschließend mit `invoice_add_position` oder `invoice_add_product_position` hinzugefügt.

| Parameter      | Typ    | Pflicht | Beschreibung                                                                                      |
| -------------- | ------ | ------- | ------------------------------------------------------------------------------------------------- |
| `customerId`   | string | ja      | UUID des Kunden. Der Kunde muss aktiv sein.                                                       |
| `currencyCode` | string |         | Währungscode, z. B. `"EUR"`. Ohne Angabe die Standardwährung des Kunden.                          |
| `dueDate`      | string |         | Fälligkeitsdatum im Format `YYYY-MM-DD`. Ohne Angabe die Standard-Zahlungsfrist der Organisation. |
| `title`        | string |         | Rechnungstitel. Ohne Angabe der Titel aus der Standard-Rechnungsvorlage.                          |
| `introduction` | string |         | Einleitungstext.                                                                                  |
| `closing`      | string |         | Schlusstext.                                                                                      |
| `internalNote` | string |         | Interne Notiz (nicht auf dem Beleg sichtbar).                                                     |

Gibt die neue Entwurfs-Rechnung mit Kopfdaten, Positionen und Summen (in Cent) zurück.

***

#### `invoice_get`

Liest eine Rechnung mit Kopfdaten, Positionen und Summen. Die Rechnungs-ID kann eine UUID oder eine Belegnummer sein.

| Parameter   | Typ    | Pflicht | Beschreibung                            |
| ----------- | ------ | ------- | --------------------------------------- |
| `invoiceId` | string | ja      | UUID der Rechnung oder die Belegnummer. |

Gibt zurück: Kopfdaten (id, number, status, type, customer, currencyCode, dueDate, finalizedAt, title, introduction, closing, internalNote), Summen (`totals.net`/`tax`/`gross` in Cent), Positionen und ein `editable`-Kennzeichen.

***

#### `invoice_update`

Aktualisiert die Kopf-Felder einer noch nicht finalisierten Rechnung. Nur mitgesendete Felder werden geändert; fehlende Felder bleiben unverändert.

| Parameter           | Typ    | Pflicht | Beschreibung                                                    |
| ------------------- | ------ | ------- | --------------------------------------------------------------- |
| `invoiceId`         | string | ja      | UUID der Rechnung.                                              |
| `title`             | string |         | Neuer Titel.                                                    |
| `introduction`      | string |         | Neuer Einleitungstext.                                          |
| `closing`           | string |         | Neuer Schlusstext.                                              |
| `internalNote`      | string |         | Interne Notiz.                                                  |
| `dueDate`           | string |         | Fälligkeitsdatum im Format `YYYY-MM-DD`.                        |
| `customerAddressId` | string |         | UUID einer bestehenden Kundenadresse als neue Rechnungsadresse. |
| `paymentMethodId`   | string |         | UUID einer bestehenden Zahlungsart des Kunden.                  |

Gibt die aktualisierte Rechnung zurück (gleiche Struktur wie `invoice_get`).

***

#### `invoice_add_position`

Fügt einer Entwurfs-Rechnung eine frei definierte Position hinzu. Preis und Steuergruppe sind immer explizit anzugeben.

| Parameter            | Typ    | Pflicht | Beschreibung                                                                 |
| -------------------- | ------ | ------- | ---------------------------------------------------------------------------- |
| `invoiceId`          | string | ja      | UUID der Entwurfs-Rechnung.                                                  |
| `name`               | string | ja      | Bezeichnung der Position.                                                    |
| `unitId`             | string | ja      | UUID der Einheit.                                                            |
| `unitPrice`          | string | ja      | Netto-Einzelpreis als String im Format `"12.34"` (Punkt als Trennzeichen).   |
| `taxGroupId`         | string | ja      | UUID der Steuergruppe.                                                       |
| `quantity`           | number |         | Menge (Standard 1, kann negativ sein).                                       |
| `description`        | string |         | Optionale Positionsbeschreibung.                                             |
| `discountPercentage` | number |         | Rabatt in Prozent (0-100). Schließt `discountFixed` aus.                     |
| `discountFixed`      | string |         | Fester Netto-Rabatt als String `"12.34"`. Schließt `discountPercentage` aus. |
| `serviceDateFrom`    | string |         | Leistungszeitraum-Beginn `YYYY-MM-DD`. Nur gemeinsam mit `serviceDateTo`.    |
| `serviceDateTo`      | string |         | Leistungszeitraum-Ende `YYYY-MM-DD`. Nur gemeinsam mit `serviceDateFrom`.    |
| `productId`          | string |         | UUID eines Produkts für Statistikzwecke (keine Preisableitung).              |

Gibt `{ positionId, invoice }` zurück, wobei `invoice` die aktualisierte Rechnung mit Summen in Cent enthält.

***

#### `invoice_add_product_position`

Leitet eine Rechnungsposition aus einem Produkt im Katalog ab. Bezeichnung, Einheit und Steuergruppe stammen aus dem Produkt; der Preis kommt entweder aus einem Flat-Fee-Preisplan oder aus einem expliziten Preis-Override.

<Info>
  Ein Preis wird nie geraten. Fehlt sowohl ein passender Preisplan als auch ein `unitPrice`-Override, lehnt das Tool die Anfrage mit einer klaren Fehlermeldung ab.
</Info>

| Parameter     | Typ    | Pflicht | Beschreibung                                                                 |
| ------------- | ------ | ------- | ---------------------------------------------------------------------------- |
| `invoiceId`   | string | ja      | UUID der Entwurfs-Rechnung.                                                  |
| `productId`   | string | ja      | UUID des Produkts, aus dem die Position abgeleitet wird.                     |
| `pricePlanId` | string |         | UUID eines Flat-Fee-Preisplans des Produkts für die Preisableitung.          |
| `unitPrice`   | string |         | Preis-Override als String `"12.34"`. Hat Vorrang vor dem Preisplan.          |
| `quantity`    | number |         | Menge (Standard 1).                                                          |
| `taxGroupId`  | string |         | Override der Steuergruppe (UUID). Ohne Angabe die Steuergruppe des Produkts. |
| `name`        | string |         | Override der Positionsbezeichnung. Ohne Angabe der Produktname.              |

Gibt `{ positionId, invoice }` zurück (Summen in Cent).

***

#### `invoice_update_position`

Aktualisiert eine Position einer bearbeitbaren Rechnung. Nur mitgesendete Felder werden geändert.

| Parameter            | Typ    | Pflicht | Beschreibung                                                                |
| -------------------- | ------ | ------- | --------------------------------------------------------------------------- |
| `invoiceId`          | string | ja      | UUID der Rechnung.                                                          |
| `positionId`         | string | ja      | UUID der zu ändernden Position.                                             |
| `name`               | string |         | Neuer Positionsname.                                                        |
| `quantity`           | number |         | Neue Menge.                                                                 |
| `unitPrice`          | string |         | Neuer Netto-Einzelpreis als String `"12.34"`.                               |
| `taxGroupId`         | string |         | UUID der Steuergruppe.                                                      |
| `description`        | string |         | Neue Positionsbeschreibung.                                                 |
| `discountPercentage` | number |         | Rabatt in Prozent (0-100). Schließt `discountFixed` aus.                    |
| `discountFixed`      | string |         | Fester Netto-Rabatt als String `"5.00"`. Schließt `discountPercentage` aus. |
| `serviceDateFrom`    | string |         | Leistungsdatum von (`YYYY-MM-DD`).                                          |
| `serviceDateTo`      | string |         | Leistungsdatum bis (`YYYY-MM-DD`).                                          |

Gibt `{ positions[], totals }` zurück (Beträge in Cent).

***

#### `invoice_remove_position`

Entfernt eine Position von einer bearbeitbaren Rechnung. Die verbleibenden Positionen werden neu nummeriert und die Summen neu berechnet.

| Parameter    | Typ    | Pflicht | Beschreibung                       |
| ------------ | ------ | ------- | ---------------------------------- |
| `invoiceId`  | string | ja      | UUID der Rechnung.                 |
| `positionId` | string | ja      | UUID der zu entfernenden Position. |

Gibt `{ positions[], totals }` zurück (Beträge in Cent).

***

#### `invoice_close`

Schließt eine Rechnung endgültig, ohne sie zu finalisieren. Der Beleg erhält den Status `STATUS_CLOSED` und wird verworfen: keine Belegnummer, kein PDF, keine Zahlung. Nur aus den Status Entwurf (`STATUS_DRAFT`) oder `STATUS_NEW` heraus möglich.

| Parameter   | Typ    | Pflicht | Beschreibung                             |
| ----------- | ------ | ------- | ---------------------------------------- |
| `invoiceId` | string | ja      | UUID der Rechnung.                       |
| `reason`    | string |         | Optionaler Kommentar (max. 255 Zeichen). |

Gibt `{ id, status }` zurück.

***

#### `invoice_finalize`

Bereitet die Finalisierung einer Rechnung vor und legt eine Freigabe-Aufgabe in der App-Inbox an. Die Rechnung wird dabei **nicht** direkt finalisiert.

Da eine finalisierte Rechnung rechtlich bindend und unveränderlich ist, läuft die Finalisierung zweistufig ab: Der KI-Assistent prüft, ob die Rechnung finalisierbar ist (gültige Zahlungsart, aktiver Kunde), und legt dann eine Freigabe-Aufgabe an. Ein Mensch prüft und genehmigt diese anschließend in der App-Inbox. Erst nach der manuellen Freigabe wird die Rechnung tatsächlich finalisiert.

Ein zweiter Aufruf für dieselbe Rechnung liefert die bereits vorhandene offene Aufgabe zurück, sofern die Rechnung seit der ersten Anfrage nicht verändert wurde; die Parameter der ursprünglichen Anfrage bleiben dabei bestehen. Wurde die Rechnung geändert, wird die alte Aufgabe verworfen und eine neue mit neuer `actionItemId` angelegt. Freigabe-Anfragen verfallen 72 Stunden nach der Erstellung und müssen dann erneut gestellt werden.

| Parameter   | Typ     | Pflicht | Beschreibung                                                                                                |
| ----------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| `invoiceId` | string  | ja      | UUID der Rechnung.                                                                                          |
| `dueDate`   | string  |         | Fälligkeitsdatum im Format `YYYY-MM-DD`. Ohne Angabe gilt die Standard-Zahlungsfrist bei der Finalisierung. |
| `sendEmail` | boolean |         | Ob bei der Freigabe eine E-Mail an den Kunden gesendet wird (Standard: `true`).                             |

Gibt `{ actionItemId, status: "pending_approval", invoiceId, summary, hint }` zurück, wobei `summary` Gesamtbeträge (Netto, Steuer, Brutto in Cent), Anzahl Positionen und Kundendaten enthält, und `hint` den Hinweis auf die notwendige manuelle Freigabe in der Inbox. Mit `pending_action_status` (Parameter `actionItemId`) kannst du den Freigabestatus und das Ablaufdatum abfragen. Berechtigung: `invoice:finalize`.

***

#### `pending_action_status`

Fragt den Status einer Freigabe-Aufgabe ab, zum Beispiel einer per `invoice_finalize` angelegten Rechnungsfreigabe.

| Parameter      | Typ    | Pflicht | Beschreibung                                      |
| -------------- | ------ | ------- | ------------------------------------------------- |
| `actionItemId` | string | ja      | UUID der Freigabe-Aufgabe aus `invoice_finalize`. |

Gibt `{ id, type, status, createdAt, resolvedAt, expiresAt, invoiceId, executions }` zurück. `expiresAt` ist der Zeitpunkt (UTC), zu dem die Freigabe-Anfrage abläuft; danach muss sie per `invoice_finalize` neu angefordert werden.

Mögliche Werte für `status`: `open`, `snoozed`, `in_progress`, `resolved`, `dismissed`.

Jeder Eintrag in `executions` enthält `actionKey` (z. B. `approve` oder `reject`), `resultStatus` (`success` oder `failure`), `resultMessage` und `executedAt`. Bei erfolgreicher Freigabe steht in `resultMessage` die vergebene Belegnummer. Schlägt die Freigabe fehl, weil die Rechnung nach der Anfrage geändert wurde oder die Anfrage abgelaufen ist, enthält `resultMessage` den Grund; die Rechnung bleibt in dem Fall unfinalisiert. Berechtigung: `action-item:read`.

***

## Rechnung mit KI erstellen und finalisieren: ein typischer Ablauf

So erstellst du mit einem KI-Assistenten eine vollständige Rechnung und sendest sie zur Freigabe:

<Steps>
  <Step title="Rechnung anlegen">
    Lege eine neue Rechnung für den gewünschten Kunden an:

    ```json theme={null}
    {
      "tool": "invoice_create",
      "arguments": {
        "customerId": "<KUNDEN-ID>",
        "title": "Leistungen Juli 2026",
        "dueDate": "2026-08-15"
      }
    }
    ```

    Die Antwort enthält die `invoiceId`, die du für alle weiteren Aufrufe benötigst.
  </Step>

  <Step title="Positionen hinzufügen">
    Füge Positionen aus dem Katalog oder frei definierte Positionen hinzu:

    ```json theme={null}
    {
      "tool": "invoice_add_product_position",
      "arguments": {
        "invoiceId": "<RECHNUNGS-ID>",
        "productId": "<PRODUKT-ID>",
        "pricePlanId": "<PREISPLAN-ID>",
        "quantity": 5
      }
    }
    ```

    Beachte: `unitPrice` kommt immer aus dem Preisplan oder einem expliziten Override, nie aus einer Schätzung.
  </Step>

  <Step title="Rechnung prüfen">
    Lies die aktuelle Rechnung mit allen Positionen und Summen, bevor du die Finalisierung anforderst:

    ```json theme={null}
    {
      "tool": "invoice_get",
      "arguments": { "invoiceId": "<RECHNUNGS-ID>" }
    }
    ```
  </Step>

  <Step title="Freigabe anfordern">
    Erstelle die Freigabe-Aufgabe. Die Rechnung wird **noch nicht** finalisiert:

    ```json theme={null}
    {
      "tool": "invoice_finalize",
      "arguments": {
        "invoiceId": "<RECHNUNGS-ID>",
        "sendEmail": true
      }
    }
    ```

    Die Antwort enthält `actionItemId`, `status: "pending_approval"`, eine Zusammenfassung der Rechnung und einen `hint`-Text. Informiere die Nutzerin oder den Nutzer, dass die Freigabe in der App-Inbox wartet.
  </Step>

  <Step title="Mensch prüft und gibt frei">
    Ein Mitarbeiter öffnet die Inbox, prüft die Rechnung und gibt sie frei oder lehnt sie ab. Dieser Schritt findet ausschließlich in der App statt und kann nicht durch den KI-Assistenten übersprungen werden.
  </Step>

  <Step title="Status abfragen">
    Frage den Freigabestatus ab, bis die Aufgabe bearbeitet wurde:

    ```json theme={null}
    {
      "tool": "pending_action_status",
      "arguments": { "actionItemId": "<AUFGABEN-ID>" }
    }
    ```

    Bei `status: "resolved"` und `executions[0].resultStatus: "success"` ist die Rechnung finalisiert; `resultMessage` enthält die Belegnummer. Bei `resultStatus: "failure"` wurde die Rechnung nicht finalisiert (z. B. weil sie nach der Anfrage geändert wurde); erstelle in dem Fall eine neue Freigabe-Anfrage.
  </Step>
</Steps>

<Warning>
  Eine genehmigte Rechnung ist rechtlich bindend und unveränderlich. Die manuelle Freigabe kann nicht aus dem MCP-Server heraus umgangen werden.
</Warning>

***

## Audit

Jeder Tool-Aufruf über den MCP-Server wird automatisch protokolliert: Nutzerkonto, Organisation, Tool-Name, Parameter (ohne sensible Inhalte), Ergebnis und Ausführungsdauer. Der Audit-Log ist unveränderlich und dient der Nachvollziehbarkeit für Compliance-Anforderungen. Auch fehlgeschlagene oder abgelehnte Aufrufe werden erfasst.

***

### DATEV

#### `datev_export_status`

Zeigt, ob ein DATEV-Cloud-Konto verbunden ist, und liefert für jeden Monat eine Zusammenfassung der exportierten und offenen Buchungen sowie den letzten Export-Status.

| Parameter    | Typ     | Pflicht | Beschreibung                                                           |
| ------------ | ------- | ------- | ---------------------------------------------------------------------- |
| `monthsBack` | integer |         | Anzahl Monate rückwärts für die Übersicht (1 bis 36, Standard 12)      |
| `yearMonth`  | string  |         | Monat `YYYY-MM`, für den die einzelnen Export-Läufe aufgelistet werden |

***

#### `datev_cloud_export_start`

Startet einen DATEV-Cloud-Export für einen oder mehrere Monate. Dieses Tool verändert Daten. Ohne `confirm: true` kommt eine Rückfrage zur Bestätigung zurück, es wird nichts ausgeführt.

| Parameter    | Typ     | Pflicht | Beschreibung                                                   |
| ------------ | ------- | ------- | -------------------------------------------------------------- |
| `yearMonth`  | string  |         | Einzelner Monat `YYYY-MM`                                      |
| `yearMonths` | array   |         | Liste von Monaten `YYYY-MM` (alternativ zu `yearMonth`)        |
| `confirm`    | boolean |         | Muss `true` sein, um den Export tatsächlich zu starten         |
| `sendAgain`  | boolean |         | Auch erneut senden, wenn bereits exportiert (Standard `false`) |

***

### Produktkatalog

#### `product_tree`

Liefert den Produktkatalog als Kategorie-Baum. Jeder Knoten enthält `id`, `name`, `path`, `count` (Anzahl Produkte) und `children`. Mit `includeProducts: true` zusätzlich eine flache Produktliste.

| Parameter         | Typ     | Pflicht | Beschreibung                                                        |
| ----------------- | ------- | ------- | ------------------------------------------------------------------- |
| `includeProducts` | boolean |         | Wenn `true`, zusätzlich eine flache Produktliste (Standard `false`) |
| `locale`          | string  |         | Sprache für Produkt- und Einheitennamen, z. B. `"de"` oder `"en"`   |

***

#### `product_search`

Sucht Produkte im Katalog. Teiltreffer auf Produktname, internen Namen und Produktnummer.

| Parameter         | Typ     | Pflicht | Beschreibung                                         |
| ----------------- | ------- | ------- | ---------------------------------------------------- |
| `query`           | string  |         | Suchbegriff                                          |
| `familyId`        | string  |         | UUID einer Produktkategorie                          |
| `includeArchived` | boolean |         | Archivierte Produkte einschließen (Standard `false`) |
| `limit`           | integer |         | Maximale Trefferzahl (max. 50, Standard 20)          |
| `offset`          | integer |         | Offset für die Paginierung (Standard 0)              |
| `locale`          | string  |         | Sprache für Namen, z. B. `"de"` oder `"en"`          |

***

#### `product_prices`

Liefert alle Preispläne eines Produkts in vollständiger Form. Die zurückgegebene Preisplan-ID ist genau die `pricePlan`-Referenz, die ein Angebots-Produkteintrag (`subscriptionProposal`) benötigt. Alle Beträge sind ganzzahlige Cent.

| Parameter   | Typ    | Pflicht | Beschreibung      |
| ----------- | ------ | ------- | ----------------- |
| `productId` | string | ja      | UUID des Produkts |

Je Preisplan wird zurückgegeben: `id`, `name`, `internalName`, `status`, `currency`, `chargeType`, `billingInterval`, `billingFrequency`, `payInAdvance`, `proRata`, `freeUnits`, `minimumCommitment` und ein `price`-Block mit den Beträgen in Cent.

***

### Angebote

Die Angebots-Tools unterscheiden sich in Lese- und Schreiboperationen. Lesende Tools (`offer_get`, `offer_block_types`) benötigen `offer:read`. Schreibende Tools (`offer_create`, `offer_update`, `offer_add_block`, `offer_update_block`, `offer_remove_block`, `offer_upload_image`) benötigen `offer:write`. Angenommene oder archivierte Angebote sind unveränderlich.

#### `offer_block_types`

Listet alle unterstützten Block-Typen mit Metadaten auf (ob der Block atomar ist, ob er in PDF und Dokument erlaubt ist, und welche Attribute er akzeptiert). Mit optionalem `type` kommt zusätzlich ein konkretes Beispiel-Node zurück, das direkt an `offer_add_block` übergeben werden kann.

| Parameter | Typ    | Pflicht | Beschreibung                                           |
| --------- | ------ | ------- | ------------------------------------------------------ |
| `type`    | string |         | Block-Typ, z. B. `"heading"`, `"subscriptionProposal"` |

***

#### `offer_get`

Liest den Zustand eines Angebots effizient: Einstellungen (ID, Nummer, Name, Status, Anzeigesprache, Kunde, Gültigkeitsdatum, Annahme-Modus, Variablen) und eine kompakte Abschnitts- und Block-Übersicht. Mit `sectionIndex` (und optional `blockIndex`) wird zusätzlich der rohe JSON-Body genau dieses Abschnitts oder Blocks zurückgegeben.

| Parameter      | Typ     | Pflicht | Beschreibung                                     |
| -------------- | ------- | ------- | ------------------------------------------------ |
| `offerId`      | string  | ja      | ULID des Angebots                                |
| `sectionIndex` | integer |         | 0-basierter Abschnitts-Index für den rohen Body  |
| `blockIndex`   | integer |         | 0-basierter Block-Index innerhalb des Abschnitts |

***

#### `offer_create`

Legt ein neues Angebot an. Alle Parameter sind optional: ein Angebot ohne Kunde ist ausdrücklich erlaubt.

| Parameter        | Typ    | Pflicht | Beschreibung                                    |
| ---------------- | ------ | ------- | ----------------------------------------------- |
| `customerId`     | string |         | UUID des Kunden                                 |
| `name`           | string |         | Angebotstitel                                   |
| `locale`         | string |         | Anzeigesprache, z. B. `"de"`, `"en"`, `"de-DE"` |
| `validUntil`     | string |         | Gültigkeitsdatum ISO-8601, z. B. `2026-12-31`   |
| `salesChannelId` | string |         | UUID oder technischer Name des Vertriebskanals  |
| `templateId`     | string |         | UUID einer Angebotsvorlage                      |

***

#### `offer_update`

Aktualisiert die Einstellungen eines Angebots. Nur mitgesendete Felder werden geändert; fehlende Felder bleiben unverändert.

| Parameter         | Typ    | Pflicht | Beschreibung                                            |
| ----------------- | ------ | ------- | ------------------------------------------------------- |
| `offerId`         | string | ja      | ULID des Angebots                                       |
| `name`            | string |         | Neuer Angebotstitel                                     |
| `locale`          | string |         | Neue Anzeigesprache                                     |
| `validUntil`      | string |         | Neues Gültigkeitsdatum (leerer String löscht das Datum) |
| `acceptanceMode`  | string |         | `click`, `esignature` oder `print`                      |
| `customVariables` | object |         | Eigene Variablen als flaches Objekt                     |

***

#### `offer_add_block`

Fügt einen Block in einen Angebots-Abschnitt ein. Den gültigen Block-Node erhältst du über `offer_block_types`. Hat das Angebot noch keine Abschnitte, wird automatisch einer angelegt.

| Parameter      | Typ     | Pflicht | Beschreibung                                              |
| -------------- | ------- | ------- | --------------------------------------------------------- |
| `offerId`      | string  | ja      | ULID des Angebots                                         |
| `block`        | object  | ja      | Tiptap-Node mit `type` und optional `attrs`/`content`     |
| `sectionIndex` | integer |         | 0-basierter Abschnitts-Index (Standard 0)                 |
| `position`     | integer |         | Einfüge-Position im Abschnitt (Standard: am Ende anfügen) |

***

#### `offer_update_block`

Ersetzt einen bestehenden Block vollständig durch den übergebenen Tiptap-Node.

| Parameter      | Typ     | Pflicht | Beschreibung                 |
| -------------- | ------- | ------- | ---------------------------- |
| `offerId`      | string  | ja      | ULID des Angebots            |
| `sectionIndex` | integer | ja      | 0-basierter Abschnitts-Index |
| `blockIndex`   | integer | ja      | 0-basierter Block-Index      |
| `block`        | object  | ja      | Neuer Tiptap-Node            |

***

#### `offer_remove_block`

Entfernt einen Block aus einem Abschnitt; die nachfolgenden Blöcke rücken auf.

| Parameter      | Typ     | Pflicht | Beschreibung                 |
| -------------- | ------- | ------- | ---------------------------- |
| `offerId`      | string  | ja      | ULID des Angebots            |
| `sectionIndex` | integer | ja      | 0-basierter Abschnitts-Index |
| `blockIndex`   | integer | ja      | 0-basierter Block-Index      |

***

#### `offer_upload_image`

Lädt ein Bild für die Verwendung in Angeboten hoch. Erlaubt sind PNG, JPEG, WebP und GIF (max. 5 MB, dekodiert). Der MIME-Typ wird aus den Bilddaten erkannt; die Dateiendung wird entsprechend erzwungen. SVG wird abgelehnt. Die Antwort enthält `mediaId`, `url`, `filename` und einen fertigen `imageBlock`, der direkt an `offer_add_block` übergeben werden kann.

| Parameter  | Typ    | Pflicht | Beschreibung                                    |
| ---------- | ------ | ------- | ----------------------------------------------- |
| `filename` | string | ja      | Dateiname des Bilds                             |
| `data`     | string | ja      | Base64-kodierte Bilddaten (max. 5 MB dekodiert) |
| `altText`  | string |         | Alternativtext für das Bild                     |
| `title`    | string |         | Titel des Bilds                                 |

***

## Angebote mit KI erstellen: ein typischer Ablauf

So erstellst du mit einem KI-Assistenten ein vollständiges Angebot:

<Steps>
  <Step title="Angebot anlegen">
    Lege ein leeres Angebot für den gewünschten Kunden an:

    ```json theme={null}
    {
      "tool": "offer_create",
      "arguments": {
        "customerId": "<KUNDEN-ID>",
        "name": "Software-Paket 2026",
        "locale": "de",
        "validUntil": "2026-12-31"
      }
    }
    ```

    Die Antwort enthält die `offerId`, die du für alle weiteren Aufrufe benötigst.
  </Step>

  <Step title="Passende Produkte finden">
    Suche im Produktkatalog nach dem gewünschten Produkt:

    ```json theme={null}
    {
      "tool": "product_search",
      "arguments": {
        "query": "Pro Plan",
        "locale": "de"
      }
    }
    ```
  </Step>

  <Step title="Preisplan ermitteln">
    Lade die Preispläne des gefundenen Produkts:

    ```json theme={null}
    {
      "tool": "product_prices",
      "arguments": {
        "productId": "<PRODUKT-ID>"
      }
    }
    ```

    Die `id` des gewünschten Plans verwendest du im nächsten Schritt als `pricePlan`.
  </Step>

  <Step title="Produktblock hinzufügen">
    Füge einen `subscriptionProposal`-Block mit Produkt und Preisplan ein:

    ```json theme={null}
    {
      "tool": "offer_add_block",
      "arguments": {
        "offerId": "<ANGEBOTS-ID>",
        "block": {
          "type": "subscriptionProposal",
          "attrs": {
            "currency": "EUR",
            "products": [
              {
                "product": "<PRODUKT-ID>",
                "pricePlan": "<PREISPLAN-ID>",
                "quantity": 1
              }
            ],
            "terms": [
              { "contractPeriod": "12M", "cancellationPeriod": "3M" }
            ]
          }
        }
      }
    }
    ```
  </Step>

  <Step title="Textinhalte ergänzen">
    Füge Überschriften, Absätze oder Tabellen hinzu. Welche Block-Typen zur Verfügung stehen und wie ein gültiger Node aussieht, liefert `offer_block_types`.
  </Step>

  <Step title="Bild hochladen und einfügen">
    Lade ein Bild hoch und füge es direkt ein. Das Tool gibt einen fertigen `imageBlock` zurück:

    ```json theme={null}
    {
      "tool": "offer_upload_image",
      "arguments": {
        "filename": "logo.png",
        "data": "<BASE64-DATEN>",
        "altText": "Unternehmenslogo"
      }
    }
    ```

    Den zurückgegebenen `imageBlock` übergibst du als `block` an `offer_add_block`.
  </Step>
</Steps>

<Info>
  Alle Angebots-Tools respektieren den Lebenszyklus: ein angenommenes oder archiviertes Angebot kann nicht mehr verändert werden. Prüfe den Status vorab mit `offer_get`.
</Info>
