Das ist eine für den Ausdruck optimierte Ansicht des gesamten Kapitels inkl. Unterseiten. Druckvorgang starten.

Zur Standardansicht zurückkehren.

MCP-Server

Die DataBridge unterstützt das Model Context Protocol: ein eingebauter, read-only Server für Jobs, Mappings, Service-Bus-Queues und echte Connector-Schemas für KI-Assistenten wie Claude Code, GitHub Copilot oder Microsoft 365 Copilot.

Die dsyr.DataBridge unterstützt das Model Context Protocol (MCP) — den offenen Standard, über den KI-Assistenten strukturiert auf externe Systeme zugreifen. Ein eingebauter, read-only MCP-Server macht dieselben Daten, die auch über die HTTP-APIs (GetJobByRowKey, GetMappingByRowKey) verfügbar sind, sowie die Service-Bus-Queue-Diagnose zusätzlich als MCP-Tools ansprechbar — sodass ein KI-Assistent (Claude Code, GitHub Copilot, Microsoft 365 Copilot, …) direkt gegen eine laufende DataBridge-Instanz Fragen stellen kann, statt Tabellen manuell in Azure Storage Explorer oder per REST-Aufruf durchzusehen.

Auf dieser Seite

  • Funktionsweise — Endpunkt, Transport, Authentifizierung und wie die Function intern aufgebaut ist.
  • Operationen — die zehn verfügbaren Tools im Detail: was jedes einzelne liest, und was es nicht liest.
  • Nutzung — Einrichtung mit Claude Code, GitHub Copilot (VS Code) und Microsoft 365 Copilot.

Kurzüberblick

FunctionMcp (DataBridge/Functions/McpApi.cs)
RoutePOST /api/mcp (JSON-RPC-Austausch über Streamable HTTP)
Auth-LevelFunction (Function Key als ?code=-Query-Parameter)
Tools10 — Jobs & Mappings, Service-Bus-Diagnose, Connector-Schema (siehe Operationen)
SchreibzugriffKeiner — alle Tools sind read-only, siehe Operationen

Der Server ist damit direkt an einer laufenden Function App (lokal oder in Azure) ansprechbar, ohne eigenen Prozess oder zusätzliche Infrastruktur.

1 - Funktionsweise

Wie der MCP-Server technisch aufgebaut ist: Endpunkt, Transport, Authentifizierung, interner Aufbau und eine bekannte Einschränkung.

Endpunkt

FunctionMcp (DataBridge/Functions/McpApi.cs)
RoutePOST /api/mcp (JSON-RPC-Austausch); GET /api/mcp liefert bewusst 405 — siehe Bekannte Einschränkung
Auth-LevelFunction (Function Key als ?code=-Query-Parameter)
ProtokollMCP über Streamable HTTP, stateless — jede POST-Anfrage ist ein eigenständiger JSON-RPC-Austausch, es gibt keine langlebige Server-Session

Der Server ist damit direkt an einer laufenden Function App (lokal oder in Azure) ansprechbar, ohne eigenen Prozess oder zusätzliche Infrastruktur. Da die Authentifizierung über den normalen Azure-Functions-Mechanismus läuft (Function Key), lässt sich der Server ohne zusätzliche Identity-Provider-Konfiguration einrichten — der Key kann als Query-Parameter oder als Header übergeben werden, je nachdem, was der jeweilige Client unterstützt (siehe Nutzung).


Interner Aufbau

Der Mcp-Function-Trigger baut pro HTTP-Anfrage einen eigenen StreamableHttpServerTransport auf und delegiert die eigentliche Tool-Logik an den IMcpToolService (DataBridge.Core/Services/Mcp/), der wiederum dieselben Service-Interfaces (IJobService, IMappingService, IServiceBusService, IConnectionService) aufruft wie die klassischen HTTP-Funktionen — es gibt keine internen Selbstaufrufe zwischen den Functions. McpApi.cs bleibt dadurch auf die reine Protokoll-Anbindung beschränkt (Tool-Registrierung, Transport), analog zum dünnen Zuschnitt von JobApi/MappingApi. Die eigentliche Tool-Logik (Init-Guard, Job-/Mapping-Lesezugriff, Service-Bus-Delegation, Connector-Auflösung) liegt in IMcpToolService, testbar unabhängig vom Protokoll-Layer. Details zur Service-Schicht: Architektur.

