# Funktionsweise

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

---

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

---

## Endpunkt

| | |
|---|---|
| **Function** | `Mcp` (`DataBridge/Functions/McpApi.cs`) |
| **Route** | `POST /api/mcp` (JSON-RPC-Austausch); `GET /api/mcp` liefert bewusst `405` — siehe [Bekannte Einschränkung](#bekannte-einschränkung-get-auf-apimcp) |
| **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](../nutzung/)).

---

## Interner Aufbau

Der `Mcp`-Function-Trigger baut pro HTTP-Anfrage einen eigenen
[`StreamableHttpServerTransport`](https://github.com/modelcontextprotocol/csharp-sdk) 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](../../ueberblick/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:

```csharp
[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.
