Startseite / Artikel

ClickHouse MCP und die Grenzen des Nur-Lese-Zugriffs: Strukturierter Zugang für Agenten

Der Lesezugriff auf MCP verhindert Schreibvorgänge, nicht jedoch falsche Zahlen, Datenlecks bei Mietern oder die Offenlegung des Schemas. Sehen Sie, was die ClickHouse-Benchmarks zeigen und wie strukturierte Metriken-Tools das Risiko verringern.

Verlag: REACTAPP.TOP8 Min. Lesezeit1772 Wörter

Quellen: ClickHouse MCP: Why Read-Only Access Isn’t Enough

Fast jedes Tutorial zur Verbindung eines KI-Agenten mit einer Datenbank endet mit der gleichen Empfehlung: Verwenden Sie eine nur zum Lesen zugängliche Verbindung. Aktivieren Sie readonly=1, gewähren Sie nur das Recht auf SELECT, sodass der Agent die Daten prüfen kann, sie aber niemals ändern darf. Das ist vernünftig – doch es ist die Antwort auf eine Frage, die nicht die eigentliche Sorge darstellt.

ClickHouse hat kürzlich 28 Modelle anhand von 201 echten Fragen aus seinem internen Analyse-Agenten getestet. Das beste Modell lag 76,6 % der Zeit richtig. Nur wenige falsche Antworten resultierten aus fehlgeschlagenen Abfragen; die meisten entstanden durch Abfragen, die ohne Fehler ausgeführt wurden, aber falsche Ergebnisse lieferten – und eine nur zum Lesen zugängliche Verbindung kann nicht einmal eine solche Situation verhindern.

„Nur Lesen“ bestimmt, ob ein Agent schreiben kann. Es sagt nichts darüber aus, was der Agent fragen darf. Wenn man einem Modell ein SQL-Tool zur Verfügung stellt, wird die Erstellung von Abfragen delegiert: Der Agent wählt die Tabellen, die Verknüpfungen, das Aggregationsniveau, die Filter sowie die Anzahl der zurückgegebenen Zeilen aus. „Nur Lesen“ beeinflusst all das nicht. Es schützt Ihre Daten davor, zerstört zu werden, tut aber nichts, um zu verhindern, dass sie falsch sind. Die entscheidende Kategorie ist nicht Lesen versus Schreiben, sondern willkürlich versus strukturiert.

Was der offizielle ClickHouse MCP-Server tut

Der offizielle Server ClickHouse/mcp-clickhouse enthält bereits die Funktionen list_databases, list_tables und run_query. Standardmäßig ist er nur zum Lesen geeignet. Die Aktivierung von Schreibfunktionen erfordert eine Umgebungsvariable, und gefährliche Befehle wie DROP benötigen eine weitere Variable. Für HTTP- und SSE-Transporte ist die Authentifizierung standardmäßig zwingend erforderlich (sowohl Träger-Token als auch OAuth/OIDC funktionieren), und der Start fehlschlägt, solange sie nicht eingerichtet sind. In der Dokumentation wird empfohlen, das MCP-Datenbankkonto wie jeden externen Client mit minimalen Berechtigungen zu behandeln.

Zwei Punkte verdienen besondere Aufmerksamkeit. Die Abfrist wird auf der Client-Seite durchgesetzt, sodass der MCP-Server mit dem Warten aufhört, die Abfrage aber weiterhin im Cluster ausgeführt wird. Zudem gilt readonly=1 pro Abfrage und bietet nur geringen Schutz. Wenn Sie sich als ClickHouse-User mit einem lockeren Profil und weitreichenden Berechtigungen anmelden, schützt dieses Flag nichts.

Auch wenn alles perfekt konfiguriert ist, hinterlässt eine eingeschränkte, nur zum Lesen zugelassene Verbindung weiterhin die Möglichkeit, dass ein Agent willkürliche SQL-Anfragen gegen Ihr Lager stellt. Das ist das eigentliche Problem.

Eine Fehlerquelle, die keine Einstellung beheben kann

