MCP-Server in C# bauen

MCP-Server in C# bauen
09/2026
Kauthar A. | Solution Engineer

TL;DR

Das Model Context Protocol (MCP) senkt den Integrationsaufwand zwischen KI-Anwendungen und internen Systemen von M×N auf M+N. Der Beitrag zeigt Schritt für Schritt, wie ein MCP-Server in C# entsteht: vom ersten Tool über das Testen mit dem MCP Inspector bis zum sicheren Betrieb. Entscheidend ist die Sicherheit: Least Privilege, keine Tools für freies SQL und Freigaben für schreibende Operationen. Der funktionierende Server ist schnell gebaut, der produktive braucht ein durchdachtes Berechtigungskonzept.

Wer ein internes System an einen KI-Assistenten anbinden will, kennt das Problem: Für Claude Desktop schreibt man eine Integration, für die eigene Anwendung eine zweite, für das nächste Tool eine dritte. Bei M Systemen und N Anwendungen landet man bei M×N Integrationen und pflegt sie alle.

Das Model Context Protocol (MCP) löst genau das. Es standardisiert die Schnittstelle zwischen KI-Anwendungen und externen Systemen, sodass aus M×N ein M+N wird: Jedes System bekommt einen Server, jede Anwendung einen Client, und beide Seiten sprechen dasselbe Protokoll.

Die Rollenverteilung

Drei Begriffe, die man auseinanderhalten sollte:

  • Host: die Anwendung, in der das Modell läuft (Claude Desktop, Claude Code, eure eigene App)
  • Client: die Protokollverbindung, die der Host pro Server aufbaut
  • Server: euer Code, der Fähigkeiten anbietet

Ein Server stellt drei Arten von Primitives bereit: Tools (Aktionen, die das Modell ausführen kann), Resources (Daten, die es lesen kann) und Prompts (vorbereitete Vorlagen). Für den Einstieg reichen Tools. Sie decken die meisten Anwendungsfälle ab.

Setup

Die Basis bildet das offizielle MCP-SDK für .NET, das sich per NuGet in ein C#-Projekt einbinden lässt.

1dotnet new console -n Devware.Mcp.Inventory
2cd Devware.Mcp.Inventory
3dotnet add package ModelContextProtocol --prerelease
4dotnet add package Microsoft.Extensions.Hosting

Die Program.cs bleibt erfreulich kurz:

1var builder = Host.CreateApplicationBuilder(args);
2
3// Wichtig: stdout gehört dem Protokoll. Logging muss nach stderr.
4builder.Logging.AddConsole(options =>
5    options.LogToStandardErrorThreshold = LogLevel.Trace);
6
7builder.Services
8    .AddMcpServer()
9    .WithStdioServerTransport()
10    .WithToolsFromAssembly();
11
12await builder.Build().RunAsync();

Der Kommentar ist kein Detail, sondern der Fehler, den jeder genau einmal macht. Beim stdio-Transport laufen die Protokollnachrichten über die Standardausgabe. Ein einziges Console.WriteLine zerstört den Nachrichtenstrom, und der Host meldet nur, dass die Verbindung abgebrochen ist. Logging gehört nach stderr.

Das erste Tool

Tools sind Methoden mit Attributen. Das Schema generiert das SDK aus der Signatur:

1[McpServerToolType]
2public sealed class InventoryTools
3{
4    [McpServerTool(Name = "get_stock_level")]
5    [Description("Liefert den aktuellen Lagerbestand zu einer Artikelnummer.")]
6    public static async Task<string> GetStockLevel(
7        InventoryDbContext db,
8        [Description("Artikelnummer im Format 'A-10432'")] string sku,
9        CancellationToken ct)
10    {
11        var item = await db.Items
12            .AsNoTracking()
13            .FirstOrDefaultAsync(i => i.Sku == sku, ct);
14
15        return item is null
16            ? $"Kein Artikel mit der Nummer {sku} gefunden."
17            : $"{item.Name}: {item.Quantity} Stück, Lagerort {item.Location}.";
18    }
19}

Zwei Dinge sind hier wichtiger, als sie aussehen.

Die Description-Attribute sind eure eigentliche API-Dokumentation, und zwar für das Modell. Sie landen im Prompt. Eine vage Beschreibung führt dazu, dass das Tool zur falschen Zeit oder mit unsinnigen Parametern aufgerufen wird. Schreibt sie wie eine Doku für einen neuen Kollegen: Was macht das Tool, wann setzt man es ein, welches Format haben die Parameter?

