Pular para o conteúdo

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.1recomendado 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 Bearerpara 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:

  1. O cliente envia initialize (sem header de sessão obrigatório).
  2. O servidor responde com serverInfo e um ID de sessão no header de resposta Mcp-Session-Id.
  3. O cliente inclui Mcp-Session-Id em cada requisição subsequente.
  4. O cliente chama tools/list para descobrir ferramentas, depois tools/call para invocá-las.

Um initialize mínimo:

Terminal window
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/Sales

Use 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 um name correspondente à caption pura do mapa measures de describe_cube (ex.: "Unit Sales", não "[Measures].[Unit Sales]").
  • rows / columns / filters — array de seleções de eixo. Cada item tem dimension e level (obrigatórios), hierarchy (obrigatório quando a dimensão tem mais de uma) e um array members opcional 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:

  1. 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.
  2. O cliente descobre automaticamente os endpoints OAuth do Saiku Cloud e se registra — sem client ID ou secret para criar (Dynamic Client Registration).
  3. 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.
  4. O conector está ativo — o agente pode chamar list_cubes e 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