MCP Server (AI-Ready Data Layer)
Drizzle Cube includes a built-in MCP server that lets AI agents like Claude, ChatGPT, and n8n query your semantic layer directly. The server follows the Model Context Protocol specification and exposes tools for discovering cubes, validating queries, and executing them.
MCP Server Endpoint
Section titled “MCP Server Endpoint”All framework adapters expose an MCP server at /mcp:
https://your-app.com/mcpThis endpoint implements the full MCP specification including:
- Tools - Functions the AI can call
- Prompts - Pre-built prompts for common tasks
- Server-Sent Events (SSE) - For streaming responses
Available MCP Tools
Section titled “Available MCP Tools”| Tool | Purpose |
|---|---|
drizzle_cube_discover | Find relevant cubes based on topic or intent |
drizzle_cube_validate | Validate queries and get auto-corrections |
drizzle_cube_load | Execute queries and return results |
Connecting AI Tools
Section titled “Connecting AI Tools”Claude Desktop
Section titled “Claude Desktop”Add to your claude_desktop_config.json:
{ "mcpServers": { "your-app-analytics": { "command": "npx", "args": ["-y", "@anthropic/mcp-remote", "https://your-app.com/mcp"] } }}With authentication (recommended for production):
{ "mcpServers": { "your-app-analytics": { "command": "npx", "args": [ "-y", "@anthropic/mcp-remote", "https://your-app.com/mcp", "--header", "Authorization: Bearer YOUR_TOKEN" ] } }}Claude Code
Section titled “Claude Code”claude mcp add --transport http analytics https://your-app.com/mcp \ --header "Authorization: Bearer YOUR_TOKEN"Claude.ai (Web)
Section titled “Claude.ai (Web)”- Go to Settings → Connectors → Add Connector
- Enter your MCP server URL:
https://your-app.com/mcp - The tools will be available in your conversations
Claude.ai connectors support OAuth 2.1 — if your MCP endpoint has an OAuth discovery document, Claude.ai will handle the auth flow automatically. You can also pass an authorization_token via the Messages API MCP connector for programmatic access.
ChatGPT
Section titled “ChatGPT”- Go to Settings → Connectors → Advanced → Developer Mode
- Add your MCP server URL:
https://your-app.com/mcp - The tools will be available in ChatGPT
Use the MCP Client node:
- Add an MCP Client node to your workflow
- Set the server URL:
https://your-app.com/mcp - Connect it to an AI Agent node
See n8n MCP Client documentation for details.
The AI Workflow
Section titled “The AI Workflow”When an AI agent connects to your MCP server, it typically follows this workflow:
User: "Show me average salary by department" │ ▼┌─────────────────────────────────────────────────────┐│ 1. drizzle_cube_discover ││ Find cubes related to "salary" and "department" ││ → Returns: Employees, Departments cubes with ││ suggested measures and dimensions │└─────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────┐│ 2. AI builds query using metadata ││ The AI uses cube metadata to construct a query ││ → Query: { measures: ['Employees.avgSalary'], ││ dimensions: ['Departments.name'] } │└─────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────┐│ 3. drizzle_cube_validate (optional) ││ Check query validity, get corrections if needed ││ → Returns: { isValid: true, correctedQuery } │└─────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────┐│ 4. drizzle_cube_load ││ Execute query with security context ││ → Returns: { data: [...], annotation: {...} } │└─────────────────────────────────────────────────────┘Tool Reference
Section titled “Tool Reference”drizzle_cube_discover
Section titled “drizzle_cube_discover”Find cubes relevant to a topic or intent.
// Parameters{ "topic": "salary", "intent": "I want to analyze compensation", "limit": 5, "minScore": 0.3}
// Response{ "cubes": [ { "cube": "Employees", "relevanceScore": 0.85, "matchedOn": ["measure:avgSalary", "measure:totalSalary"], "suggestedMeasures": ["Employees.avgSalary", "Employees.totalSalary"], "suggestedDimensions": ["Employees.department", "Employees.location"] } ]}drizzle_cube_validate
Section titled “drizzle_cube_validate”Validate a query and get helpful corrections.
// Parameters{ "query": { "measures": ["Employees.cont"], "dimensions": ["Departments.nam"] }}
// Response{ "isValid": false, "errors": [ "Unknown measure 'Employees.cont' - did you mean 'Employees.count'?", "Unknown dimension 'Departments.nam' - did you mean 'Departments.name'?" ], "correctedQuery": { "measures": ["Employees.count"], "dimensions": ["Departments.name"] }}drizzle_cube_load
Section titled “drizzle_cube_load”Execute a query and return results.
// Parameters{ "query": { "measures": ["Employees.count", "Employees.avgSalary"], "dimensions": ["Departments.name"] }}
// Response{ "data": [ { "Departments.name": "Engineering", "Employees.count": 45, "Employees.avgSalary": 125000 }, { "Departments.name": "Sales", "Employees.count": 32, "Employees.avgSalary": 85000 } ], "annotation": { "measures": { "Employees.count": { "title": "Total Employees", "type": "count" }, "Employees.avgSalary": { "title": "Average Salary", "type": "avg" } }, "dimensions": { "Departments.name": { "title": "Department Name", "type": "string" } } }}Configuration
Section titled “Configuration”Disabling MCP
Section titled “Disabling MCP”If you don’t want the MCP server exposed:
createCubeRouter({ // ... other options mcp: { enabled: false }})Selective Tool Exposure
Section titled “Selective Tool Exposure”Expose only specific MCP tools:
createCubeRouter({ // ... other options mcp: { enabled: true, tools: ['discover', 'validate', 'load'] // Only expose these }})Origin Restrictions
Section titled “Origin Restrictions”Restrict which origins can connect to your MCP server:
createCubeRouter({ // ... other options mcp: { enabled: true, allowedOrigins: ['https://claude.ai', 'https://chat.openai.com'] }})Security & Authentication
Section titled “Security & Authentication”Important: The MCP server does not include built-in authentication. You are responsible for adding authentication middleware, just like with the standard Cube API routes.
How It Works
Section titled “How It Works”The MCP endpoint (/mcp) is just an HTTP POST route — standard auth middleware protects it exactly like any other route. The complete flow is:
- Middleware authenticates the request (validates token, session, etc.)
extractSecurityContextextracts the user’s identity and permissions- Cube security filters scope all data access to the authenticated user’s tenant
Protecting MCP Endpoints
Section titled “Protecting MCP Endpoints”Apply authentication middleware before mounting the cube router. Here are examples for each framework:
Express
Section titled “Express”import express from 'express'import { createCubeRouter } from 'drizzle-cube/adapters/express'
const app = express()
// Auth middleware protects ALL routes including /mcpapp.use(async (req, res, next) => { const token = req.headers.authorization?.replace('Bearer ', '') if (!token) return res.status(401).json({ error: 'Unauthorized' }) req.user = await validateToken(token) next()})
// Both /cubejs-api/v1/* AND /mcp are now protectedapp.use('/', createCubeRouter({ cubes: [employeesCube], drizzle: db, schema, extractSecurityContext: async (req) => ({ organisationId: req.user.orgId, userId: req.user.id })}))import { Hono } from 'hono'import { createCubeRoutes } from 'drizzle-cube/adapters/hono'
const app = new Hono()
// Auth middleware protects ALL routes including /mcpapp.use('*', async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', '') if (!token) return c.json({ error: 'Unauthorized' }, 401) const user = await validateToken(token) c.set('user', user) await next()})
// Cube routes (including MCP) are now protectedapp.route('/', createCubeRoutes({ cubes: [employeesCube], drizzle: db, schema, extractSecurityContext: async (c) => ({ organisationId: c.get('user').orgId, userId: c.get('user').id })}))Fastify
Section titled “Fastify”import Fastify from 'fastify'import { registerCubeRoutes } from 'drizzle-cube/adapters/fastify'
const fastify = Fastify()
// Auth hook protects ALL routes including /mcpfastify.addHook('onRequest', async (request, reply) => { const token = request.headers.authorization?.replace('Bearer ', '') if (!token) return reply.code(401).send({ error: 'Unauthorized' }) request.user = await validateToken(token)})
await registerCubeRoutes(fastify, { cubes: [employeesCube], drizzle: db, schema, extractSecurityContext: async (req) => ({ organisationId: req.user.orgId, userId: req.user.id })})Next.js
Section titled “Next.js”// In Next.js, validate auth inside extractSecurityContext// since there's no separate middleware layer for API routes
export const cubeHandlers = createCubeHandlers({ cubes: [employeesCube], drizzle: db, schema, extractSecurityContext: async (req) => { const token = req.headers.authorization?.replace('Bearer ', '') if (!token) throw new Error('Unauthorized') const user = await validateToken(token) return { organisationId: user.orgId, userId: user.id } }})Security Context Enforcement
Section titled “Security Context Enforcement”All MCP tools respect your security context:
drizzle_cube_loadexecutes queries with the authenticated user’s security contextdrizzle_cube_discoverreturns cube metadata (schema information) — access is gated by your auth middleware- Multi-tenant isolation is enforced on all data access via cube
sqlfilters
Enhancing AI Discovery
Section titled “Enhancing AI Discovery”The MCP tools work best when your cubes have rich semantic metadata. See Adding Semantic Metadata for how to add:
- Descriptions for cubes, measures, and dimensions
- Synonyms for alternate names (“revenue” → “sales”, “income”)
- Example questions that help AI understand the cube’s purpose
Try It Live
Section titled “Try It Live”Connect to the demo MCP server to try it out:
https://try.drizzle-cube.dev/mcpNext Steps
Section titled “Next Steps”- Adding Semantic Metadata - Make your cubes more discoverable
- Claude Desktop Setup - Connect Claude Desktop to your data
- Claude Code Plugin - Query from Claude Code