Die [Description]-Attribute und Standardwerte für die Tool-Parameter liegen bewusst auf den Methoden von IMcpToolService selbst (reines System.ComponentModel) — McpApi.cs bindet die Tools direkt auf diese Service-Methoden, ohne zusätzliche Wrapper-Methoden. Der Transport-Teil (ModelContextProtocol.AspNetCore) bleibt im Host; DataBridge.Core referenziert nur ModelContextProtocol.Core, und das ausschließlich für die McpException (siehe unten).

Fehlerweitergabe an den Client

Der MCP-C#-SDK ersetzt jede unbehandelte Exception eines Tools durch den generischen Text „An error occurred invoking ‘…’.“ und hängt nur bei einer McpException deren Message an. Alle anderen Exceptions landen zwar im Log, der Client sieht aber nicht, warum der Aufruf fehlgeschlagen ist. Ein CallToolFilter hilft hier nicht, weil der Catch im SDK innerhalb des Tool-Aufrufs sitzt. McpToolService wirft deshalb bei den beiden Connector-Tools (list_connector_tables, get_connector_table_schema) Connector-Fehler und unbekannte Connection-Namen als McpException weiter (SurfaceConnectorErrors), sodass z. B. die angefragte $metadata-URL und der HTTP-Status beim Client ankommen. Der Test McpApiTests.ToolsCall_ListConnectorTables_ConnectorFails_ErrorResultCarriesTheCause sichert dieses Verhalten durch die echte SDK-Pipeline ab.


Bekannte Einschränkung: GET auf /api/mcp

Manche MCP-Clients (typischerweise Remote-Clients, die den Endpunkt über eine echte Azure-Deployment-URL statt lokal ansprechen) versuchen nach dem initialize-POST zusätzlich einen GET auf denselben Endpunkt, um einen optionalen Server-zu-Client-SSE-Stream zu öffnen (Teil der Streamable-HTTP-Spezifikation).

Da dieser Server stateless ist (keine Session, kein Server-Push), unterstützt er diesen Stream bewusst nicht. Die Route ist deshalb zusätzlich zum POST-Handler mit einem eigenen GET-Handler registriert, der spezifikationskonform mit 405 Method Not Allowed (statt einem nicht aussagekräftigen, von Azure Functions automatisch erzeugten 404) antwortet:

[Function("McpGet")]
public IActionResult HandleMcpGetRequest(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "mcp")] HttpRequest req)
{
    req.HttpContext.Response.Headers.Append("Allow", "POST");
    return new StatusCodeResult(StatusCodes.Status405MethodNotAllowed);
}

Manche Clients interpretieren 405 als „kein SSE-Support, aber Server ist erreichbar" und fallen automatisch auf reines POST zurück; ein 404 liest sich dagegen wie „Endpunkt existiert nicht" und lässt die Verbindung fehlschlagen. Ohne diesen Handler tritt das Problem nur bei Remote-/Azure-Deployments zuverlässig auf — lokal (Claude Code über .mcp.json) sendet der Client i. d. R. keinen GET-Probe-Request, daher blieb es dort unbemerkt.

2 - Operationen

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

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.


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).

ToolLiestLiest nicht
list_jobsAlle konfigurierten Jobs: Quelle/Ziel-Connection, Richtung, Priorität, Zeitplan, Mapping-Name, aus der jobs-TabelleKeine Connection-Credentials (nur der Name der Connection, nicht ihre Zugangsdaten)
get_jobEin einzelner Job anhand seines RowKey (Job-Name) — dieselben Felder wie list_jobsdito
list_mappingsAlle Feld-Mappings zwischen Quell- und Zielsystemen aus der mappings-TabelleKeine tatsächlichen Datensatz-Inhalte — nur die Mapping-Konfiguration (welches Feld auf welches Feld abgebildet wird), nicht produzierte/synchronisierte Daten
get_mappingEin 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.

ToolLiestLiest nicht
list_queue_statusesLaufzeit-Status (aktive/dead-letter/geplante Nachrichtenanzahl) aller Export-QueuesKeine einzelnen Nachrichten — nur Zähler
get_queue_statusLaufzeit-Status einer einzelnen Queue für eine Job-Prioritätdito
peek_queue_messagesSequenznummer, JobName, SourceKey, Enqueue-Zeitpunkt, Zustellversuche und BodySize (Größe des Nachrichteninhalts in Bytes) je NachrichtDen Nachrichteninhalt selbst (Body) nicht — siehe Kasten unten
peek_dead_letter_queue_messagesZusätzlich zu den obigen Feldern: DeadLetterReason, DeadLetterErrorDescription, ErrorMessage, ErrorTimeStamp, ErrorStackTrace, OperationIddito — auch hier nur BodySize, nie Body

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

