Przejdź do głównej zawartości

Serwer MCP

Saiku Cloud ma wbudowany serwer Model Context Protocol (MCP), który wystawia Twoje kostki agentom LLM jako typowane narzędzia. Agenci odkrywają, jakie kostki są dostępne, pytają o ich strukturę i uruchamiają zapytania — wszystko przez małą, walidowaną powierzchnię narzędzi, która nie pozwala im wymyślać nazw miar ani halucynować odniesień do kolumn.

Endpoint MCP siedzi pod adresem https://api.saiku.bi/rest/saiku/api/mcp. Mówi przez streamable-HTTP MCP (aktualna wersja protokołu) i obsługuje dwa sposoby uwierzytelniania:

  • Konektor OAuth 2.1zalecany dla klientów czatu (Claude Desktop, claude.ai, Cursor — wszystko, co ma opcję „konektor niestandardowy” / „zdalne MCP”). Wklej URL i klient loguje użytkownika; nie ma kluczy do kopiowania ani tworzenia. Samoobsługowe, dostępne w planie Team i wyższych.
  • Klucz API Bearerdla klientów programistycznych / SDK. Ten sam klucz sk_... co każdy inny endpoint na tej powierzchni.

Zobacz Podłączanie agenta dla obu.

Dlaczego MCP zamiast zwykłego SQL?

LLM-y są niezawodnie słabe w pisaniu analitycznego SQL. Na Spider 2.0, standardowym realistycznym benchmarku text-to-SQL, czołowe modele frontierowe osiągają około 24%. Awarie nie są subtelne — wymyślone nazwy kolumn, złe złączenia, halucynowane tabele, schemy pomylone między bazami.

Kostka Saiku zapobiega tej klasie awarii strukturalnie:

  • Agent wybiera miary i wymiary po nazwie z samoopisującej się schemy.
  • Walidacja chodzi po stronie serwera. Jeśli agent wymyśli nazwę, zwracamy strukturalne 400 z listą prawidłowych alternatyw.
  • Agregacja, złączenia i sumowanie są częścią definicji kostki — agent nie komponuje złączeń ani nie wybiera agregacji, tylko wybiera, jakich miar i wymiarów chce.

Wynikiem jest powierzchnia analityczna, której LLM-y mogą używać niezawodnie w produkcji, nie tylko w demach.

Uścisk dłoni

MCP to JSON-RPC 2.0 nad HTTP. Pełny przepływ:

  1. Klient wysyła initialize (nagłówek sesji nie jest wymagany).
  2. Serwer odpowiada z serverInfo i ID sesji w nagłówku odpowiedzi Mcp-Session-Id.
  3. Klient dołącza Mcp-Session-Id do każdego kolejnego żądania.
  4. Klient wywołuje tools/list do odkrycia narzędzi, potem tools/call, by je wywoływać.

Minimalne initialize:

Okno terminala
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" }
}
}'

Odpowiedź zawiera nagłówek ID sesji — wyciągnij go z Mcp-Session-Id i wysyłaj przy każdym kolejnym wywołaniu.

Jeśli używasz wysokopoziomowego klienta MCP (oficjalne SDK obsługują uścisk dłoni za Ciebie), to wszystko jest niewidoczne. Skonfiguruj klienta z URL-em + kluczem Bearer i działa.

Sześć narzędzi

Serwer wystawia sześć narzędzi, zaprojektowanych jako minimalny użyteczny zestaw do pracy analitycznej.

ID kostek

Każde narzędzie, które nazywa kostkę, przyjmuje ID kostki w formacie connectionName/catalog/schema/cubeName. Części dostaniesz z odpowiedzi list_cubes. Przykład:

cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales

Używaj całego tego ciągu rozdzielonego ukośnikami jako argumentu cube wszędzie, gdzie poniższe narzędzia go chcą.

list_cubes

Lista wszystkich kostek OLAP, do których bieżący użytkownik może wysyłać zapytania. Zawsze pierwsze wywołanie, gdy nie wiesz, co jest dostępne.

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

Odpowiedź:

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

