# Operationen

> Die zehn MCP-Tools im Detail: was jedes einzelne liest — und was bewusst nicht zurückgegeben wird.

---

Site [llms.txt](/llms.txt)

---

Alle zehn Tools sind als `ReadOnly = true`, `Idempotent = true` und `Destructive = false` markiert
(MCP-Tool-Annotationen, die dem aufrufenden Client mitgeben, dass wiederholte Aufrufe risikolos
sind). Kein Tool schreibt, löscht oder sendet — weder in der `jobs`-/`mappings`-Tabelle, noch in
den Service-Bus-Queues, noch in einem Quell-/Zielsystem.

> **Grenze ist Metadaten vs. Nutzdaten, nicht nur lesend vs. schreibend**
>
> Auch innerhalb der reinen Lesezugriffe gibt es eine bewusste zweite Grenze: Betriebs-/
> Konfigurationsdaten (Job-Namen, Schemata, Fehlermeldungen, Zeitstempel) sind unkritisch und werden
> vollständig zurückgegeben. **Fachliche Nutz-/Kundendaten** — z. B. der Inhalt einer
> Service-Bus-Nachricht — werden dagegen **nie** zurückgegeben, selbst wenn das zugrunde liegende
> Tool ansonsten "nur liest". Details dazu unten bei den Peek-Tools.



---

## Jobs & Mappings (Table Storage)

Lesen ausschließlich aus den beiden Tabellen `jobs`/`mappings`, über dieselben In-Memory-Caches
wie `JobApi`/`MappingApi` (`IJobService.Jobs`/`IMappingService.Mappings` — reine `Query<T>()`-Reads,
kein `Upsert`/`Delete` im Codepfad).

| Tool | Liest | Liest **nicht** |
|---|---|---|
| `list_jobs` | Alle konfigurierten Jobs: Quelle/Ziel-Connection, Richtung, Priorität, Zeitplan, Mapping-Name, aus der `jobs`-Tabelle | Keine Connection-Credentials (nur der *Name* der Connection, nicht ihre Zugangsdaten) |
| `get_job` | Ein einzelner Job anhand seines `RowKey` (Job-Name) — dieselben Felder wie `list_jobs` | dito |
| `list_mappings` | Alle Feld-Mappings zwischen Quell- und Zielsystemen aus der `mappings`-Tabelle | Keine tatsächlichen Datensatz-Inhalte — nur die Mapping-*Konfiguration* (welches Feld auf welches Feld abgebildet wird), nicht produzierte/synchronisierte Daten |
| `get_mapping` | Ein einzelnes Mapping anhand seines Namens (der Mapping-Name, auf den ein Job verweist und unter dem `list_mappings` es auflistet) | dito |

**Warum keine Credentials möglich sind:** `IConnectionService.GetConnector(name)` liefert nur die
`IConnector`-Instanz zurück, nicht das zugrunde liegende `ConnectionSetting`-Objekt. Felder wie
`ClientSecret`, `Password` oder `ApiKeyValue` sind private Felder in den konkreten Connectoren und
nicht Teil des `IConnector`-Interfaces — es gibt schlicht keinen Rückgabewert, über den sie
herauskommen könnten.

---

## Service Bus (Diagnose)

Reine Peek-/Status-Operationen über `IServiceBusService`. Die beiden Peek-Tools erzeugen den
Empfänger ohne `PeekLock` und rufen ausschließlich `receiver.PeekMessagesAsync(...)` auf — es gibt
im Codepfad kein `ReceiveMessagesAsync`, `CompleteMessageAsync`, `DeadLetterMessageAsync` oder
`AbandonMessageAsync`. Auch `SendMessageAsync` (existiert auf `IServiceBusService` für
`GetChanges`) ist von keinem MCP-Tool aus erreichbar.