ToolLiestLiest nicht
list_connector_tablesTabellen-/Entitätsnamen einer Connection direkt aus den Metadaten des Zielsystems — zum Abgleich mit den in Jobs/Mappings konfigurierten NamenKeine tatsächlichen Datensätze aus der Tabelle — nur ihren Namen
get_connector_table_schemaSpalten-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 verlassenKeine tatsächlichen Datensätze/Zeilenwerte — nur die Spalten-Struktur

Beide Tools nehmen einen connectionName entgegen (den RowKey aus der connections-Tabelle — siehe 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).

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.

ConnectorMetadatenquelleapiPath
MSSQLINFORMATION_SCHEMASchema, z. B. dbo
PostgreSQLINFORMATION_SCHEMASchema, z. B. public
MySQLINFORMATION_SCHEMADatenbank
OracleUSER_TAB_COLUMNS/USER_CONSTRAINTS, mit apiPath ALL_*-ViewsOwner/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-TypMSSQLPostgreSQLMySQLOracle
Guiduniqueidentifieruuid——
Booleanbitbooleantinyint(1)/boolean, bit(1)boolean (23ai)
Bytetinyint—tinyint unsigned—
Int16smallintsmallinttinyint, smallint, yearnumber(1–4,0)
Int32intintegerint, mediumint, smallint unsignednumber(5–9,0)
Int64bigintbigintbigint, int unsigned, bit(n>1)number(10–18,0)
Decimaldecimal, numeric, moneynumericdecimal, bigint unsignednumber mit Nachkommastellen, ab 19 Stellen oder ohne Angabe
Singlereal, float(1–24)realfloatbinary_float
Doublefloatdouble precisiondoublebinary_double, float
DateOnlydatedatedate—
DateTimedatetime, datetime2, smalldatetimetimestampdatetime, timestampdate, timestamp, timestamp with local time zone
DateTimeOffsetdatetimeoffsettimestamp with time zone—timestamp with time zone
TimeSpantimetime, intervaltimeinterval
Byte[]binary, varbinary, image, timestamp/rowversionbyteabinary, varbinary, blob-Typenblob, raw
StringZeichentypen, xmlZeichentypen, json/jsonbZeichentypen, jsonZeichentypen, 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.

3 - Nutzung

Den MCP-Server mit Claude Code, GitHub Copilot (VS Code) oder Microsoft 365 Copilot verbinden.

Der Server spricht Standard-MCP über Streamable HTTP — grundsätzlich verbindet sich jeder MCP-fähige Client über dieselbe URL (.../api/mcp?code=<Function-Key>). Die folgenden Abschnitte zeigen die konkrete Einrichtung für die gängigsten Clients.


Claude Code

Dieses Repository bringt bereits eine passende .mcp.json mit:

{
  "mcpServers": {
    "databridge": {
      "type": "http",
      "url": "${DATABRIDGE_MCP_URL:-http://localhost:7062}/api/mcp?code=${DATABRIDGE_FUNCTION_KEY:-local}"
    }
  }
}

Lokal (Core Tools / Debugger): Der lokale Function-Host prüft den Auth-Level Function nicht — der Platzhalterwert local in der URL reicht aus. Voraussetzung ist nur, dass die Function App tatsächlich auf Port 7062 läuft (nicht der func-Standard 7071!):

  • Visual Studio / dotnet run: Startprofil DataBridge aus src/DataBridge/Properties/launchSettings.json (setzt bereits --port 7062).
  • VS Code: Task func: host start aus .vscode/tasks.json (ebenfalls fest auf --port 7062).
  • Rider: die in der IDE angezeigte „Azure Functions Project"-Run-Configuration DataBridge — im Feld „Function host arguments" muss host start --pause-on-error --port 7062 stehen.

Danach lädt Claude Code den databridge-Server automatisch; die Tools erscheinen mit dem Präfix mcp__databridge__....

Gegen eine Azure-Deployment-Instanz: Die URL enthält zwei Umgebungsvariablen, die ohne eigene Werte auf den lokalen Host zeigen. Für ein Deployment setzt man sie in der nicht eingecheckten .claude/settings.local.json des Repositorys:

{
  "env": {
    "DATABRIDGE_MCP_URL": "https://<function-app-name>.azurewebsites.net",
    "DATABRIDGE_FUNCTION_KEY": "<Function Key>"
  }
}

Den Function Key gibt es im Azure Portal → Function App → Funktionen → Mcp → Funktionsschlüssel. Niemals einen echten Key in .mcp.json committen. Geänderte Werte greifen erst beim Start einer neuen Claude-Code-Session.