Richtigkeit steht an erster Stelle, denn keine ClickHouse-Einstellung kann dabei helfen. Der Einsatz von SQL durch einen Agent setzt voraus, dass die Modelle bereits gut darin sind, solche Anfragen zu formulieren. Das stimmt teilweise – doch der falsche Teil davon ist der gefährliche.

Der beste Beweis stammt von ClickHouse selbst. Sein Agentic Analytics Benchmark, der am 1. September 2026 veröffentlicht wurde, wiedergab 201 Anfragen aus dem Produktivverkehr seines internen Analyseassistenten gegen eine synthetische Rekonstruktion desselben Lagers. Das beste Modell erzielte 76,6 % Punkte.

Diese Fragen stellten Mitarbeiter des Unternehmens, wobei jemand, der sich mit dem Thema auskennt, jedes Ergebnis durchlas. Wenn etwa ein Viertel der Antworten falsch ist, erkennt eine beteiligte Person die meisten daraus resultierenden Fehler. Wenn dasselbe Modell hinter einer für Kunden sichtbaren Funktion verwendet wird, kann niemand die Ausgabe überprüfen oder auch nur vermuten, dass sie falsch sein könnte. Die Genauigkeit bleibt dieselbe, doch das Risiko ist weitaus größer.

Die Verteilung der Fehler ist aussagekräftiger als die höchste Punktzahl. Laufzeitfehler sind praktisch verschwunden – nur vier von 28 Modellen zeigten solche Fehler. Bei allen Modellen war der größte Fehlergrund das Konzept selbst: Die Wahl des falschen Ansatzes erklärt 53 % bis 82 % der Fehlschläge. Laut ClickHouse sind Syntax und Ausführung bereits gelöst; nun zählen Datenmodellierung und Schichtkontext.

Der beste Performer veranschaulicht das. Fable 5.1 wies den geringsten Anteil an fehlerhaften Plänen unter den zehn führenden Modellen auf, doch gleichzeitig die höchste Fehlerrate bei fehlerhaften Daten insgesamt. Es erstellt einen guten Plan, wählt aber anschließend das falsche Feld oder die falsche Aggregationsstufe aus. Keines davon kann von einem intelligenteren Modell behoben werden. Beides verschwindet, wenn die verfügbaren Dimensionen, Messgrößen und Zeitaggregationsstufen im Voraus definiert sind.

Deshalb sind diese Fehler gefährlich – und nicht nur ärgerlich. Ein fehlerhafter Plan, der dennoch reibungslos ausgeführt wird, ergibt eine Abfrage, die korrekt interpretiert wird, ausgeführt wird, Zeilen zurückgibt und eine plausibel aussehende Zahl liefert. Es gibt keine Ausnahmen und keine roten Linien im Protokoll. Jemand liest diese Informationen in einem Dashboard oder einer Slack-Zusammenfassung und handelt daraufhin.

ClickHouse weist selbst auf eine Einschränkung hin. Die Referenzantworten entstanden, als drei Modelle verschiedener Hersteller übereinstimmten – und nicht durch eine menschliche Überprüfung; nur etwa die Hälfte dieser Übereinstimmungen war einstimmig, und das Urteilsgremium gilt als nachsichtig. Ein Konsens ist kein Beweis, weshalb 76,6 Prozent unsicher sind und vermutlich etwas hoch angesetzt sind. Der Ausgang hängt hier davon ab, wie die Fehler aussehen, und nicht vom genauen Prozentsatz.

ClickHouse erschwert die Arbeit im Vergleich zu vielen anderen Datenbanken. Vernetzungen sind aufwändig und schwierig in der Handhabung; die Dokumentation rät dazu, die Daten zu denormalisieren und die Anzahl der verwendeten Vernetzungen einzuschränken. Bei den meisten Vernetzungsalgorithmen wird die rechte Tabelle als Hash-Tabelle im Speicher abgelegt. Bei einer dictionary-basierten Vernetzung in einer eins-zu-vielen-Beziehung behält ClickHouse stillschweigend eine passende Zeile bei und ignoriert den Rest, sodass man zwar ein kleineres, falsches Ergebnis erhält, aber keinen Fehler. Ein Agent, der sich im Schema umsieht, wird mit Sicherheit in solche Fallen tappen. Weder readonly=1, noch ein Einstellungsprofil oder eine Quotenregelung helfen. Nur das Entziehen der Abfrageerstellungsmöglichkeiten an das Modell hilft.