Dependency Injection funktioniert in Tool-Parametern. Der InventoryDbContext wird vom Container aufgelöst, sku kommt vom Modell. Ihr müsst also keine Service-Locator-Konstruktionen bauen, sondern arbeitet mit dem DI-Container, den ihr ohnehin habt.

Beachtenswert ist auch der Rückgabewert: kein JSON-Dump der Entity, sondern ein lesbarer Satz. Das Modell verarbeitet Text. Je klarer die Antwort, desto weniger interpretiert es hinein. Und der Nicht-gefunden-Fall gibt eine verwertbare Auskunft zurück statt null.

Testen und Debuggen

Zum Ausprobieren gibt es den MCP Inspector, ein Web-Tool, das sich als Client an euren Server hängt:

npx @modelcontextprotocol/inspector dotnet run

Ihr seht die registrierten Tools, könnt sie manuell aufrufen und den Nachrichtenverkehr mitlesen. Das ist deutlich schneller als der Umweg über einen echten Host. Und wenn ein Tool nicht auftaucht, liegt der Fehler meist in der Assembly-Registrierung oder an einem fehlenden Attribut.

Sicherheit gehört vor das Deployment

Der Punkt, an dem MCP-Projekte typischerweise schiefgehen: Der Server läuft mit euren Rechten, aber das Modell entscheidet, wann welches Tool aufgerufen wird. Und die Eingabe, auf die es reagiert, kann aus einem Dokument stammen, das jemand anderes geschrieben hat.

Daraus folgen drei Regeln:

Least Privilege auf Datenbankebene. Ein eigener DB-Benutzer mit Leserechten auf genau den Tabellen, die der Server braucht. Nicht der Anwendungsbenutzer.

Kein Tool, das freies SQL entgegennimmt. Ein ExecuteSqlRaw mit modellgeneriertem String ist eine SQL-Injection mit Extraschritten. Modelliert stattdessen enge, spezifische Tools.

Schreibende Operationen brauchen eine Freigabe. Lesen kann das Modell selbstständig. Alles, was Daten ändert, verschickt oder Geld bewegt, gehört hinter eine explizite Bestätigung, im Host oder in eurer eigenen Anwendung.

Transport und Betrieb

Der stdio-Transport ist die richtige Wahl für Server, die lokal neben dem Host laufen: kein Netzwerk, keine Authentifizierung, Prozessisolation durch das Betriebssystem.

Sobald mehrere Nutzer denselben Server verwenden sollen, braucht ihr den HTTP-Transport und damit Authentifizierung, Autorisierung und alles, was ein normaler Webservice auch braucht. Die gute Nachricht: Ab hier ist es wieder ASP.NET Core mit euren bestehenden Policies. Die schlechte: Ein MCP-Server ohne Auth im Netz ist eine offene Schnittstelle zu euren Daten.

Fazit

Der Aufwand für einen funktionierenden Server liegt bei einem halben Tag. Der Aufwand für einen Server, den man produktiv betreiben kann, liegt beim Berechtigungskonzept, das man vorher einplanen sollte, nicht nachträglich.

Kauthar A. | Solution Engineer
Kauthar A. | Solution Engineer

KI-Integration

Einstufung, Kennzeichnung und Datenwege klären wir am Projektanfang, nicht in der Abnahme. Auf .NET-Fundament, seit 2004, ISO 27001 und ISO 9001 zertifiziert.
Pfeil-Icon – Mehr erfahren

Mehr zum Thema

Pfeil nach rechts (Verlinkung)
Unittests in C#
09/2026

Unittests in C#

Blauer Pfeil nach rechts (Verlinkung)
EU AI Act: was er für den Mittel­stand bedeutet
09/2026

EU AI Act: was er für den Mittel­stand bedeutet

Blauer Pfeil nach rechts (Verlinkung)
Effizientes Prompt engineering in Claude 2026
09/2026

Effizientes Prompt engineering in Claude 2026

Blauer Pfeil nach rechts (Verlinkung)
Effizientes Prompt engineering in Claude 2026
09/2026

Effizientes Prompt engineering in Claude 2026

Blauer Pfeil nach rechts (Verlinkung)