Skip to content
Conduit

mcp server

SqlMcpServer

MCP-Server in C# / .NET 10: kontrollierter Lesezugriff auf eine SQL-Datenbank für KI-Agenten (API-Key, SQL-Guard, Audit-Log)

vompa/SqlMcpServercommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 22, 2026

SqlMcpServer

CI

Ein kleiner, bewusst sauber gehaltener MCP-Server (Model Context Protocol) in C# / .NET 10, der einem KI-Agenten kontrollierten, rein lesenden Zugriff auf eine SQL-Datenbank gibt – abgesichert durch Authentifizierung, Eingabeprüfung, eine schreibgeschützte Datenbankverbindung, Limits und Audit-Log.

Das Projekt ist als Referenz gedacht: wenig Code, aber jede Entscheidung ist begründet (siehe Designentscheidungen) und durch Tests belegt.

flowchart TB
    agent["KI-Agent<br/>(MCP-Client)"]

    subgraph server["SqlMcpServer (ASP.NET Core)"]
        direction TB
        auth["API-Key-Auth<br/>nur Hash gespeichert"]
        rate["Rate Limit<br/>pro Client"]
        tools["MCP-Tools<br/>list_tables · describe_table · run_query"]
        guard["SqlGuard<br/>genau ein SELECT"]
        svc["DatabaseService<br/>read-only Verbindung<br/>Zeilen- und Zeitlimit"]
        audit["Audit-Log<br/>pro Aufruf"]
        auth --> rate --> tools --> guard --> svc
        tools -.-> audit
    end

    db[("SQLite<br/>Mode=ReadOnly")]

    agent -- "HTTP + API-Key" --> auth
    svc --> db

    classDef client fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
    classDef security fill:#fff7ed,stroke:#f97316,color:#7c2d12
    classDef endpoint fill:#ecfdf5,stroke:#10b981,color:#064e3b
    classDef data fill:#f1f5f9,stroke:#64748b,color:#0f172a
    class agent client
    class auth,rate,guard,audit security
    class tools,svc endpoint
    class db data

So sieht es aus

Echte Aufrufe gegen den lokal laufenden Server (Ausgabe gekürzt). Grün zeigt erwartetes Verhalten, auch bei abgewiesenen Anfragen.

Funktionen

MCP-ToolZweck
list_tablesTabellen mit Zeilenzahl – erster Schritt, um das Schema zu erkunden
describe_tableSpalten, Typen, NOT NULL, Primärschlüssel einer Tabelle
run_queryEine einzelne SELECT-/WITH … SELECT-Abfrage, Ergebnis auf N Zeilen begrenzt

Fehler (z. B. verbotenes SQL oder Syntaxfehler) kommen mit verständlichem Text beim Agenten an, damit er seine Abfrage selbst korrigieren kann. Interne Fehler dagegen werden nur generisch gemeldet.

Schnellstart

Voraussetzung: .NET 10 SDK.

# 1. Server starten (Development-Profil enthält einen Demo-Key: dev-key-change-me)
ASPNETCORE_ENVIRONMENT=Development dotnet run --project src/SqlMcpServer --urls http://localhost:5080

# 2. Testen
curl -s -X POST http://localhost:5080/mcp \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -H "X-Api-Key: dev-key-change-me" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"run_query","arguments":{"sql":"SELECT country, COUNT(*) AS kunden FROM customers GROUP BY country"}}}'

Beim ersten Start wird eine Demo-Datenbank (db/demo.db) mit einem kleinen Shop-Schema (customers, products, orders, order_items) angelegt.

PowerShell: $env:ASPNETCORE_ENVIRONMENT="Development"; dotnet run --project src/SqlMcpServer --urls http://localhost:5080

An einen Agenten anbinden

Claude Code:

claude mcp add --transport http sqlmcp http://localhost:5080/mcp --header "X-Api-Key: dev-key-change-me"

Danach kann man z. B. fragen: „Welche Kunden aus der Schweiz haben bestellt, und wie hoch ist ihr Umsatz?“ – der Agent erkundet das Schema mit list_tables/describe_table und formuliert die passende SQL-Abfrage.

Eigenen Key erzeugen (Produktion)

Konfiguriert wird nur der SHA-256-Hash eines Keys, nie der Klartext:

KEY=$(openssl rand -base64 32)                      # zufälliger Key – nur an den Client geben
dotnet run --project src/SqlMcpServer -- hash-key "$KEY"   # Hash ausgeben
// Konfiguration, z. B. per Umgebungsvariable Authentication__ApiKeys__0__KeySha256 oder Secret Store
"Authentication": { "ApiKeys": [ { "Name": "reporting-agent", "KeySha256": "<hash>" } ] }

Ohne konfigurierten Key startet der Server nicht (fail closed).

Sicherheitsmodell

Ein Agent, der SQL schreibt, ist ein nicht vertrauenswürdiger Client – auch ein „braver“ Agent kann per Prompt Injection manipuliert werden. Deshalb gilt Defense in Depth:

SchichtMaßnahmeWo
AuthentifizierungAPI-Key (X-Api-Key oder Authorization: Bearer), nur Hash gespeichert, Vergleich in konstanter ZeitSecurity/
Secure by defaultFallback-Policy verlangt Login für jeden Endpunkt; nur /health ist anonymProgram.cs
Brute-Force-BremseRate Limit pro Client bzw. IP (auch für fehlgeschlagene Versuche)Program.cs
EingabeprüfungNur eine Anweisung, nur SELECT/WITH, keine Kommentare, Schlüsselwort-Sperrliste, Längenlimit; Literale werden vor der Prüfung neutralisiertData/SqlGuard.cs
Schreibschutz in der DBVerbindung mit Mode=ReadOnly und PRAGMA query_only – greift auch, falls der Guard umgangen würdeData/DatabaseService.cs
Kein SQL-ZusammenbauTabellennamen werden gegen das Schema geprüft und quotiert, Werte laufen als ParameterData/DatabaseService.cs
RessourcenschutzZeilenlimit (MaxRows) und Zeitlimit pro AbfrageData/DatabaseService.cs
NachvollziehbarkeitAudit-Log: welcher Client hat welches Tool mit welchem Argument aufgerufenTools/DatabaseTools.cs

Tests

dotnet test
BereichWas geprüft wird
SqlGuardTestserlaubte/abgelehnte Abfragen, Mehrfach-Statements, Kommentare, Quote-Tricks, Längenlimit
DatabaseServiceTestsSchema-Abfragen, Zeilenlimit, SQL-Fehler, Schreibschutz der Verbindung auch ohne Guard
ApiKeyHasherTestsHashing gegen bekannten SHA-256-Testvektor, Validierung von Hash-Strings
McpEndpointTestsIntegration gegen den echten Server im Speicher: 401 ohne/mit falschem Key, Key per Header und Bearer, Tool-Discovery per MCP-Client, SELECT Ende-zu-Ende, Schreibversuch → Tool-Fehler

Die CI (.github/workflows/ci.yml) baut mit TreatWarningsAsErrors und Analyzern (AnalysisLevel=latest-recommended) und führt alle Tests aus.

Projektstruktur

src/SqlMcpServer/
  Program.cs                    Verdrahtung: Konfiguration, Auth, Rate Limit, MCP
  Security/                     API-Key-Handler, Hashing, Optionen
  Data/                         SqlGuard, DatabaseService, Demo-Daten
  Tools/DatabaseTools.cs        die MCP-Tools, die der Agent sieht
tests/SqlMcpServer.Tests/       Unit- und Integrationstests (xUnit)

Designentscheidungen

  • HTTP-Transport (stateless) statt stdio: Ein Server mit Authentifizierung und Audit gehört in einen Dienst, nicht in einen Kindprozess des Clients. „Stateless“ erlaubt horizontale Skalierung ohne Sticky Sessions.
  • API-Key statt OAuth: Die MCP-Spezifikation sieht für öffentliche Server OAuth 2.1 vor. Für einen internen Dienst mit wenigen bekannten Clients sind API-Keys einfacher und ehrlicher; die Erweiterung auf OAuth/Entra ID ist unten skizziert.
  • SHA-256 für Keys: Keys sind zufällig und hochentropisch, ein schneller Hash genügt. Für Benutzerpasswörter wäre das falsch (dort PBKDF2/Argon2).
  • Guard + read-only Verbindung: Der Guard ist Komfort (frühe, erklärende Fehler) und eine Verteidigungslinie, aber Textfilter für SQL sind nie allein vertrauenswürdig – darum ist die Datenbankverbindung die verbindliche Grenze.
  • SQLite: Null Setup für Reviewer. Die Trennung DatabaseService/SqlGuard hält den Wechsel auf SQL Server oder PostgreSQL klein.

Bekannte Grenzen und nächste Schritte

  • Zeitlimit bei SQLite ist kooperativ: Microsoft.Data.Sqlite kann eine laufende Abfrage nicht hart unterbrechen; bei SQL Server/PostgreSQL übernimmt CommandTimeout bzw. Statement-Timeout das serverseitig.
  • Alle Tabellen sind sichtbar: Eine Allowlist freigegebener Tabellen/Spalten (oder Views) wäre der nächste Schritt für produktive Daten, ebenso Maskierung personenbezogener Spalten.
  • Eine Rolle: Alle Keys haben dieselben Rechte; Rollen/Scopes pro Key wären eine einfache Erweiterung.
  • OAuth 2.1 / Entra ID: AddJwtBearer statt ApiKey-Schema – Handler und Policy-Struktur bleiben gleich.
  • Rate-Limit-Zustand pro Instanz: Bei mehreren Instanzen braucht es einen gemeinsamen Speicher (z. B. Redis) oder ein Gateway.

Lizenz

MIT

↑↓ navigate openesc close