| Tool | Liest | Liest **nicht** |
|---|---|---|
| `list_queue_statuses` | Laufzeit-Status (aktive/dead-letter/geplante Nachrichtenanzahl) aller Export-Queues | Keine einzelnen Nachrichten — nur Zähler |
| `get_queue_status` | Laufzeit-Status einer einzelnen Queue für eine Job-Priorität | dito |
| `peek_queue_messages` | Sequenznummer, JobName, SourceKey, Enqueue-Zeitpunkt, Zustellversuche und **`BodySize`** (Größe des Nachrichteninhalts in Bytes) je Nachricht | **Den Nachrichteninhalt selbst (`Body`) nicht** — siehe Kasten unten |
| `peek_dead_letter_queue_messages` | Zusätzlich zu den obigen Feldern: DeadLetterReason, DeadLetterErrorDescription, ErrorMessage, ErrorTimeStamp, ErrorStackTrace, OperationId | dito — auch hier nur `BodySize`, nie `Body` |

> **Warum kein Nachrichteninhalt**
>
> Der Body einer Queue-Nachricht enthält die **fachlichen Nutzdaten** des jeweiligen Jobs — z. B.
> bei einem BC-Artikelstamm-Job Produktcode, Bezeichnung und Dimensionswerte, bei anderen Jobs unter
> Umständen Kundennamen, Adressen oder Preise. Das ist etwas grundlegend anderes als Betriebsmetadaten
> wie ein Fehlertext oder ein Zeitstempel. Beide Peek-Tools geben deshalb bewusst **nie** den
> Nachrichteninhalt zurück — nur dessen Größe in Bytes (`BodySize`), berechnet über
> `message.Body.ToMemory().Length`, ohne den Inhalt jemals zu deserialisieren oder als String zu
> lesen. Für die Fehleranalyse reichen die übrigen Felder (insbesondere bei
> `peek_dead_letter_queue_messages` die Fehlerdetails) in aller Regel aus.



---

## Connector-Metadaten (echtes Ziel-/Quellsystem-Schema)

| Tool | Liest | Liest **nicht** |
|---|---|---|
| `list_connector_tables` | Tabellen-/Entitätsnamen einer Connection **direkt aus den Metadaten des Zielsystems** — zum Abgleich mit den in Jobs/Mappings konfigurierten Namen | Keine tatsächlichen Datensätze aus der Tabelle — nur ihren Namen |
| `get_connector_table_schema` | Spalten-Metadaten (Typ, Nullable, maximale Länge, bei SQL-Dezimalspalten Precision/Scale, Primary Key) einer Tabelle/Entität — zum Validieren eines Mappings gegen das **tatsächliche** Schema, statt sich auf die Mapping-Konfiguration allein zu verlassen | Keine tatsächlichen Datensätze/Zeilenwerte — nur die Spalten-*Struktur* |

Beide Tools nehmen einen `connectionName` entgegen (den `RowKey` aus der `connections`-Tabelle —
siehe [Verbindungen](../../konfiguration/verbindungen/), z. B. `BC_Test` oder `CRM_Test`) sowie
einen `tableName`/Ressourcennamen. Für BC/NAV gibt es zusätzlich den `apiPath` (derselbe Wert wie
`TableMapping.SourcePath`/`TargetPath` im Mapping), da beide Connectoren ihr Schema über einen
versionierten OData-`$metadata`-Endpunkt beziehen. Bei BC ist er optional: leer bedeutet `v2.0`
(die Standard-API); Custom-APIs (z. B. `publisher/gruppe/v1.0`) brauchen ihren Pfad weiterhin.
Bei den SQL-Connectoren hat `apiPath` eine andere Bedeutung, siehe unten.

