Planung: Wie Du Deinen eigenen MCP Datenbank Server konzipierst
Ein MCP Server ist eine Schnittstelle zwischen KI-Assistenten und Deiner Datenbank. Er kontrolliert, wer was darf und protokolliert alle Zugriffe. In diesem Beispiel arbeiten wir mit der (fiktiven) Tabelle content_metadata in der (genauso fiktiven) Datenbank karlkratz_de.
Das ist eine Tabelle, in der z. B. die Meta-Daten der Seiten meiner Website enthalten sind. Und ich möchte gerne mit Tools wie karlsCORE und lokalen KI-Systemen die Meta-Informationen der Seiten in Abhängigkeit externer Parameter und interner Regeln kontinuierlich dynamisch justieren.
Was soll Dein Server können?
Damit das funktioniert, nutze ich eine Datenbank (MariaDB), Anthropic Claude und einen MCP Server. Der MCP Server muss eigentlich nur das hier können:
Die vier Grundoperationen
- SELECT: Daten aus der Tabelle lesen
- INSERT: Neue Datensätze hinzufügen
- UPDATE: Bestehende Datensätze ändern
- DELETE: Datensätze entfernen
Diese vier Operationen folgen dem CRUD-Prinzip (Create, Read, Update, Delete). Mehr braucht es nicht.
Sicherheitsmechanismen
Security, it's a thing! Ich betreibe meinen Server zwar hinter unterschiedlichen Firewall-Konzepten, aber die schönsten Angriffe kommen bekanntlich von innen. Deshalb ist es ok, wenn man auch beim experimentellen Einsatz wenigstens ein Auge auf dem Thema Sicherheit hat.
SQL-Injection ist die häufigste Angriffsmethode auf Datenbanken. Dein Server verhindert das durch:
- Parameter Binding: SQL-Befehle und Daten werden strikt getrennt
- Input Validation: Alle Eingaben werden auf schädliche Muster geprüft
- User Permissions: Verschiedene Benutzer mit unterschiedlichen Rechten
- Query Logging: Jede Operation wird protokolliert
Die Tabellenstruktur verstehen
Bevor ich mit irgendetwas anfange, schaue ich mir immer erst an, womit ich arbeite. In diesem Fall ist das die Struktur der content_metadata Tabelle:
DESCRIBE karlkratz_de.content_metadata;
Typische Struktur (bei mir sieht das so aus):
id INT PRIMARY KEY AUTO_INCREMENT
title VARCHAR(255) NOT NULL
... noch mehr (OpenGraph, Canonical, stuff)
description TEXT
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
status ENUM('draft', 'published', 'archived')
author_id INT
Diese Struktur bestimmt, was Du später mit Deinen Daten machen kannst. Bei mir sind das hauptsächlich Meta-Titel und Descriptions, die ich je nach Suchintention und Kontext anpassen möchte.
Die Architektur
Nach vielen Experimenten (und einigen spektakulären Fehlschlägen) hat sich bei mir diese Struktur bewährt:
content-metadata-server/
├── server.py # Der Hauptserver mit FastMCP
├── modules/
│ ├── database.py # Kümmert sich um alle DB-Operationen
│ ├── security.py # Prüft, ob jemand Unfug treiben will
│ └── logging.py # Schreibt mit, was passiert
├── config/
│ ├── database.json # Wo steht die Datenbank?
│ ├── credentials.json # Die geheimen Zugangsdaten (statt im .env, wo sie hingehören)
│ ├── security.json # Was ist erlaubt, was nicht?
│ └── logging.json # Was soll protokolliert werden?
├── logs/
│ └── operations.log # Das Logbuch
└── docs/
└── howto/ # Diese Dokumentation hier
Warum so modular? Weil ich dann einzelne Teile austauschen kann, ohne alles neu zu schreiben. Hat mir schon oft den Hintern gerettet.
Das Sicherheitskonzept
Datenbankebene
Ich arbeite mit zwei dedizierten MariaDB-Benutzern. Warum? Weil ich paranoid bin:
content_reader: Kann nur lesen. Perfekt für Reports und Analysen.content_writer: Kann fast alles mit der Tabelle machen. Aber auch nur mit dieser einen.
Beide Benutzer können sich nur von localhost verbinden. Keine Remote-Zugriffe.
Anwendungsebene
Hier wird's interessant. Nach einigen unangenehmen Erfahrungen (frag nicht...) habe ich folgende Sicherheitsmechanismen implementiert:
- Prepared Statements: Die einzige Art, wie ich noch SQL-Queries ausführe.
- Whitelist-Validierung: Nur explizit erlaubte Befehle kommen durch
- Rate Limiting: 50 Queries pro Minute reichen völlig. Braucht es plötzlich mehr, ist was falsch.
- Query Timeout: Nach 30 Sekunden ist Schluss. Wenn's länger dauert, ist die Query kaputt.
Error Handling
Fehler passieren. Ständig. Der Trick ist, sie sinnvoll zu behandeln. Mein Server gibt strukturierte Fehlermeldungen zurück:
{
"success": false,
"error": "Title cannot be empty",
"error_code": "VALIDATION_FAILED",
"details": {
"field": "title",
"reason": "Required field missing",
"suggested_action": "Provide a valid title"
}
}
Warum so ausführlich? Weil ich keine Lust habe, stundenlang in Logs zu wühlen. Der Fehler sagt mir sofort, was los ist und wie ich es behebe.
Logging-Strategie
Ich logge alles. Wirklich alles. Das hilft, merkwürdige Probleme zu finden.
Pro-Tipp: Für bestimmte Log-Level wird in meinen Systemen automatisch eine (Support-)Transaktion eröffnet. Und die schaut sich wiederum zuerst ein KI-System an, das Vorschläge für die Fehlerbehebung macht. Manche nennen mich faul. Ich nenne es schwäbisch.
Was wird geloggt?
- Wann ist was passiert? (Zeitstempel)
- Wer war's? (Benutzer)
- Was wurde gemacht? (Operation)
- Wie lange hat's gedauert? (Performance-Tracking)
- Hat's geklappt? (Erfolg/Fehler)
- Was wurde geändert? (Betroffene Datensätze)
Klingt nach Overkill? Ist es nicht. Wenn nachts um 3 Uhr was schiefgeht, bin ich froh über jede Information.
Performance-Optimierung
Performance ist kein Zufall. Hier ein paar grundlegende Gedanken:
- Connection Pooling: Verbindungen wiederverwenden statt ständig neue aufbauen
- Result Limits: Niemand braucht 10.000 Ergebnisse auf einmal. 100 reichen.
- Index-Nutzung: Richtige Indizes machen den Unterschied zwischen 0.01 und 10 Sekunden
- Caching: Warum zweimal das Gleiche abfragen? Caches sind Freunde.
Mit diesen Optimierungen sollte auch ein kleiner Server butterweich laufen.
Testing
Testing nervt, aber es nervt weniger als Production-Bugs. Mein Ansatz:
- Unit Tests: Jede Funktion (automatisiert) einzeln durchklopfen
- Integration Tests: Schauen, ob alles zusammenspielt
- Security Tests: Versuchen, den eigenen Server zu hacken (das genügt auf einfachstem Skript-Kiddi-Level)
- Load Tests: Was passiert, wenn 100 Systeme gleichzeitig anfragen?
Ja, das kostet Zeit. Aber es kostet weniger Zeit als am Wochenende Bugs zu fixen.
Deployment
Der schönste Server nützt nichts, wenn Claude ihn nicht findet. Die Registrierung läuft über die .claude.json:
{
"datenbank": {
"command": "/var/deinverzeichnis/mcp/datenbank/venv/bin/python",
"args": ["/var/deinverzeichnis/mcp/datenbank/server.py"],
"env": {
"DATENBANK_CONFIG_DIR": "/var/deinverzeichnis/mcp/datenbank/config"
}
}
}
Ein Neustart von Claude, und der Server ist verfügbar.
Grundregel bei mir: Es klappt NIE beim ersten Mal, weil irgendwo noch ein Bug drin ist. Also: Geduldig bleiben.
Was Du jetzt weißt
Wenn Du bis hier gelesen hast, kennst Du:
- Die vier CRUD-Operationen (mehr brauchst Du wirklich nicht).
- Warum Sicherheit wichtig ist (und wie Du sie implementierst).
- Eine bewährte Architektur (die bei mir seit Monaten läuft).
- Den Tech-Stack, der funktioniert.
Die Planung steht. Jetzt geht's ans Eingemachte. Im nächsten Kapitel zeige ich Dir Schritt für Schritt, wie Du das Grundgerüst aufbaust. Von der Verzeichnisstruktur über Python Virtual Environments bis zu den ersten Config-Dateien - alles mit konkreten Befehlen zum Kopieren. Weiter zur praktischen Vorbereitung und dem Setup