Zwraca do kilkudziesięciu pozycji. Bez paginacji — kostki Saiku liczy się w dziesiątkach na tenanta, nie w tysiącach.

describe_cube

Dostań kompletną strukturę do odpytywania jednej kostki. Zawsze wywołuj to przed run_query, jeśli jeszcze nie widziałeś struktury kostki — mówi dokładnie, jakie nazwy są prawidłowe, i zawiera gotowe przykładowe ciała zapytań.

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

Zwraca miary (kluczowane po lowercase’owej nazwie), wymiary, hierarchie, poziomy, przykładowych członków z unikalnymi nazwami MDX (w stylu [Customer].[Customers].[USA].[CA].[San Diego]) i gotowy do użycia blok requestSchema + examples dla narzędzia run_query.

search_members

Znajdź unikalne nazwy MDX członków na poziomie po dopasowaniu podciągu. Używaj, gdy kostka ma więcej członków na poziomie niż pokryła próbka describe_cube (np. wyszukiwanie konkretnego miasta, klienta lub marki produktu), albo gdy użytkownik mówi „filtruj po Włochach” i musisz potwierdzić pisownię.

{
"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 jest wymagane, gdy wymiar ma więcej niż jedną (typowe — wymiary Time mają zwykle kilka). Dla wymiarów z jedną hierarchią możesz pominąć.

Zwraca do limit trafień z caption, name i uniqueName.

run_query

Główne narzędzie. Większość pytań użytkowników ląduje tutaj. Zbuduj żądanie względem struktury z describe_cube; serwer waliduje każdą nazwę i zwraca strukturalny VALIDATION_ERROR z prawidłowymi alternatywami, jeśli któraś nazwa jest zła, więc nie waliduj samodzielnie.

{
"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
}
}

Reguły kształtu wyciągnięte z aktualnego requestSchema:

  • measures — tablica obiektów. Każda pozycja ma name pasujący do gołego podpisu z mapy measures z describe_cube (np. "Unit Sales", nie "[Measures].[Unit Sales]").
  • rows / columns / filters — tablica wyborów osi. Każda pozycja ma dimension i level (wymagane), hierarchy (wymagane, gdy wymiar ma więcej niż jedną) i opcjonalną tablicę members z unikalnymi nazwami MDX, by ograniczyć zakres.
  • cube — pełny string ID kostki (zobacz wyżej) lub obiekt {connectionName, catalog, schema, cubeName}.
  • format"records" (domyślnie) lub "matrix". Agenci prawie zawsze chcą records.
  • limit — limit wierszy. Domyślnie 100; max 10 000 na ścieżce Mondrian.

Odpowiedź:

{
"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" }
}
}
]
}

Koperta komórki ({value, formatted, properties}) niesie zarówno sparsowaną wartość liczbową, jak i sformatowany przez Mondriana napis do wyświetlenia. Używaj value do arytmetyki, formatted do wyświetlania.

queryId z odpowiedzi można przekazać do drillthrough, by zinspecować bazowe wiersze za dowolną komórką.

preview_query

Skompiluj zapytanie do MDX bez jego wykonywania. Używaj, gdy chcesz pokazać użytkownikowi, co zapytanie zrobi, zaudytować wygenerowane zapytanie albo oszacować koszt przed uruchomieniem drogiej agregacji.

{
"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" }]
}
}

Odpowiedź:

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

Walidacja chodzi tak samo jak w run_query — preview zwraca ten sam kształt VALIDATION_ERROR, jeśli nazwy się nie rozwiązują.

drillthrough

Pobierz surowe wiersze tabeli faktów stojące za konkretnym zapytaniem. Używaj, gdy użytkownik pyta „pokaż mi bazowe transakcje” lub chce zinspecować szczegóły jednej komórki.

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

Przekaż queryId zwrócony przez wcześniejsze wywołanie run_query (to pole queryId w odpowiedzi, nie id koperty JSON-RPC). Komórki w odpowiedzi używają tej samej typowanej koperty co run_query.

Błędy walidacji