Eine eigene Datei wie .mcp.local.json wertet Claude Code nicht aus. Wer zusätzlich einen persönlichen Server-Eintrag braucht (etwa für eine zweite Instanz), legt ihn über claude mcp add --scope local --transport http <name> "<url>" an.

Beispiel-Interaktion

Ein typischer Anwendungsfall: prüfen, ob ein Mapping-Feld wirklich im Zielsystem existiert.

Prompt: „Prüfe, ob das Mapping BCAccounts gültige Zielfelder hat — vergleiche mit den echten Metadaten der Connection."

Claude ruft dafür typischerweise nacheinander auf:

  1. get_mapping mit rowKey: "BCAccounts" → liefert die konfigurierten Feld-Mappings inkl. TargetTable/TargetPath.
  2. get_connector_table_schema mit der targetConnection aus dem zugehörigen Job sowie tableName/apiPath aus dem Mapping → liefert das tatsächliche Schema.
  3. Vergleicht beide Ergebnisse und meldet fehlende Felder oder Typ-Abweichungen.

Damit lassen sich Mapping-Fehler bereits vor dem ersten produktiven Lauf eines Jobs erkennen, statt sie erst über eine fehlgeschlagene Dead-Letter-Nachricht zu bemerken (siehe peek_dead_letter_queue_messages, Operationen).


GitHub Copilot (VS Code)

VS Code (ab Version 1.99, Agent-/Chat-Modus) unterstützt MCP-Server nativ über eine mcp.json — entweder projektweit unter .vscode/mcp.json oder als Nutzer-Konfiguration über die Command Palette (MCP: Open User Configuration). Anders als Claude Codes .mcp.json liegt der Server hier unter dem Schlüssel servers, nicht mcpServers:

{
  "servers": {
    "databridge": {
      "type": "http",
      "url": "https://<function-app-name>.azurewebsites.net/api/mcp?code=${input:databridgeFunctionKey}"
    }
  },
  "inputs": [
    {
      "id": "databridgeFunctionKey",
      "type": "promptString",
      "description": "DataBridge MCP Function Key",
      "password": true
    }
  ]
}

Der inputs-Block sorgt dafür, dass VS Code beim ersten Verbindungsversuch einmalig nach dem Function Key fragt (maskierte Eingabe) statt ihn im Klartext in der Datei zu speichern — dieselbe Absicht wie die ${DATABRIDGE_FUNCTION_KEY:-local}-Umgebungsvariable im Claude-Code-Beispiel oben, nur über den VS-Code-eigenen Mechanismus. Lokal (Function-Host ohne Auth-Prüfung) reicht auch hier ein beliebiger Platzhalterwert. Details zu Input-Variablen: VS Code MCP-Konfigurationsreferenz.

Nach dem Speichern zeigt VS Code den Server im Chat-/Agent-Modus an; die Tools lassen sich dort wie jedes andere MCP-Tool auswählen und aufrufen.


Microsoft 365 Copilot

Microsoft 365 Copilot bindet externe MCP-Server nicht direkt per URL im Chat ein, sondern über Copilot Studio — dort lässt sich ein bestehender MCP-Server einem Agenten als Tool hinzufügen, und dieser Agent dann in Microsoft 365 Copilot veröffentlichen. Die Einrichtung erfolgt über den MCP-Onboarding-Wizard in Copilot Studio:

  1. Im Agenten zur Seite Tools wechseln → Add a tool → New tool → Model Context Protocol.
  2. Server-URL eintragen: https://<function-app-name>.azurewebsites.net/api/mcp.
  3. Als Authentifizierungsart API key wählen (nicht OAuth 2.0 — unser Server nutzt den Azure-Functions-eigenen Function-Key-Mechanismus, kein OAuth).
  4. Als Type Query wählen und als Parametername code eintragen — das entspricht genau dem ?code=-Query-Parameter, den auch Claude Code und VS Code verwenden.
  5. Create, dann den Function Key als API-Key-Wert für die Verbindung hinterlegen.

Copilot Studio unterstützt für MCP ausschließlich den Streamable-HTTP-Transport (SSE ist seit August 2025 deprecatet) — genau das Protokoll, das McpApi.cs implementiert, es sind also keine serverseitigen Anpassungen nötig.

Alternativ lässt sich der Server auch als benutzerdefinierter Connector über eine OpenAPI-Beschreibung in Power Apps einbinden (x-ms-agentic-protocol: mcp-streamable-1.0) — das ist der Weg, wenn mehr Kontrolle über die Connector-Konfiguration nötig ist als der Wizard bietet. Details dazu: Microsoft-Dokumentation zu MCP in Copilot Studio.