Zum Inhalt springen

MCP Server

Saiku Cloud liefert einen eingebauten Model Context Protocol (MCP) Server, der Ihre Cubes als typisierte Tools an LLM-Agenten freigibt. Agenten entdecken, welche Cubes verfügbar sind, fragen ihre Struktur ab und führen Abfragen aus — alles über eine kleine, validierte Tool-Oberfläche, die sie davon abhält, Measure-Namen zu erfinden oder Spaltenverweise zu halluzinieren.

Der MCP-Endpunkt sitzt unter https://api.saiku.bi/rest/saiku/api/mcp. Er spricht Streamable-HTTP MCP (die aktuelle Protokollversion) und unterstützt zwei Authentifizierungsmethoden:

  • OAuth-2.1-Connectorempfohlen für Chat-Clients (Claude Desktop, claude.ai, Cursor — alles mit einer „Custom Connector” / „Remote MCP”-Option). Fügen Sie die URL ein, und der Client meldet den Benutzer an; es gibt keine Schlüssel zu kopieren oder zu erstellen. Self-Service, verfügbar ab Team-Plan.
  • Bearer-API-Keyfür programmatische / SDK-Clients. Der gleiche sk_...-Schlüssel wie für jeden anderen Endpunkt auf dieser Oberfläche.

Siehe Einen Agenten verbinden für beide Methoden.

Warum MCP statt einfachem SQL?

LLMs sind zuverlässig schlecht darin, analytisches SQL zu schreiben. Auf Spider 2.0, dem Standard-Real-World-Text-to-SQL-Benchmark, erreichen die besten Frontier-Modelle etwa 24%. Die Fehler sind nicht subtil — erfundene Spaltennamen, falsche Joins, halluzinierte Tabellen, über Datenbanken hinweg verwechselte Schemas.

Ein Saiku-Cube verhindert diese Fehlerklasse strukturell:

  • Der Agent wählt Measures und Dimensionen nach Namen aus einem selbstbeschreibenden Schema.
  • Die Validierung läuft serverseitig. Wenn der Agent einen Namen erfindet, geben wir einen strukturierten 400-Code zurück, der die gültigen Alternativen auflistet.
  • Aggregation, Joins und Totalisierung sind Teil der Cube-Definition — der Agent komponiert keine Joins und wählt keine Aggregationen, er wählt nur, welche Measures und Dimensionen er möchte.

Das Ergebnis ist eine analytische Oberfläche, die LLMs zuverlässig in Produktion verwenden können, nicht nur in Demos.

Der Handshake

MCP ist JSON-RPC 2.0 über HTTP. Der vollständige Flow:

  1. Der Client sendet initialize (kein Session-Header erforderlich).
  2. Der Server antwortet mit serverInfo und einer Session-ID im Response-Header Mcp-Session-Id.
  3. Der Client schließt Mcp-Session-Id in jeder weiteren Anfrage ein.
  4. Der Client ruft tools/list auf, um Tools zu entdecken, dann tools/call, um sie aufzurufen.

Ein minimales Initialize:

Terminal-Fenster
curl -X POST https://api.saiku.bi/rest/saiku/api/mcp \
-H "Authorization: Bearer $SAIKU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "my-agent", "version": "1.0" }
}
}'

Die Antwort enthält den Session-ID-Header — extrahieren Sie ihn aus Mcp-Session-Id und senden Sie ihn bei jedem weiteren Aufruf.

Wenn Sie einen High-Level-MCP-Client verwenden (die offiziellen SDKs übernehmen den Handshake für Sie), ist das alles unsichtbar. Konfigurieren Sie den Client mit der URL + Ihrem Bearer-Key, und er funktioniert.

Die sechs Tools

Der Server stellt sechs Tools zur Verfügung, die als die minimale nützliche Menge für analytische Arbeit konzipiert sind.

Cube-IDs

Jedes Tool, das einen Cube benennt, nimmt eine Cube-ID im Format connectionName/catalog/schema/cubeName. Sie erhalten die Teile aus der list_cubes-Antwort. Beispiel:

cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales

Verwenden Sie diesen gesamten Slash-separierten String als cube-Argument überall dort, wo die unten beschriebenen Tools einen wünschen.

list_cubes

Listet jeden OLAP-Cube auf, den der aktuelle Benutzer abfragen kann. Immer Ihr erster Aufruf, wenn Sie nicht wissen, was verfügbar ist.

{
"name": "list_cubes",
"arguments": {}
}

Antwort:

{
"cubes": [
{
"connectionName": "cloud__62e7bf54__v1__foodmart-globex-demo",
"catalog": "FoodMart",
"schema": "FoodMart",
"cubeName": "Sales",
"cubeCaption": "Sales",
"defaultMeasure": "Unit Sales",
"measureCount": 8
}
]
}

Liefert bis zu einigen Dutzend Einträgen zurück. Nicht paginiert — Saiku-Cubes werden pro Tenant in Zehnern gezählt, nicht in Tausenden.

describe_cube

Erhalten Sie die vollständige abfragbare Struktur eines Cubes. Rufen Sie dies immer vor run_query auf, wenn Sie die Cube-Struktur noch nicht gesehen haben — es sagt Ihnen genau, welche Namen gültig sind, und enthält fertige Beispiel-Abfrage-Bodies.

{
"name": "describe_cube",
"arguments": {
"cube": "cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales"
}
}

Liefert Measures zurück (per Kleinschreibung-Namen verschlüsselt), Dimensionen, Hierarchien, Levels, Sample-Member mit eindeutigen MDX-Namen (Stil [Customer].[Customers].[USA].[CA].[San Diego]) und einen einsatzbereiten requestSchema + examples-Block für das run_query-Tool.

search_members

Finden Sie die eindeutigen MDX-Namen von Membern auf einem Level per Substring-Match. Verwenden Sie dies, wenn der Cube mehr Member auf einem Level hat, als das Sample von describe_cube abdeckt (z. B. Suche nach einer bestimmten Stadt, einem Kunden oder einer Produktmarke), oder wenn der Benutzer „nach Italien filtern” sagt und Sie die Schreibweise bestätigen müssen.

{
"name": "search_members",
"arguments": {
"cube": "cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales",
"dimension": "Customers",
"hierarchy": "Customers",
"level": "City",
"q": "San",
"limit": 25
}
}

hierarchy ist erforderlich, wenn die Dimension mehr als eine hat (häufig — Time-Dimensionen haben typischerweise mehrere). Bei Dimensionen mit einer einzigen Hierarchie können Sie es weglassen.

Liefert bis zu limit Treffer mit caption, name und uniqueName zurück.

run_query

Das primäre Tool. Die meisten Benutzerfragen landen hier. Bauen Sie die Anfrage gegen die Struktur aus describe_cube; der Server validiert jeden Namen und gibt einen strukturierten VALIDATION_ERROR mit gültigen Alternativen zurück, wenn ein Name falsch ist, also vorvalidieren Sie nicht selbst.

{
"name": "run_query",
"arguments": {
"cube": "cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales",
"measures": [
{ "name": "Unit Sales" },
{ "name": "Store Sales" }
],
"rows": [
{ "dimension": "Time", "hierarchy": "Time", "level": "Year" }
],
"columns": [
{ "dimension": "Customers", "hierarchy": "Customers", "level": "Country",
"members": ["[Customer].[Customers].[USA]"] }
],
"filters": [
{ "dimension": "Promotions", "hierarchy": "Promotions", "level": "Promotion Name",
"members": ["[Promotion].[Promotions].[No Promotion]"] }
],
"limit": 100
}
}

Form-Regeln aus dem Live-requestSchema:

  • measures — Array von Objekten. Jeder Eintrag hat einen name, der der bloßen Beschriftung aus der measures-Map von describe_cube entspricht (z. B. "Unit Sales", nicht "[Measures].[Unit Sales]").
  • rows / columns / filters — Array von Achsenauswahlen. Jeder Eintrag hat dimension und level (erforderlich), hierarchy (erforderlich, wenn die Dimension mehr als eine hat) und ein optionales members-Array von eindeutigen MDX-Namen zur Einschränkung.
  • cube — der vollständige Cube-ID-String (siehe oben) oder ein Objekt {connectionName, catalog, schema, cubeName}.
  • format"records" (Standard) oder "matrix". Agenten wollen fast immer Records.
  • limit — Zeilenobergrenze. Standard 100; Max. 10 000 auf dem Mondrian-Pfad.