Każde narzędzie, które przyjmuje nazwy kostek / miar / wymiarów, przechodzi walidację po stronie serwera. Gdy coś się nie rozwiązuje, dostajesz strukturalny błąd, a nie generyczne 500:

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

Pole alternatives to zabójcza funkcja dla agentów LLM — gdy model źle zapamiętał nazwę, serwer mówi mu najbliższe prawidłowe opcje, a agent może się samodzielnie skorygować w jednej próbie zamiast zgadywać.

Podłączanie agenta

Klienci czatu — konektor OAuth (zalecane)

Klienci, którzy obsługują niestandardowe / zdalne konektory MCP (Claude Desktop, claude.ai, Cursor i inni), łączą się bez klucza API — uwierzytelniają użytkownika przez OAuth. Dodaj URL MCP jako konektor niestandardowy i klient zrobi resztę:

  1. W ustawieniach konektorów klienta dodaj niestandardowy/zdalny serwer MCP z URL-em https://api.saiku.bi/rest/saiku/api/mcp. Zostaw puste pola opcjonalne „client ID / secret”.
  2. Klient automatycznie odkrywa endpointy OAuth Saiku Cloud i rejestruje się — bez tworzenia client ID ani sekretu (Dynamic Client Registration).
  3. Przeglądarka otwiera logowanie do Saiku Cloud (Twoje normalne logowanie do workspace), potem pokazuje ekran zgody nazywając klienta i dostęp, o który prosi (odpytywanie kostki tylko do odczytu). Zatwierdź.
  4. Konektor żyje — agent może natychmiast wywołać list_cubes i inne narzędzia.

Autoryzacja konektora wymaga workspace w planie Team, Business lub Enterprise. Poświadczenie, które dostaje klient, jest zakresowane do tylko-do-odczytu MCP dla Twojego workspace i niczego więcej; nigdy nie widzi Twoich poświadczeń hurtowni, innych tenantów ani żadnej powierzchni zapisu.

Zarządzanie konektorami. Każdy autoryzowany klient pojawia się w Połączenia → Podłączeni agenci w dashboardzie, gdzie możesz odwołać dowolnego z nich jednym kliknięciem. Tokeny dostępu są krótkożywotne (1 godzina) i odświeżają się po cichu; unieważnienie unieważnia natychmiast refresh token, więc dostęp wygasa najpóźniej w ciągu godziny — zwykle od razu.

Programistyczne — klucz API Bearer

Dla własnego kodu, frameworków agentów lub dowolnego klienta, który wstrzykuje statyczny nagłówek, wygeneruj klucz API w dashboardzie (Klucze API, zakresuj do MCP) i przekaż jako token Bearer:

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

Przez oficjalne SDK MCP: zinstancjonuj StreamableHttpClientTransport względem URL-a z nagłówkiem Bearer, potem wywołuj standardowe API klienta MCP.

Po podłączeniu (na którykolwiek sposób) prompt Twojego agenta zwykle potrzebuje jednej linijki — „Masz dostęp do serwera MCP saiku z narzędziami do odpytywania kostek analitycznych. Użyj list_cubes, by zacząć.” Reszta to agent odkrywający i używający narzędzi tak, jak są opisane w schemie.

Limity szybkości

Ruch MCP wlicza się w standardowy budżet limitów szybkości Twojego tenanta (zobacz Uwierzytelnianie). Ciężcy agenci na planie Starter mogą uderzyć w limit; rozważ Team lub Business dla produkcyjnych obciążeń agentów.

Czego MCP nie wystawia

Powierzchnia MCP jest tylko do odczytu. Narzędzia, które modyfikują stan — zapisywanie workbooków, tworzenie schem, dodawanie połączeń, mutowanie definicji kostek — nie są wystawione przez MCP. Te żyją w dashboardzie lub w REST API, gdzie człowiek jest w pętli.

To celowe: agent, który może odczytywać Twoje dane, ale nie może modyfikować Twoich schem, nigdy nie zepsuje przypadkowo Twojego ustawienia analitycznego. Jeśli chcesz agenta, który autoruje kostki, użyj API inferencji AI i wstaw między kroki przegląd człowieka.

Co dalej