Tenant-Bereich als Parameter – niemals als Grenze

Der zweite Fehlertyp ist jener, der zu einer Offenlegung von Sicherheitslücken führt. In einem Produkt mit mehreren Nutzern ist die Identität des Nutzers der wichtigste Prädikat in jeder Abfrage. Mit einem beliebigen SQL-Tool schreibt der Agent dieses Prädikat in eine zur Laufzeit erstellte WHERE-Klausel, sodass das Modell es weglassen, falsch auswerten oder durch zuvor gelesenen Inhalt davon abgehalten werden kann.

Hier lauert ein tiefer struktureller Problembereich. Wenn man einem MCP-Server eine Verbindungszeichenkette gibt, meldet er sich als ein einziger ClickHouse-Benutzer an, der an eine bestimmte Reihe von Zeilenvorgaben gebunden ist – ohne Kenntnis darüber, welcher Kunde hinter jeder Anfrage steht. Diese Information befindet sich in Ihrer Anwendung, in der authentifizierten Sitzung, während ein generisches SQL-Tool diese bereits vor dem Schreiben einer Zeile verloren hat.

Discovery übergeben das Schema

Das dritte Problem ist subtiler. Um ein Lager abzufragen, muss ein Agent es zunächst sehen, weshalb er list_tables aufruft, welches jede Tabelle mit ihren Spalten, Zeilenzahlen sowie dem vollständigen CREATE TABLE-Statement zurückgibt. Spaltennamen, das interne Vokabular des Unternehmens sowie Tabellen, von deren Existenz man vergessen hatte, gelangen alle in den Kontext des Modells und fließen anschließend in die Protokolle, Spuren sowie externe Inferenzdienste weiter. Es handelt sich dabei nicht um einen theoretischen Datenleck, da es sich um den notwendigen ersten Schritt handelt. Die Erlaubnis für beliebigen SQL-Code sowie die Offenlegung des gesamten Schemas sind letztlich dieselbe Entscheidung.

Gestrukturer Zugriff

Eine leistungsstärkere SQL-Tool ist keine Alternative – vielmehr ist das Fehlen eines solchen Tools die Alternative. Schreiben Sie Ihr Analysevokabular einmal nieder, also Dimensionen, Messgrößen, benannte Metriken, Tenant-Regeln sowie die Funktionsweise der Zeit, und bieten Sie dem Agenten nur das an. Das Modell wählt aus einem Katalog zulässiger Fragen aus und erstellt niemals eine Abfrage.

@hypequery/mcp, eine Bibliothek für dieses Zweck, bietet vier Tools an:

  • list_datasets: registrierte Datensätze mit Angaben zu ihren Dimensionen, Messgrößen und Metriken.
  • get_dataset_schema: die Felder, benannten Metriken, Filter und Grenzen eines Datensatzes.
  • query_metric: Ausführung einer benannten Metrik.
  • query_dataset: eine ad-hoc-Abfrage über ausgewählte Dimensionen und Messgrößen.

Es gibt kein run_sql, nicht einmal eine eingeschränkte Version. Ein Aufruf sieht so aus:

{
  "name": "query_metric",
  "arguments": {
    "dataset": "orders",
    "metric": "revenue",
    "dimensions": ["country"],
    "filters": [
      { "field": "status", "operator": "eq", "value": "completed" }
    ],
    "orderBy": [{ "field": "revenue", "direction": "desc" }],
  }
}

Der Agent wählte eine Metrik, eine Gruppierung und ein Filter aus. Er entschied nicht, was Umsatz bedeutet. Diese Definition befindet sich in Ihrer Codebasis, in TypeScript, wurde wie alles andere überprüft und versioniert, und Ihre API sowie das Dashboard verwenden dieselbe. Filteroperator und Zeitintervalle stammen aus festgelegten Mengen, und Felder werden vor dem Ausführen überprüft. Das Registry ist die Grenze:

export const datasets = {
  orders: {
    ...Orders,
    metrics: { revenue },
  },
};

Ein Datensatz, der in diesem Objekt fehlt, kann weder aufgelistet, untersucht noch abgefragt werden, wodurch die Menge der Fragen, die ein Agent stellen kann, zu einem überprüfbaren Objekt in Ihrem Repository wird. Der Tenant-Bereich ist außerdem nicht mehr etwas, das vom Modell bereitgestellt wird:

await createMCPServer({
  datasets,
  analytics,
  tenantId: 'tenant_123',
});

Der Wert stammt vom Host-Prozess, der bereits weiß, wer authentifiziert ist. Wenn man ein Datensatz mit Tenant-Schlüssel registriert, ohne diesen anzugeben, weigert sich der Server, zu starten:

MCP server tenantId is required for tenant-scoped datasets

Standardmäßig werden auch die generierten SQL-Abfragen sowie die auf SQL basierenden Feldausdrücke aus den Antworten entfernt, sodass das Modell nur Zahlen und Feldnamen sieht und nicht die Struktur Ihrer Abfrage.

Ein Wort zum Kostenaspekt

Der Preis ist eine Nebensache. Der offizielle Server leitet dem Client nur readonly weiter, ohne max_rows_to_read, max_bytes_to_read oder max_memory_usage, und es gibt keinen vom Server vorgeschriebenen Ausführungstimer. Bei Rechenleistung, die nach Verbrauch abgerechnet wird, stellt ein Agent, der in Schleifen vollständige Tabellenscans wiederholt, ein Abrechnungsproblem dar; eine begrenzte Anzahl an Abfragen verringert den Schaden. Doch LIMIT beschränkt die zurückgegebenen Zeilen, nicht die gescannten Bytes, und eine gruppierte Abfrage über einen langen Zeitraum liest weiterhin alles, was benötigt wird. Strukturierte Zugriffsbeschränkungen legen fest, welche Abfragen möglich sind, nicht, wie viel jede einzelne kostet. Egal, was Sie wählen – legen Sie in einem Konfigurationsprofil für den MCP-Benutzer echte Obergrenzen für Zeilen, Bytes, Speicher und Ausführungsdauer fest und fügen Sie eine Quotenregel hinzu.

Was es nicht löst

Nichts davon ist neu. Sowohl Simons Willisons tödliches Trio als auch die Supabase MCP-Demonstration von 2025 argumentierten, dass Lesezugriff keine Sicherheitsgrenze darstellt, und Cube sowie dbt fördern seit Langem die semantische Schicht für Agenten.

Der ehrliche Kompromiss ist die Flexibilität. Eine strukturierte Oberfläche beantwortet nur das, was jemand modelliert hat; daher erhält eine Frage außerhalb des Katalogs keine Antwort, während ein Agent mit SQL möglicherweise etwas Nützliches improvisieren könnte. Gleichzeitig wird dadurch auch nicht alles andere ausgeschlossen. Dataset-Labels und -Beschreibungen erreichen das Modell und prägen sein Verhalten – eine eingeschränktere Form der „Tool-Poisoning“-Oberfläche, aber keine vollständig beseitigte; diese Strings befinden sich jedoch in überprüftem TypeScript und nicht auf einem Server Dritter. Zudem wird eine regulierte Metrik ein Modell nicht davon abhalten, eine korrekte Zahl in seiner Zusammenfassung falsch zu interpretieren.

Mit kleinen Schritten anfangen

Es ist nicht notwendig, das gesamte Lager zu modellieren. Wählen Sie einen Metrikenwert, von dem Ihr Produkt abhängt – jenen, bei dem eine falsche Antwort schädlich wäre – definieren Sie ihn einmal, registrieren Sie ihn und geben Sie ihn dem Agenten allein. Vergleichen Sie anschließend, was der Agent zurückgibt, mit dem, was Ihre Anzeige anzeigt. Wenn die Zahlen übereinstimmen, befindet sich die Definition nun an einem Ort, von dem beide abrufen können – und das ist die ganze Idee.