Antwort:

{
"status": "SUCCESS",
"queryId": "eb623b5f-f32d-4b84-96fa-2bde148556a0",
"runtimeMs": 196,
"totalRows": 1,
"data": [
{
"Year": "1997",
"Unit Sales": {
"value": 266773.0,
"formatted": "266,773",
"properties": { "formatString": "Standard", "datatype": "Numeric" }
},
"Store Sales": {
"value": 565238.13,
"formatted": "565,238.13",
"properties": { "formatString": "#,###.00", "datatype": "Numeric" }
}
}
]
}

Der Zell-Envelope ({value, formatted, properties}) trägt sowohl den geparsten numerischen Wert als auch den formatierten Anzeigestring von Mondrian. Verwenden Sie value für Arithmetik, formatted für die Anzeige.

Die queryId in der Antwort kann an drillthrough übergeben werden, um die zugrundeliegenden Zeilen hinter einer beliebigen Zelle zu inspizieren.

preview_query

Kompilieren Sie eine Abfrage zu MDX, ohne sie auszuführen. Verwenden Sie dies, wenn Sie dem Benutzer zeigen möchten, was die Abfrage tun wird, eine generierte Abfrage auditieren oder die Kosten vor dem Ausführen einer teuren Aggregation schätzen möchten.

{
"name": "preview_query",
"arguments": {
"cube": "cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales",
"measures": [{ "name": "Unit Sales" }],
"rows": [{ "dimension": "Time", "hierarchy": "Time", "level": "Year" }]
}
}

Antwort:

{
"status": "PREVIEW",
"queryId": "f4e2a890-…",
"generatedMdx": "SELECT NON EMPTY {[Measures].[Unit Sales]} ON COLUMNS,\nNON EMPTY [Time].[Time].[Year].Members ON ROWS\nFROM [Sales]"
}

Die Validierung läuft genauso wie bei run_query — Preview liefert die gleiche VALIDATION_ERROR-Form, wenn sich Namen nicht auflösen lassen.

drillthrough

Holen Sie die rohen Faktentabellenzeilen hinter einer bestimmten Abfrage. Verwenden Sie dies, wenn der Benutzer fragt: „zeige mir die zugrundeliegenden Transaktionen” oder Details zu einer einzelnen Zelle inspizieren möchte.

{
"name": "drillthrough",
"arguments": {
"queryId": "eb623b5f-f32d-4b84-96fa-2bde148556a0",
"maxrows": 100
}
}

Übergeben Sie die queryId, die ein früherer run_query-Aufruf zurückgegeben hat (es ist das queryId-Feld in der Antwort, nicht die id des JSON-RPC-Envelopes). Zellen in der Antwort verwenden denselben typisierten Envelope wie run_query.

Validierungsfehler

Jedes Tool, das Cube- / Measure- / Dimension-Namen akzeptiert, führt serverseitige Validierung durch. Wenn etwas nicht aufgelöst werden kann, erhalten Sie einen strukturierten Fehler statt eines generischen 500:

{
"isError": true,
"structuredContent": {
"code": "VALIDATION_ERROR",
"message": "Unknown measure: [Measures].[Reveue]",
"field": "measures[0]",
"alternatives": [
"[Measures].[Revenue]",
"[Measures].[Repeat Revenue]"
]
}
}

Das alternatives-Feld ist das Killer-Feature für LLM-Agenten — wenn sich das Modell einen Namen falsch merkt, teilt der Server ihm die nächstgelegenen gültigen Optionen mit, und der Agent kann sich in einem einzigen Retry selbst korrigieren, statt zu raten.

Einen Agenten verbinden

Chat-Clients — OAuth-Connector (empfohlen)

