Funktionsweise
Themen:
Endpunkt
| Function | Mcp (DataBridge/Functions/McpApi.cs) |
| Route | POST /api/mcp (JSON-RPC-Austausch); GET /api/mcp liefert bewusst 405 — siehe Bekannte Einschränkung |
| Auth-Level | Function (Function Key als ?code=-Query-Parameter) |
| Protokoll | MCP ü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.