Servidor MCP
O Saiku Cloud vem com um servidor Model Context Protocol (MCP) integrado que expõe seus cubos a agentes LLM como ferramentas tipadas. Os agentes descobrem quais cubos estão disponíveis, perguntam sobre sua estrutura e executam consultas — tudo por uma superfície de ferramentas pequena e validada que os impede de inventar nomes de medidas ou alucinar referências de colunas.
O endpoint MCP fica em
https://api.saiku.bi/rest/saiku/api/mcp. Ele fala
streamable-HTTP MCP (a versão atual do protocolo) e suporta
duas maneiras de autenticar:
- Conector OAuth 2.1 — recomendado para clientes de chat (Claude Desktop, claude.ai, Cursor — qualquer coisa com uma opção de “custom connector” / “remote MCP”). Cole a URL e o cliente loga o usuário; não há chaves para copiar ou criar. Self-service, disponível no Team em diante.
- API key Bearer — para clientes programáticos / SDK. A mesma
chave
sk_...de todos os outros endpoints desta superfície.
Veja Conectar um agente para os dois.
Por que MCP em vez de SQL puro?
LLMs são consistentemente ruins em escrever SQL analítico. No Spider 2.0, o benchmark padrão de texto-para-SQL no mundo real, os principais modelos de fronteira pontuam em torno de 24%. As falhas não são sutis — nomes de coluna inventados, joins errados, tabelas alucinadas, schemas confundidos entre bancos.
Um cubo Saiku evita essa classe de falha estruturalmente:
- O agente escolhe medidas e dimensões pelo nome a partir de um schema auto-descritivo.
- A validação roda do lado do servidor. Se o agente inventa um nome, retornamos um 400 estruturado listando as alternativas válidas.
- Agregação, joins e totalização são parte da definição do cubo — o agente não compõe joins nem escolhe agregações, apenas escolhe quais medidas e dimensões quer.
O resultado é uma superfície analítica que LLMs podem usar com confiabilidade em produção, não apenas em demos.
O handshake
MCP é JSON-RPC 2.0 sobre HTTP. O fluxo completo:
- O cliente envia
initialize(sem header de sessão obrigatório). - O servidor responde com
serverInfoe um ID de sessão no header de respostaMcp-Session-Id. - O cliente inclui
Mcp-Session-Idem cada requisição subsequente. - O cliente chama
tools/listpara descobrir ferramentas, depoistools/callpara invocá-las.
Um initialize mínimo:
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" } } }'A resposta inclui o header com o ID de sessão — extraia-o de
Mcp-Session-Id e envie-o em todas as chamadas subsequentes.
Se você está usando um cliente MCP de alto nível (os SDKs oficiais cuidam do handshake para você), tudo isso é invisível. Configure o cliente com a URL + sua chave Bearer e funciona.
As seis ferramentas
O servidor expõe seis ferramentas, projetadas para serem o conjunto mínimo útil para trabalho analítico.
IDs de cubo
Toda ferramenta que nomeia um cubo recebe um ID de cubo no formato
connectionName/catalog/schema/cubeName. Você obtém as partes da
resposta de list_cubes. Exemplo:
cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/SalesUse essa string inteira separada por barras como o argumento cube
em qualquer lugar onde as ferramentas abaixo pedirem um.
list_cubes
Lista todos os cubos OLAP que o usuário atual pode consultar. Sempre sua primeira chamada quando você não sabe o que está disponível.
{ "name": "list_cubes", "arguments": {}}Resposta:
{ "cubes": [ { "connectionName": "cloud__62e7bf54__v1__foodmart-globex-demo", "catalog": "FoodMart", "schema": "FoodMart", "cubeName": "Sales", "cubeCaption": "Sales", "defaultMeasure": "Unit Sales", "measureCount": 8 } ]}Retorna até algumas dezenas de entradas. Não é paginado — cubos do Saiku são contados em dezenas por tenant, não em milhares.
describe_cube
Obtém a estrutura consultável completa de um cubo. Sempre chame
isso antes de run_query se ainda não tiver visto a estrutura do cubo
— ele diz exatamente quais nomes são válidos e inclui
exemplos prontos de corpos de consulta.
{ "name": "describe_cube", "arguments": { "cube": "cloud__62e7bf54__v1__foodmart-globex-demo/FoodMart/FoodMart/Sales" }}Retorna medidas (indexadas por nome em minúsculas), dimensões,
hierarquias, levels, membros de exemplo com nomes únicos MDX
(estilo [Customer].[Customers].[USA].[CA].[San Diego]) e um
bloco requestSchema + examples pronto para uso pela ferramenta
run_query.
search_members
Encontra os nomes únicos MDX dos membros em um level por correspondência de
substring. Use quando o cubo tem mais membros em um level do que
a amostra do describe_cube cobriu (ex.: procurando uma
cidade, cliente ou marca específica) ou quando o usuário diz
“filtre por Itália” e você precisa confirmar a ortografia.
{ "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 é obrigatório quando a dimensão tem mais de uma
(comum — dimensões de Tempo tipicamente têm várias). Para
dimensões com hierarquia única, você pode omiti-lo.
Retorna até limit resultados com caption, name e
uniqueName.
run_query
A ferramenta principal. A maioria das perguntas dos usuários cai aqui. Construa a
requisição a partir da estrutura de describe_cube; o servidor
valida cada nome e retorna um VALIDATION_ERROR estruturado
com alternativas válidas se algum nome estiver errado, então não
faça pré-validação por conta própria.
{ "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 }}Regras de formato extraídas do requestSchema ao vivo:
measures— array de objetos. Cada item tem umnamecorrespondente à caption pura do mapameasuresdedescribe_cube(ex.:"Unit Sales", não"[Measures].[Unit Sales]").rows/columns/filters— array de seleções de eixo. Cada item temdimensionelevel(obrigatórios),hierarchy(obrigatório quando a dimensão tem mais de uma) e um arraymembersopcional de nomes únicos MDX para restringir.cube— a string completa de ID do cubo (veja acima) ou um objeto{connectionName, catalog, schema, cubeName}.format—"records"(default) ou"matrix". Agentes quase sempre querem records.limit— limite de linhas. Default 100; máximo 10 000 no caminho do Mondrian.
Resposta:
{ "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" } } } ]}O envelope da célula ({value, formatted, properties}) carrega
tanto o valor numérico parseado quanto a string de exibição formatada
do Mondrian. Use value para aritmética, formatted para exibição.
O queryId na resposta pode ser passado a drillthrough para
inspecionar as linhas subjacentes por trás de qualquer célula.
preview_query
Compila uma consulta para MDX sem executá-la. Use quando quiser mostrar ao usuário o que a consulta fará, auditar uma consulta gerada ou estimar o custo antes de rodar uma agregação cara.
{ "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" }] }}Resposta:
{ "status": "PREVIEW", "queryId": "f4e2a890-…", "generatedMdx": "SELECT NON EMPTY {[Measures].[Unit Sales]} ON COLUMNS,\nNON EMPTY [Time].[Time].[Year].Members ON ROWS\nFROM [Sales]"}A validação roda igual à de run_query — o preview retorna o
mesmo formato VALIDATION_ERROR se os nomes não resolverem.
drillthrough
Busca as linhas brutas da tabela de fatos por trás de uma consulta específica. Use quando o usuário pede “mostre-me as transações subjacentes” ou quer inspecionar o detalhe de uma única célula.
{ "name": "drillthrough", "arguments": { "queryId": "eb623b5f-f32d-4b84-96fa-2bde148556a0", "maxrows": 100 }}Passe o queryId retornado por uma chamada anterior de run_query (é o
campo queryId na resposta, não o id do envelope
JSON-RPC). As células na resposta usam o mesmo envelope tipado de
run_query.
Erros de validação
Toda ferramenta que aceita nomes de cubo / medida / dimensão roda validação do lado do servidor. Quando algo não resolve, você recebe um erro estruturado em vez de um 500 genérico:
{ "isError": true, "structuredContent": { "code": "VALIDATION_ERROR", "message": "Unknown measure: [Measures].[Reveue]", "field": "measures[0]", "alternatives": [ "[Measures].[Revenue]", "[Measures].[Repeat Revenue]" ] }}O campo alternatives é o recurso matador para agentes LLM —
quando o modelo se confunde com um nome, o servidor diz a ele as
opções válidas mais próximas, e o agente pode se autocorrigir em uma
nova tentativa em vez de chutar.
Conectar um agente
Clientes de chat — conector OAuth (recomendado)
Clientes que suportam conectores MCP customizados / remotos (Claude Desktop, claude.ai, Cursor e outros) conectam sem API key — eles autenticam o usuário via OAuth. Adicione a URL do MCP como conector customizado e o cliente cuida do resto:
- Nas configurações de conector do cliente, adicione um servidor MCP
customizado/remoto com a URL
https://api.saiku.bi/rest/saiku/api/mcp. Deixe quaisquer campos opcionais “client ID / secret” em branco. - O cliente descobre automaticamente os endpoints OAuth do Saiku Cloud e se registra — sem client ID ou secret para criar (Dynamic Client Registration).
- Um navegador abre para fazer login no Saiku Cloud (seu login normal de workspace), depois mostra uma tela de consentimento nomeando o cliente e o acesso que está sendo pedido (consulta somente leitura ao cubo). Aprove.
- O conector está ativo — o agente pode chamar
list_cubese as outras ferramentas imediatamente.
Autorizar um conector requer um workspace Team, Business ou Enterprise. A credencial que o cliente recebe tem escopo de MCP somente leitura para o seu workspace e nada mais; ela nunca vê as credenciais do seu data warehouse, outros tenants ou qualquer superfície de escrita.
Gerenciar conectores. Todo cliente autorizado aparece em Conexões → Agentes conectados no dashboard, onde você pode revogar qualquer um deles em um clique. Os tokens de acesso são de curta duração (1 hora) e renovam silenciosamente; uma revogação invalida o refresh token imediatamente, então o acesso para no máximo em uma hora — geralmente na hora.
Programático — API key Bearer
Para seu próprio código, frameworks de agente ou qualquer cliente que injete um header estático, gere uma API key no dashboard (API keys, restrinja o escopo a MCP) e passe-a como Bearer token:
{ "mcpServers": { "saiku": { "url": "https://api.saiku.bi/rest/saiku/api/mcp", "headers": { "Authorization": "Bearer sk_..." } } }}Via os SDKs oficiais MCP: instancie um
StreamableHttpClientTransport contra a URL com o header
Bearer, depois chame a API padrão do cliente MCP.
Após conectar (de qualquer das maneiras), o prompt do seu agente normalmente
precisa de uma linha — “You have access to a saiku MCP server with
tools for querying analytics cubes. Use list_cubes to start.”
Tudo o mais é o agente descobrindo e usando as ferramentas da
maneira que estão descritas no schema.
Limites de taxa
O tráfego MCP conta no orçamento padrão de limite de taxa do seu tenant (veja Autenticação). Agentes pesados no tier Starter podem atingir o limite; considere Team ou Business para cargas de agente em produção.
O que o MCP não expõe
A superfície MCP é somente leitura. Ferramentas que modificam estado — salvar workbooks, criar schemas, adicionar conexões, mutar definições de cubo — não são expostas via MCP. Essas vivem no dashboard ou na API REST, onde um humano está no loop.
Isso é deliberado: um agente que pode ler seus dados mas não pode modificar seus schemas nunca consegue quebrar acidentalmente sua configuração analítica. Se você quer um agente que escreva cubos, use a API de AI inference e coloque uma etapa de revisão humana no meio.
Para onde ir agora
- API de AI inference — para autorar cubos em vez de consultá-los.
- Autenticação — Bearer tokens, limites de taxa, formatos de erro.
- Isolamento de tenant — como o MCP mantém seus dados invisíveis para agentes de outros tenants.