Clients, die Custom- / Remote-MCP-Connectors unterstützen (Claude Desktop, claude.ai, Cursor und andere), verbinden sich ohne API-Key — sie authentifizieren den Benutzer stattdessen über OAuth. Fügen Sie die MCP-URL als Custom Connector hinzu, und der Client erledigt den Rest:

  1. Fügen Sie in den Connector-Einstellungen des Clients einen Custom- / Remote-MCP-Server mit der URL https://api.saiku.bi/rest/saiku/api/mcp hinzu. Lassen Sie etwaige optionale „Client-ID / Secret”-Felder leer.
  2. Der Client entdeckt die OAuth-Endpunkte von Saiku Cloud automatisch und registriert sich selbst — keine Client-ID oder Secret zu erstellen (Dynamic Client Registration).
  3. Ein Browser öffnet sich, um sich bei Saiku Cloud anzumelden (Ihr normaler Workspace-Login), und zeigt dann einen Consent-Screen, der den Client und den angeforderten Zugriff benennt (Lesezugriff auf Cube-Abfragen). Genehmigen Sie ihn.
  4. Der Connector ist live — der Agent kann list_cubes und die anderen Tools sofort aufrufen.

Das Autorisieren eines Connectors erfordert einen Workspace mit Team-, Business- oder Enterprise-Plan. Das Credential, das der Client erhält, ist auf Read-only-MCP für Ihren Workspace gescopt und auf nichts anderes; es sieht niemals Ihre Warehouse-Anmeldedaten, andere Tenants oder eine Schreib-Oberfläche.

Connectors verwalten. Jeder autorisierte Client erscheint unter Verbindungen → Verbundene Agenten im Dashboard, wo Sie jeden mit einem Klick widerrufen können. Access-Tokens sind kurzlebig (1 Stunde) und aktualisieren sich still; ein Widerruf invalidiert das Refresh-Token sofort, sodass der Zugriff höchstens innerhalb der Stunde stoppt — meist sofort.

Programmatisch — Bearer-API-Key

Für Ihren eigenen Code, Agenten-Frameworks oder jeden Client, der einen statischen Header injiziert, erzeugen Sie einen API-Key im Dashboard (API-Keys, scopen Sie ihn auf MCP) und übergeben Sie ihn als Bearer-Token:

{
"mcpServers": {
"saiku": {
"url": "https://api.saiku.bi/rest/saiku/api/mcp",
"headers": {
"Authorization": "Bearer sk_..."
}
}
}
}

Über die offiziellen MCP-SDKs: Instantiieren Sie ein StreamableHttpClientTransport gegen die URL mit dem Bearer-Header und rufen Sie dann die Standard-MCP-Client-API auf.

Nach dem Verbinden (auf beide Arten) braucht der Prompt Ihres Agenten typischerweise eine Zeile — „Sie haben Zugriff auf einen saiku-MCP-Server mit Tools zum Abfragen von Analyse-Cubes. Verwenden Sie list_cubes, um zu beginnen.” Alles andere ist der Agent, der die Tools so entdeckt und verwendet, wie sie im Schema beschrieben sind.

Rate-Limits

MCP-Traffic zählt zum Standard-Rate-Limit-Budget Ihres Tenants (siehe Authentifizierung). Schwere Agenten im Starter-Plan können das Limit erreichen; ziehen Sie Team oder Business für produktive Agenten-Workloads in Betracht.

Was MCP nicht freigibt

Die MCP-Oberfläche ist read-only. Tools, die Status modifizieren — Workbooks speichern, Schemas erstellen, Verbindungen hinzufügen, Cube-Definitionen mutieren — werden nicht über MCP freigegeben. Diese leben auf dem Dashboard oder der REST-API, wo ein Mensch in der Schleife ist.

Das ist beabsichtigt: Ein Agent, der Ihre Daten lesen, aber Ihre Schemas nicht modifizieren kann, kann Ihr Analyse-Setup niemals versehentlich kaputt machen. Wenn Sie einen Agenten möchten, der Cubes erstellt, verwenden Sie die AI Inference API und setzen Sie einen menschlichen Review-Schritt dazwischen.

Wie geht es weiter?