Diese beiden Tools sind die einzigen mit `OpenWorld = true` — sie greifen tatsächlich auf das
externe Quell-/Zielsystem der jeweiligen Connection zu, nicht nur auf die eigene
DataBridge-Infrastruktur. Ungültige Connection-Namen liefern eine klare Meldung
(„Connection '...' is not configured or not initialized") statt einer rohen
`KeyNotFoundException` — es lässt sich also nicht durch Ausprobieren beliebiger Namen etwas über
das System herausfinden, das über `list_jobs`/`get_job` nicht sowieso schon sichtbar wäre.
Schlägt der Zugriff auf das Zielsystem fehl (falscher `apiPath`, unbekannte Tabelle, Host nicht
erreichbar), enthält das Fehlerergebnis die Ursache, z. B. `GET …/api/nope/v1.0/$metadata returned
404 Not Found` — technisch als `McpException`, weil der MCP-SDK nur deren Message an den Client
durchreicht (siehe [Funktionsweise](../funktionsweise/#fehlerweitergabe-an-den-client)).

> **Connector-Unterstützung**
>
> `list_connector_tables`/`get_connector_table_schema` funktionieren für **alle Connectoren**:
> D365BC, D365NAV, D365CE (Dataverse), OpenAPI sowie die SQL-Connectoren **MSSQL, MySQL,
> Oracle und PostgreSQL**. Der Rückgabewert ist je Connector unterschiedlich vollständig, da
> nicht jedes System dieselbe Metadaten-Tiefe bietet:
> 
> | Connector | Typ | Nullable | Max. Länge | Precision/Scale | Primary Key |
> |---|:---:|:---:|:---:|:---:|:---:|
> | D365BC / D365NAV | ✅ | ✅ | ✅ (nur Strings) | ❌ | ✅ |
> | D365CE (Dataverse) | ✅ | ✅ | ✅ (nur Strings) | ❌ | ✅ |
> | OpenAPI | ✅ | ✅ | ✅ (falls `maxLength` im Schema) | ❌ | ❌ *(kein natives Konzept — immer `null`)* |
> | MSSQL / MySQL / PostgreSQL | ✅ | ✅ | ✅ (nur Strings) | ✅ (nur Dezimalspalten) | ✅ |
> | Oracle | ✅ | ✅ | ✅ (in Zeichen, `CHAR_LENGTH`) | ✅ (nur Dezimalspalten) | ✅ |



### SQL-Connectoren: Schema über `apiPath`

Bei den SQL-Connectoren gibt `apiPath` das **Schema** an, in dem die Tabelle liegt — bei
MySQL die **Datenbank**. Ohne `apiPath` wird das Standard-Schema der Verbindung verwendet
(MSSQL `SCHEMA_NAME()`, MySQL `DATABASE()`, PostgreSQL `current_schema()`, Oracle der
angemeldete Benutzer). Damit werden gleichnamige Tabellen in unterschiedlichen Schemas
(z. B. `dbo.Customers` und `archive.Customers`) nicht vermischt.

| Connector | Metadatenquelle | `apiPath` |
|---|---|---|
| MSSQL | `INFORMATION_SCHEMA` | Schema, z. B. `dbo` |
| PostgreSQL | `INFORMATION_SCHEMA` | Schema, z. B. `public` |
| MySQL | `INFORMATION_SCHEMA` | Datenbank |
| Oracle | `USER_TAB_COLUMNS`/`USER_CONSTRAINTS`, mit `apiPath` `ALL_*`-Views | Owner/Schema, z. B. `HR` |

Tabellen- und Schemaname werden groß-/kleinschreibungsunabhängig verglichen und als
Parameter an die Datenbank übergeben. Die Metadaten werden je Connector-Instanz
zwischengespeichert — Schemaänderungen an der Datenbank werden erst nach einem Neustart der
Function App sichtbar.

### SQL-Connectoren: Datentypen

Die Datentypen aus dem Datenbank-Katalog werden auf .NET-Typen abgebildet. Dabei werden
Precision und Scale sowie bei MySQL der vollständige Spaltentyp (`COLUMN_TYPE`) berücksichtigt.
Unbekannte Typen (z. B. `json`, `xml`, Array-Typen) erscheinen als `String`.

| .NET-Typ | MSSQL | PostgreSQL | MySQL | Oracle |
|---|---|---|---|---|
| `Guid` | `uniqueidentifier` | `uuid` | — | — |
| `Boolean` | `bit` | `boolean` | `tinyint(1)`/`boolean`, `bit(1)` | `boolean` (23ai) |
| `Byte` | `tinyint` | — | `tinyint unsigned` | — |
| `Int16` | `smallint` | `smallint` | `tinyint`, `smallint`, `year` | `number(1–4,0)` |
| `Int32` | `int` | `integer` | `int`, `mediumint`, `smallint unsigned` | `number(5–9,0)` |
| `Int64` | `bigint` | `bigint` | `bigint`, `int unsigned`, `bit(n>1)` | `number(10–18,0)` |
| `Decimal` | `decimal`, `numeric`, `money` | `numeric` | `decimal`, `bigint unsigned` | `number` mit Nachkommastellen, ab 19 Stellen oder ohne Angabe |
| `Single` | `real`, `float(1–24)` | `real` | `float` | `binary_float` |
| `Double` | `float` | `double precision` | `double` | `binary_double`, `float` |
| `DateOnly` | `date` | `date` | `date` | — |
| `DateTime` | `datetime`, `datetime2`, `smalldatetime` | `timestamp` | `datetime`, `timestamp` | `date`, `timestamp`, `timestamp with local time zone` |
| `DateTimeOffset` | `datetimeoffset` | `timestamp with time zone` | — | `timestamp with time zone` |
| `TimeSpan` | `time` | `time`, `interval` | `time` | `interval` |
| `Byte[]` | `binary`, `varbinary`, `image`, `timestamp`/`rowversion` | `bytea` | `binary`, `varbinary`, `blob`-Typen | `blob`, `raw` |
| `String` | Zeichentypen, `xml` | Zeichentypen, `json`/`jsonb` | Zeichentypen, `json` | Zeichentypen, `clob`, `json` |

Besonderheiten:

- **MSSQL** `timestamp` ist der alte Name von `rowversion`, eine binäre Zeilenversion und kein
  Datum — daher `Byte[]`.
- **MySQL** speichert `BOOLEAN` als `tinyint(1)`; erst der vollständige Spaltentyp macht daraus
  `Boolean`. Vorzeichenlose Ganzzahlen erhalten den nächstgrößeren Typ, damit der Wertebereich
  passt. `timestamp` hat keinen Offset und wird daher `DateTime`.
- **Oracle** kennt keine eigenen Ganzzahltypen; `NUMBER(p,0)` wird nach der Anzahl der Stellen
  auf `Int16`/`Int32`/`Int64` abgebildet. `DATE` enthält immer eine Uhrzeit und wird daher
  `DateTime`, nicht `DateOnly`.
- GUIDs, die als Text gespeichert sind (MySQL `char(36)`, Oracle `varchar2(36)`), erscheinen als
  `String`.

---

## Zusammenfassung: was über MCP grundsätzlich nicht möglich ist

- Keine Schreib-, Lösch- oder Sende-Operation auf `jobs`/`mappings`/Service-Bus-Queues oder einem
  Ziel-/Quellsystem — es gibt für keines der zehn Tools einen Codepfad zu `Upsert`, `Delete`,
  `SendMessageAsync`, `CompleteMessageAsync` o. Ä.
- Keine Connection-Credentials (Passwörter, Client-Secrets, API-Keys) — die sind strukturell nie
  Teil eines Rückgabewerts.
- Kein tatsächlicher Nachrichteninhalt aus einer Service-Bus-Queue — nur dessen Byte-Größe.
- Keine tatsächlichen Datensatz-Werte aus einem Ziel-/Quellsystem über die Connector-Tools — nur
  Tabellennamen und Spalten-*Struktur*, keine Zeilen.
