drizzle-cube 0.4.47 → 0.4.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/adapters/{compiler-O3T1u7jl.js → compiler-BXfrvebp.js} +4 -3
  2. package/dist/adapters/{compiler-CA6iopu7.cjs → compiler-CPE-YZfe.cjs} +2 -2
  3. package/dist/adapters/express/index.cjs +1 -1
  4. package/dist/adapters/express/index.js +4 -4
  5. package/dist/adapters/fastify/index.cjs +1 -1
  6. package/dist/adapters/fastify/index.js +39 -39
  7. package/dist/adapters/{handler-BO2nq6IS.cjs → handler-DumFgnNM.cjs} +10 -10
  8. package/dist/adapters/{handler-CjVc3ytc.js → handler-RItnSaEl.js} +128 -350
  9. package/dist/adapters/hono/index.cjs +1 -1
  10. package/dist/adapters/hono/index.js +4 -4
  11. package/dist/adapters/mcp-prompts-BUFyQLHQ.js +377 -0
  12. package/dist/adapters/mcp-prompts-B_NvEJT_.cjs +111 -0
  13. package/dist/adapters/mcp-tools.cjs +1 -1
  14. package/dist/adapters/mcp-tools.js +2 -2
  15. package/dist/adapters/mcp-transport-BhPqU0Ft.js +527 -0
  16. package/dist/adapters/mcp-transport-Kk_HTCeR.cjs +35 -0
  17. package/dist/adapters/mcp-transport.d.ts +310 -5
  18. package/dist/adapters/nextjs/index.cjs +1 -1
  19. package/dist/adapters/nextjs/index.js +4 -4
  20. package/dist/adapters/utils-CyBt-as9.cjs +15 -0
  21. package/dist/adapters/{utils-C7Nrw9Wb.js → utils-IH1ePsBd.js} +707 -655
  22. package/dist/adapters/utils.cjs +1 -1
  23. package/dist/adapters/utils.d.ts +11 -0
  24. package/dist/adapters/utils.js +2 -2
  25. package/dist/mcp-app/mcp-app.html +78 -44
  26. package/dist/server/index.cjs +144 -38
  27. package/dist/server/index.d.ts +24 -19
  28. package/dist/server/index.js +541 -676
  29. package/package.json +1 -1
  30. package/dist/adapters/mcp-prompts-BAutSQYA.js +0 -344
  31. package/dist/adapters/mcp-prompts-DsAkafVn.cjs +0 -5
  32. package/dist/adapters/mcp-transport-Cim_5cBN.js +0 -424
  33. package/dist/adapters/mcp-transport-DPpBCNea.cjs +0 -70
  34. package/dist/adapters/utils-tNZ6Cvzw.cjs +0 -15
@@ -1,3 +1,3 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../utils-tNZ6Cvzw.cjs`),t=require(`../compiler-CA6iopu7.cjs`),n=require(`../mcp-transport-DPpBCNea.cjs`);let r=require(`hono`);var i=e=>{let t={origin:`*`,allowMethods:[`GET`,`HEAD`,`PUT`,`POST`,`DELETE`,`PATCH`],allowHeaders:[],exposeHeaders:[],...e},n=(e=>typeof e==`string`?e===`*`?t.credentials?e=>e||null:()=>e:t=>e===t?t:null:typeof e==`function`?e:t=>e.includes(t)?t:null)(t.origin),r=(e=>typeof e==`function`?e:Array.isArray(e)?()=>e:()=>[])(t.allowMethods);return async function(e,i){function a(t,n){e.res.headers.set(t,n)}let o=await n(e.req.header(`origin`)||``,e);if(o&&a(`Access-Control-Allow-Origin`,o),t.credentials&&a(`Access-Control-Allow-Credentials`,`true`),t.exposeHeaders?.length&&a(`Access-Control-Expose-Headers`,t.exposeHeaders.join(`,`)),e.req.method===`OPTIONS`){(t.origin!==`*`||t.credentials)&&a(`Vary`,`Origin`),t.maxAge!=null&&a(`Access-Control-Max-Age`,t.maxAge.toString());let n=await r(e.req.header(`origin`)||``,e);n.length&&a(`Access-Control-Allow-Methods`,n.join(`,`));let i=t.allowHeaders;if(!i?.length){let t=e.req.header(`Access-Control-Request-Headers`);t&&(i=t.split(/\s*,\s*/))}return i?.length&&(a(`Access-Control-Allow-Headers`,i.join(`,`)),e.res.headers.append(`Vary`,`Access-Control-Request-Headers`)),e.res.headers.delete(`Content-Length`),e.res.headers.delete(`Content-Type`),new Response(null,{headers:e.res.headers,status:204,statusText:`No Content`})}await i(),(t.origin!==`*`||t.credentials)&&e.header(`Vary`,`Origin`,{append:!0})}};function a(a){let{cubes:o,drizzle:s,schema:c,extractSecurityContext:l,engineType:u,cors:d,basePath:f=`/cubejs-api/v1`,cache:p,mcp:m={enabled:!0},agent:h}=a;if(!a.semanticLayer&&(!o||o.length===0))throw Error(`Either semanticLayer or a non-empty cubes array must be provided`);let g=new r.Hono;d&&g.use(`/*`,i(d));let _=a.semanticLayer??new t.t({drizzle:s,schema:c,engineType:u,cache:p,rlsSetup:a.rlsSetup});if(!a.semanticLayer&&o&&o.forEach(e=>{_.registerCube(e)}),g.post(`${f}/load`,async t=>{try{let n=await t.req.json(),r=n.query||n,i=await l(t),a=_.validateQuery(r);if(!a.isValid)return t.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=t.req.header(`x-cache-control`)===`no-cache`,s=await _.executeMultiCubeQuery(r,i,{skipCache:o});return t.json(e.r(r,s,_))}catch(e){return console.error(`Query execution error:`,e),t.json({error:e instanceof Error?e.message:`Query execution failed`},500)}}),g.get(`${f}/load`,async t=>{try{let n=t.req.query(`query`);if(!n)return t.json({error:`Query parameter is required`},400);let r;try{r=JSON.parse(n)}catch{return t.json({error:`Invalid JSON in query parameter`},400)}let i=await l(t),a=_.validateQuery(r);if(!a.isValid)return t.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=t.req.header(`x-cache-control`)===`no-cache`,s=await _.executeMultiCubeQuery(r,i,{skipCache:o});return t.json(e.r(r,s,_))}catch(e){return console.error(`Query execution error:`,e),t.json({error:e instanceof Error?e.message:`Query execution failed`},500)}}),g.post(`${f}/batch`,async t=>{try{let{queries:n}=await t.req.json();if(!n||!Array.isArray(n))return t.json({error:`Request body must contain a "queries" array`},400);if(n.length===0)return t.json({error:`Queries array cannot be empty`},400);let r=await e.u(n,await l(t),_,{skipCache:t.req.header(`x-cache-control`)===`no-cache`});return t.json(r)}catch(e){return console.error(`Batch execution error:`,e),t.json({error:e instanceof Error?e.message:`Batch execution failed`},500)}}),g.get(`${f}/meta`,t=>{try{let n=_.getMetadata();return t.json(e.a(n))}catch(e){return console.error(`Metadata error:`,e),t.json({error:e instanceof Error?e.message:`Failed to fetch metadata`},500)}}),g.post(`${f}/sql`,async t=>{try{let n=await t.req.json(),r=await l(t),i=_.validateQuery(n);if(!i.isValid)return t.json({error:`Query validation failed: ${i.errors.join(`, `)}`},400);let a=n.measures?.[0]||n.dimensions?.[0];if(!a)return t.json({error:`No measures or dimensions specified`},400);let o=a.split(`.`)[0],s=await _.generateSQL(o,n,r);return t.json(e.o(n,s))}catch(e){return console.error(`SQL generation error:`,e),t.json({error:e instanceof Error?e.message:`SQL generation failed`},500)}}),g.get(`${f}/sql`,async t=>{try{let n=t.req.query(`query`);if(!n)return t.json({error:`Query parameter is required`},400);let r=JSON.parse(n),i=await l(t),a=_.validateQuery(r);if(!a.isValid)return t.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=r.measures?.[0]||r.dimensions?.[0];if(!o)return t.json({error:`No measures or dimensions specified`},400);let s=o.split(`.`)[0],c=await _.generateSQL(s,r,i);return t.json(e.o(r,c))}catch(e){return console.error(`SQL generation error:`,e),t.json({error:e instanceof Error?e.message:`SQL generation failed`},500)}}),g.post(`${f}/dry-run`,async t=>{try{let n=await t.req.json(),r=await e.f(n.query||n,await l(t),_);return t.json(r)}catch(e){return console.error(`Dry-run error:`,e),t.json({error:e instanceof Error?e.message:`Dry-run validation failed`,valid:!1},400)}}),g.get(`${f}/dry-run`,async t=>{try{let n=t.req.query(`query`);if(!n)return t.json({error:`Query parameter is required`,valid:!1},400);let r=await e.f(JSON.parse(n),await l(t),_);return t.json(r)}catch(e){return console.error(`Dry-run error:`,e),t.json({error:e instanceof Error?e.message:`Dry-run validation failed`,valid:!1},400)}}),g.post(`${f}/explain`,async e=>{try{let t=await e.req.json(),n=t.query||t,r=t.options||{},i=await l(e),a=_.validateQuery(n);if(!a.isValid)return e.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=await _.explainQuery(n,i,r);return e.json(o)}catch(t){return console.error(`Explain error:`,t),e.json({error:t instanceof Error?t.message:`Explain query failed`},500)}}),h&&g.post(`${f}/agent/chat`,async e=>{try{let{handleAgentChat:t}=await Promise.resolve().then(()=>require(`../handler-BO2nq6IS.cjs`)),{message:n,sessionId:r,history:i}=await e.req.json();if(!n||typeof n!=`string`)return e.json({error:`message is required and must be a string`},400);let a=(h.apiKey||``).trim();if(h.allowClientApiKey){let t=e.req.header(`x-agent-api-key`);t&&(a=t.trim())}if(!a)return e.json({error:`No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header.`},401);let o=h.allowClientApiKey?e.req.header(`x-agent-provider`):void 0,s=h.allowClientApiKey?e.req.header(`x-agent-model`):void 0,c=h.allowClientApiKey?e.req.header(`x-agent-provider-endpoint`):void 0,u=await l(e),d=h.buildSystemContext?.(u),f=new TextEncoder,p=new ReadableStream({async start(e){try{let l=t({message:n,sessionId:r,history:i,semanticLayer:_,securityContext:u,agentConfig:h,apiKey:a,systemContext:d,providerOverride:o,modelOverride:s,baseURLOverride:c});for await(let t of l){let n=`data: ${JSON.stringify(t)}\n\n`;e.enqueue(f.encode(n))}}catch(t){let n={type:`error`,data:{message:t instanceof Error?t.message:`Stream failed`}};e.enqueue(f.encode(`data: ${JSON.stringify(n)}\n\n`))}finally{e.close()}}});return new Response(p,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}})}catch(t){return console.error(`Agent chat error:`,t),e.json({error:t instanceof Error?t.message:`Agent chat failed`},500)}}),m.enabled!==!1){let e={uri:`drizzle-cube://schema`,name:`Cube Schema`,description:`Current cube metadata as JSON`,mimeType:`application/json`,text:JSON.stringify(_.getMetadata(),null,2)},t=[...n.l(),e],r=n.c(),i=m.basePath??`/mcp`;g.post(`${i}`,async e=>{let i=n._(e.req.header(`origin`),m.allowedOrigins?{allowedOrigins:m.allowedOrigins}:{});if(!i.valid)return e.json(n.i(null,-32600,i.reason),403);let a=e.req.header(`accept`);if(!n.g(a))return e.json(n.i(null,-32600,`Accept header must include both application/json and text/event-stream`),400);let o=n.f(e.req.header());if(!o.ok)return e.json({error:`Unsupported MCP protocol version`,supported:o.supported},426);let s=n.p(await e.req.json().catch(()=>null));if(!s)return e.json(n.i(null,-32600,`Invalid JSON-RPC 2.0 request`),400);let c=n.v(a),u=s.method===`initialize`;try{let i=await n.s(s.method,s.params,{semanticLayer:_,extractSecurityContext:l,rawRequest:e,rawResponse:null,negotiatedProtocol:o.negotiated,resources:t,prompts:r,appEnabled:!!m.app});if(n.d(s))return e.body(null,202);let a=n.a(s.id??null,i),d=u&&i&&typeof i==`object`&&`sessionId`in i?i.sessionId:void 0,f={};if(d&&(f[n.r]=d),c){let e=new TextEncoder,t=n.m(),r=new ReadableStream({start(r){r.enqueue(e.encode(`id: ${t}\n\n`)),r.enqueue(e.encode(n.h(a,t))),r.close()}});return new Response(r,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`,...f}})}return e.json(a,200,f)}catch(t){if(n.d(s))return console.error(`MCP notification processing error:`,t),e.body(null,202);console.error(`MCP RPC error:`,t);let r=t?.code??-32603,i=t?.data,a=t.message||`MCP request failed`,o=n.i(s.id??null,r,a,i);if(c){let e=new TextEncoder,t=n.m(),r=new ReadableStream({start(r){r.enqueue(e.encode(`id: ${t}\n\n`)),r.enqueue(e.encode(n.h(o,t))),r.close()}});return new Response(r,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}})}return e.json(o,200)}}),g.delete(`${i}`,e=>e.json({error:`Session termination not supported`},405)),g.get(`${i}`,e=>{let t=new TextEncoder,r=n.m(),i,a=new ReadableStream({start(e){e.enqueue(t.encode(n.h({jsonrpc:`2.0`,method:`mcp/ready`,params:{protocol:`streamable-http`}},r,15e3))),i=setInterval(()=>{e.enqueue(t.encode(`: keep-alive
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../utils-CyBt-as9.cjs`),t=require(`../compiler-CPE-YZfe.cjs`),n=require(`../mcp-transport-Kk_HTCeR.cjs`);let r=require(`hono`);var i=e=>{let t={origin:`*`,allowMethods:[`GET`,`HEAD`,`PUT`,`POST`,`DELETE`,`PATCH`],allowHeaders:[],exposeHeaders:[],...e},n=(e=>typeof e==`string`?e===`*`?t.credentials?e=>e||null:()=>e:t=>e===t?t:null:typeof e==`function`?e:t=>e.includes(t)?t:null)(t.origin),r=(e=>typeof e==`function`?e:Array.isArray(e)?()=>e:()=>[])(t.allowMethods);return async function(e,i){function a(t,n){e.res.headers.set(t,n)}let o=await n(e.req.header(`origin`)||``,e);if(o&&a(`Access-Control-Allow-Origin`,o),t.credentials&&a(`Access-Control-Allow-Credentials`,`true`),t.exposeHeaders?.length&&a(`Access-Control-Expose-Headers`,t.exposeHeaders.join(`,`)),e.req.method===`OPTIONS`){(t.origin!==`*`||t.credentials)&&a(`Vary`,`Origin`),t.maxAge!=null&&a(`Access-Control-Max-Age`,t.maxAge.toString());let n=await r(e.req.header(`origin`)||``,e);n.length&&a(`Access-Control-Allow-Methods`,n.join(`,`));let i=t.allowHeaders;if(!i?.length){let t=e.req.header(`Access-Control-Request-Headers`);t&&(i=t.split(/\s*,\s*/))}return i?.length&&(a(`Access-Control-Allow-Headers`,i.join(`,`)),e.res.headers.append(`Vary`,`Access-Control-Request-Headers`)),e.res.headers.delete(`Content-Length`),e.res.headers.delete(`Content-Type`),new Response(null,{headers:e.res.headers,status:204,statusText:`No Content`})}await i(),(t.origin!==`*`||t.credentials)&&e.header(`Vary`,`Origin`,{append:!0})}};function a(a){let{cubes:o,drizzle:s,schema:c,extractSecurityContext:l,engineType:u,cors:d,basePath:f=`/cubejs-api/v1`,cache:p,mcp:m={enabled:!0},agent:h}=a;if(!a.semanticLayer&&(!o||o.length===0))throw Error(`Either semanticLayer or a non-empty cubes array must be provided`);let g=new r.Hono;d&&g.use(`/*`,i(d));let _=a.semanticLayer??new t.t({drizzle:s,schema:c,engineType:u,cache:p,rlsSetup:a.rlsSetup});if(!a.semanticLayer&&o&&o.forEach(e=>{_.registerCube(e)}),g.post(`${f}/load`,async t=>{try{let n=await t.req.json(),r=n.query||n,i=await l(t),a=_.validateQuery(r);if(!a.isValid)return t.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=t.req.header(`x-cache-control`)===`no-cache`,s=await _.executeMultiCubeQuery(r,i,{skipCache:o});return t.json(e.r(r,s,_))}catch(e){return console.error(`Query execution error:`,e),t.json({error:e instanceof Error?e.message:`Query execution failed`},500)}}),g.get(`${f}/load`,async t=>{try{let n=t.req.query(`query`);if(!n)return t.json({error:`Query parameter is required`},400);let r;try{r=JSON.parse(n)}catch{return t.json({error:`Invalid JSON in query parameter`},400)}let i=await l(t),a=_.validateQuery(r);if(!a.isValid)return t.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=t.req.header(`x-cache-control`)===`no-cache`,s=await _.executeMultiCubeQuery(r,i,{skipCache:o});return t.json(e.r(r,s,_))}catch(e){return console.error(`Query execution error:`,e),t.json({error:e instanceof Error?e.message:`Query execution failed`},500)}}),g.post(`${f}/batch`,async t=>{try{let{queries:n}=await t.req.json();if(!n||!Array.isArray(n))return t.json({error:`Request body must contain a "queries" array`},400);if(n.length===0)return t.json({error:`Queries array cannot be empty`},400);let r=await e.u(n,await l(t),_,{skipCache:t.req.header(`x-cache-control`)===`no-cache`});return t.json(r)}catch(e){return console.error(`Batch execution error:`,e),t.json({error:e instanceof Error?e.message:`Batch execution failed`},500)}}),g.get(`${f}/meta`,t=>{try{let n=_.getMetadata();return t.json(e.a(n))}catch(e){return console.error(`Metadata error:`,e),t.json({error:e instanceof Error?e.message:`Failed to fetch metadata`},500)}}),g.post(`${f}/sql`,async t=>{try{let n=await t.req.json(),r=await l(t),i=_.validateQuery(n);if(!i.isValid)return t.json({error:`Query validation failed: ${i.errors.join(`, `)}`},400);let a=n.measures?.[0]||n.dimensions?.[0];if(!a)return t.json({error:`No measures or dimensions specified`},400);let o=a.split(`.`)[0],s=await _.generateSQL(o,n,r);return t.json(e.o(n,s))}catch(e){return console.error(`SQL generation error:`,e),t.json({error:e instanceof Error?e.message:`SQL generation failed`},500)}}),g.get(`${f}/sql`,async t=>{try{let n=t.req.query(`query`);if(!n)return t.json({error:`Query parameter is required`},400);let r=JSON.parse(n),i=await l(t),a=_.validateQuery(r);if(!a.isValid)return t.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=r.measures?.[0]||r.dimensions?.[0];if(!o)return t.json({error:`No measures or dimensions specified`},400);let s=o.split(`.`)[0],c=await _.generateSQL(s,r,i);return t.json(e.o(r,c))}catch(e){return console.error(`SQL generation error:`,e),t.json({error:e instanceof Error?e.message:`SQL generation failed`},500)}}),g.post(`${f}/dry-run`,async t=>{try{let n=await t.req.json(),r=await e.f(n.query||n,await l(t),_);return t.json(r)}catch(e){return console.error(`Dry-run error:`,e),t.json({error:e instanceof Error?e.message:`Dry-run validation failed`,valid:!1},400)}}),g.get(`${f}/dry-run`,async t=>{try{let n=t.req.query(`query`);if(!n)return t.json({error:`Query parameter is required`,valid:!1},400);let r=await e.f(JSON.parse(n),await l(t),_);return t.json(r)}catch(e){return console.error(`Dry-run error:`,e),t.json({error:e instanceof Error?e.message:`Dry-run validation failed`,valid:!1},400)}}),g.post(`${f}/explain`,async e=>{try{let t=await e.req.json(),n=t.query||t,r=t.options||{},i=await l(e),a=_.validateQuery(n);if(!a.isValid)return e.json({error:`Query validation failed: ${a.errors.join(`, `)}`},400);let o=await _.explainQuery(n,i,r);return e.json(o)}catch(t){return console.error(`Explain error:`,t),e.json({error:t instanceof Error?t.message:`Explain query failed`},500)}}),h&&g.post(`${f}/agent/chat`,async e=>{try{let{handleAgentChat:t}=await Promise.resolve().then(()=>require(`../handler-DumFgnNM.cjs`)),{message:n,sessionId:r,history:i}=await e.req.json();if(!n||typeof n!=`string`)return e.json({error:`message is required and must be a string`},400);let a=(h.apiKey||``).trim();if(h.allowClientApiKey){let t=e.req.header(`x-agent-api-key`);t&&(a=t.trim())}if(!a)return e.json({error:`No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header.`},401);let o=h.allowClientApiKey?e.req.header(`x-agent-provider`):void 0,s=h.allowClientApiKey?e.req.header(`x-agent-model`):void 0,c=h.allowClientApiKey?e.req.header(`x-agent-provider-endpoint`):void 0,u=await l(e),d=h.buildSystemContext?.(u),f=new TextEncoder,p=new ReadableStream({async start(e){try{let l=t({message:n,sessionId:r,history:i,semanticLayer:_,securityContext:u,agentConfig:h,apiKey:a,systemContext:d,providerOverride:o,modelOverride:s,baseURLOverride:c});for await(let t of l){let n=`data: ${JSON.stringify(t)}\n\n`;e.enqueue(f.encode(n))}}catch(t){let n={type:`error`,data:{message:t instanceof Error?t.message:`Stream failed`}};e.enqueue(f.encode(`data: ${JSON.stringify(n)}\n\n`))}finally{e.close()}}});return new Response(p,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}})}catch(t){return console.error(`Agent chat error:`,t),e.json({error:t instanceof Error?t.message:`Agent chat failed`},500)}}),m.enabled!==!1){let e={uri:`drizzle-cube://schema`,name:`Cube Schema`,description:`Current cube metadata as JSON`,mimeType:`application/json`,text:JSON.stringify(_.getMetadata(),null,2)},t=[...n.l(),e],r=n.c(),i=m.basePath??`/mcp`;g.post(`${i}`,async e=>{let i=n._(e.req.header(`origin`),m.allowedOrigins?{allowedOrigins:m.allowedOrigins}:{});if(!i.valid)return e.json(n.i(null,-32600,i.reason),403);let a=e.req.header(`accept`);if(!n.g(a))return e.json(n.i(null,-32600,`Accept header must include both application/json and text/event-stream`),400);let o=n.f(e.req.header());if(!o.ok)return e.json({error:`Unsupported MCP protocol version`,supported:o.supported},426);let s=n.p(await e.req.json().catch(()=>null));if(!s)return e.json(n.i(null,-32600,`Invalid JSON-RPC 2.0 request`),400);let c=n.v(a),u=s.method===`initialize`;try{let i=await n.s(s.method,s.params,{semanticLayer:_,extractSecurityContext:l,rawRequest:e,rawResponse:null,negotiatedProtocol:o.negotiated,resources:t,prompts:r,appEnabled:!!m.app});if(n.d(s))return e.body(null,202);let a=n.a(s.id??null,i),d=u&&i&&typeof i==`object`&&`sessionId`in i?i.sessionId:void 0,f={};if(d&&(f[n.r]=d),c){let e=new TextEncoder,t=n.m(),r=new ReadableStream({start(r){r.enqueue(e.encode(`id: ${t}\n\n`)),r.enqueue(e.encode(n.h(a,t))),r.close()}});return new Response(r,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`,...f}})}return e.json(a,200,f)}catch(t){if(n.d(s))return console.error(`MCP notification processing error:`,t),e.body(null,202);console.error(`MCP RPC error:`,t);let r=t?.code??-32603,i=t?.data,a=t.message||`MCP request failed`,o=n.i(s.id??null,r,a,i);if(c){let e=new TextEncoder,t=n.m(),r=new ReadableStream({start(r){r.enqueue(e.encode(`id: ${t}\n\n`)),r.enqueue(e.encode(n.h(o,t))),r.close()}});return new Response(r,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}})}return e.json(o,200)}}),g.delete(`${i}`,e=>e.json({error:`Session termination not supported`},405)),g.get(`${i}`,e=>{let t=new TextEncoder,r=n.m(),i,a=new ReadableStream({start(e){e.enqueue(t.encode(n.h({jsonrpc:`2.0`,method:`mcp/ready`,params:{protocol:`streamable-http`}},r,15e3))),i=setInterval(()=>{e.enqueue(t.encode(`: keep-alive
2
2
 
3
3
  `))},15e3)},cancel(){clearInterval(i)}});return new Response(a,{status:200,headers:{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}})})}return g}function o(e,t){let n=a(t);return e.route(`/`,n),e}function s(e){return o(new r.Hono,e)}exports.createCubeApp=s,exports.createCubeRoutes=a,exports.mountCubeRoutes=o;
@@ -1,6 +1,6 @@
1
- import { a as e, f as t, o as n, r, u as i } from "../utils-C7Nrw9Wb.js";
2
- import { t as a } from "../compiler-O3T1u7jl.js";
3
- import { _ as o, a as s, c, d as l, f as u, g as d, h as f, i as p, l as m, m as h, p as g, r as _, s as v, v as y } from "../mcp-transport-Cim_5cBN.js";
1
+ import { a as e, f as t, o as n, r, u as i } from "../utils-IH1ePsBd.js";
2
+ import { t as a } from "../compiler-BXfrvebp.js";
3
+ import { _ as o, a as s, c, d as l, f as u, g as d, h as f, i as p, l as m, m as h, p as g, r as _, s as v, v as y } from "../mcp-transport-BhPqU0Ft.js";
4
4
  import { Hono as b } from "hono";
5
5
  //#region node_modules/hono/dist/middleware/cors/index.js
6
6
  var x = (e) => {
@@ -160,7 +160,7 @@ function S(S) {
160
160
  }
161
161
  }), M && N.post(`${k}/agent/chat`, async (e) => {
162
162
  try {
163
- let { handleAgentChat: t } = await import("../handler-CjVc3ytc.js"), { message: n, sessionId: r, history: i } = await e.req.json();
163
+ let { handleAgentChat: t } = await import("../handler-RItnSaEl.js"), { message: n, sessionId: r, history: i } = await e.req.json();
164
164
  if (!n || typeof n != "string") return e.json({ error: "message is required and must be a string" }, 400);
165
165
  let a = (M.apiKey || "").trim();
166
166
  if (M.allowClientApiKey) {
@@ -0,0 +1,377 @@
1
+ //#region src/server/ai/query-schema.ts
2
+ var e = {
3
+ measures: {
4
+ type: "array",
5
+ items: {
6
+ type: "string",
7
+ pattern: "^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$"
8
+ },
9
+ description: "Aggregation measures — EXACTLY \"CubeName.measureName\" (two parts, one dot). Copy field names verbatim from discover results. WRONG: \"Sales.Sales.count\" (double-prefixed). RIGHT: \"Sales.count\"."
10
+ },
11
+ dimensions: {
12
+ type: "array",
13
+ items: {
14
+ type: "string",
15
+ pattern: "^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$"
16
+ },
17
+ description: "Grouping dimensions — EXACTLY \"CubeName.dimensionName\" (two parts, one dot). Copy from discover results. Can include dimensions from RELATED cubes via joins. WRONG: \"Teams.Teams.name\". RIGHT: \"Teams.name\"."
18
+ },
19
+ filters: {
20
+ type: "array",
21
+ items: {
22
+ type: "object",
23
+ properties: {
24
+ member: {
25
+ type: "string",
26
+ description: "\"CubeName.fieldName\""
27
+ },
28
+ operator: {
29
+ type: "string",
30
+ enum: /* @__PURE__ */ "equals.notEquals.contains.notContains.startsWith.notStartsWith.endsWith.notEndsWith.gt.gte.lt.lte.between.notBetween.in.notIn.like.notLike.ilike.regex.notRegex.set.notSet.isEmpty.isNotEmpty.inDateRange.beforeDate.afterDate.arrayContains.arrayOverlaps.arrayContained".split(".")
31
+ },
32
+ values: {
33
+ type: "array",
34
+ items: {},
35
+ description: "Filter values. Omit for set/notSet/isEmpty/isNotEmpty."
36
+ }
37
+ },
38
+ required: ["member", "operator"]
39
+ },
40
+ description: "Filter conditions. Flat array — for AND/OR logic use { \"and\": [...] } or { \"or\": [...] } wrappers."
41
+ },
42
+ timeDimensions: {
43
+ type: "array",
44
+ items: {
45
+ type: "object",
46
+ properties: {
47
+ dimension: {
48
+ type: "string",
49
+ description: "\"CubeName.timeDimension\""
50
+ },
51
+ granularity: {
52
+ type: "string",
53
+ enum: [
54
+ "second",
55
+ "minute",
56
+ "hour",
57
+ "day",
58
+ "week",
59
+ "month",
60
+ "quarter",
61
+ "year"
62
+ ],
63
+ description: "Time bucket size. REQUIRED for time series; omit only for date range filtering."
64
+ },
65
+ dateRange: { description: "Relative string (\"last 7 days\", \"this month\", \"last quarter\") or absolute tuple [\"YYYY-MM-DD\", \"YYYY-MM-DD\"]" },
66
+ fillMissingDates: {
67
+ type: "boolean",
68
+ description: "Fill gaps in time series with fillMissingDatesValue (default: true). Requires granularity + dateRange."
69
+ },
70
+ compareDateRange: {
71
+ type: "array",
72
+ items: {},
73
+ description: "Period-over-period comparison. Array of date ranges: [\"last 30 days\", [\"2024-01-01\", \"2024-01-30\"]]"
74
+ }
75
+ },
76
+ required: ["dimension"]
77
+ },
78
+ description: "Time dimensions with optional granularity for time series. Use filters with inDateRange for aggregated totals instead."
79
+ },
80
+ order: {
81
+ type: "object",
82
+ description: "Sort order. Keys MUST be a measure or dimension already in this query, in \"CubeName.fieldName\" format. Values: \"asc\" or \"desc\". Example: {\"Sales.revenue\": \"desc\"}"
83
+ },
84
+ limit: {
85
+ type: "number",
86
+ description: "Maximum rows to return"
87
+ },
88
+ offset: {
89
+ type: "number",
90
+ description: "Number of rows to skip (for pagination)"
91
+ },
92
+ ungrouped: {
93
+ type: "boolean",
94
+ description: "When true, returns raw row-level data without GROUP BY. Requires at least one dimension. Incompatible with count/countDistinct measures and analysis modes."
95
+ },
96
+ funnel: {
97
+ type: "object",
98
+ properties: {
99
+ bindingKey: {
100
+ type: "string",
101
+ description: "Entity identifier dimension (e.g., \"Events.userId\")"
102
+ },
103
+ timeDimension: {
104
+ type: "string",
105
+ description: "Time ordering dimension (e.g., \"Events.timestamp\")"
106
+ },
107
+ steps: {
108
+ type: "array",
109
+ items: {
110
+ type: "object",
111
+ properties: {
112
+ name: {
113
+ type: "string",
114
+ description: "Human-readable step name"
115
+ },
116
+ filter: { description: "Filter or array of filters for this step" },
117
+ timeToConvert: {
118
+ type: "string",
119
+ description: "ISO 8601 duration — max time from previous step (e.g., \"P7D\" for 7 days, \"PT1H\" for 1 hour)"
120
+ }
121
+ },
122
+ required: ["name"]
123
+ },
124
+ description: "Ordered funnel steps (minimum 2). Put inDateRange time filter ONLY on step 0."
125
+ },
126
+ includeTimeMetrics: {
127
+ type: "boolean",
128
+ description: "Include avg/median/p90 time-to-convert per step"
129
+ },
130
+ globalTimeWindow: {
131
+ type: "string",
132
+ description: "ISO 8601 duration — all steps must complete within this window from step 0"
133
+ }
134
+ },
135
+ required: [
136
+ "bindingKey",
137
+ "timeDimension",
138
+ "steps"
139
+ ],
140
+ description: "Funnel analysis. When provided, measures/dimensions are ignored."
141
+ },
142
+ flow: {
143
+ type: "object",
144
+ properties: {
145
+ bindingKey: {
146
+ type: "string",
147
+ description: "Entity identifier dimension (e.g., \"Events.userId\")"
148
+ },
149
+ timeDimension: {
150
+ type: "string",
151
+ description: "Time ordering dimension (e.g., \"Events.timestamp\")"
152
+ },
153
+ eventDimension: {
154
+ type: "string",
155
+ description: "Dimension whose values become node labels (e.g., \"Events.eventType\")"
156
+ },
157
+ startingStep: {
158
+ type: "object",
159
+ properties: {
160
+ name: {
161
+ type: "string",
162
+ description: "Display name for the starting step"
163
+ },
164
+ filter: { description: "Filter(s) identifying the starting event" }
165
+ },
166
+ required: ["name"],
167
+ description: "The anchor point — an object with { name, filter }, NOT a plain string."
168
+ },
169
+ stepsBefore: {
170
+ type: "number",
171
+ description: "Steps to explore before starting step (0-5)"
172
+ },
173
+ stepsAfter: {
174
+ type: "number",
175
+ description: "Steps to explore after starting step (0-5)"
176
+ },
177
+ entityLimit: {
178
+ type: "number",
179
+ description: "Max entities to process (performance tuning)"
180
+ },
181
+ outputMode: {
182
+ type: "string",
183
+ enum: ["sankey", "sunburst"],
184
+ description: "Visualization mode (default: sankey)"
185
+ }
186
+ },
187
+ required: [
188
+ "bindingKey",
189
+ "timeDimension",
190
+ "eventDimension",
191
+ "startingStep"
192
+ ],
193
+ description: "Flow (path) analysis. When provided, measures/dimensions are ignored."
194
+ },
195
+ retention: {
196
+ type: "object",
197
+ properties: {
198
+ timeDimension: {
199
+ type: "string",
200
+ description: "Timestamp dimension (e.g., \"Events.timestamp\")"
201
+ },
202
+ bindingKey: {
203
+ type: "string",
204
+ description: "Entity identifier (e.g., \"Events.userId\")"
205
+ },
206
+ dateRange: {
207
+ type: "object",
208
+ properties: {
209
+ start: {
210
+ type: "string",
211
+ description: "YYYY-MM-DD"
212
+ },
213
+ end: {
214
+ type: "string",
215
+ description: "YYYY-MM-DD"
216
+ }
217
+ },
218
+ required: ["start", "end"],
219
+ description: "Cohort date range — MUST be an object { start, end }, NOT an array or string."
220
+ },
221
+ granularity: {
222
+ type: "string",
223
+ enum: [
224
+ "day",
225
+ "week",
226
+ "month"
227
+ ],
228
+ description: "Period size for retention buckets"
229
+ },
230
+ periods: {
231
+ type: "number",
232
+ description: "Number of retention periods to calculate"
233
+ },
234
+ retentionType: {
235
+ type: "string",
236
+ enum: ["classic", "rolling"],
237
+ description: "classic = returned in period N exactly; rolling = returned in period N or later"
238
+ },
239
+ cohortFilters: { description: "Optional filters on cohort entry events" },
240
+ activityFilters: { description: "Optional filters on return activity events" },
241
+ breakdownDimensions: {
242
+ type: "array",
243
+ items: { type: "string" },
244
+ description: "Segment retention by these dimensions (e.g., [\"Events.country\"])"
245
+ }
246
+ },
247
+ required: [
248
+ "timeDimension",
249
+ "bindingKey",
250
+ "dateRange",
251
+ "granularity",
252
+ "periods"
253
+ ],
254
+ description: "Retention (cohort) analysis. When provided, measures/dimensions are ignored."
255
+ }
256
+ }, t = "// === DRIZZLE CUBE QUERY LANGUAGE (TypeScript DSL) ===\n\ntype RegularQuery = {\n measures?: string[] // \"CubeName.measureName\" — aggregations\n dimensions?: string[] // \"CubeName.dimensionName\" — groupings (can cross cubes via joins)\n filters?: (FilterCondition | LogicalFilter)[]\n timeDimensions?: TimeDimension[]\n order?: Record<string, 'asc' | 'desc'> // keys MUST be in measures or dimensions\n limit?: number\n offset?: number\n ungrouped?: boolean // raw rows without GROUP BY\n fillMissingDatesValue?: number | null\n}\n\ntype FilterCondition = {\n member: string // \"CubeName.fieldName\"\n operator: FilterOperator\n values?: any[] // omit for set/notSet/isEmpty/isNotEmpty\n}\n\ntype LogicalFilter = { and: Filter[] } | { or: Filter[] }\n\ntype FilterOperator =\n // String\n | 'equals' | 'notEquals' | 'contains' | 'notContains'\n | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith'\n | 'like' | 'notLike' | 'ilike' | 'regex' | 'notRegex'\n // Numeric\n | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'notBetween'\n // Set membership\n | 'in' | 'notIn'\n // Null/empty\n | 'set' | 'notSet' | 'isEmpty' | 'isNotEmpty'\n // Date\n | 'inDateRange' | 'beforeDate' | 'afterDate'\n // Array (PostgreSQL)\n | 'arrayContains' | 'arrayOverlaps' | 'arrayContained'\n\ntype TimeDimension = {\n dimension: string // \"CubeName.timeDimension\"\n granularity?: Granularity // REQUIRED for time series; omit for date-range-only filtering\n dateRange?: string | [string, string] // \"last 7 days\" | [\"2024-01-01\", \"2024-03-31\"]\n fillMissingDates?: boolean // gap-fill (requires granularity + dateRange)\n compareDateRange?: (string | [string, string])[] // period-over-period\n}\n\ntype Granularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'\n\n// --- Analysis Modes (mutually exclusive with measures/dimensions) ---\n\ntype FunnelQuery = {\n funnel: {\n bindingKey: string // \"Events.userId\"\n timeDimension: string // \"Events.timestamp\"\n steps: FunnelStep[] // min 2; put inDateRange filter ONLY on step 0\n includeTimeMetrics?: boolean\n globalTimeWindow?: string // ISO 8601 duration, e.g. \"P30D\"\n }\n}\ntype FunnelStep = {\n name: string\n filter?: Filter | Filter[]\n timeToConvert?: string // ISO 8601 duration, e.g. \"P7D\", \"PT1H\"\n}\n\ntype FlowQuery = {\n flow: {\n bindingKey: string // \"Events.userId\"\n timeDimension: string // \"Events.timestamp\"\n eventDimension: string // \"Events.eventType\" — values become node labels\n startingStep: { // OBJECT, not a string!\n name: string\n filter?: Filter | Filter[]\n }\n stepsBefore: number // 0-5\n stepsAfter: number // 0-5\n entityLimit?: number\n outputMode?: 'sankey' | 'sunburst'\n }\n}\n\ntype RetentionQuery = {\n retention: {\n timeDimension: string // \"Events.timestamp\"\n bindingKey: string // \"Events.userId\"\n dateRange: { // OBJECT with start/end, NOT array/string\n start: string // \"YYYY-MM-DD\"\n end: string // \"YYYY-MM-DD\"\n }\n granularity: 'day' | 'week' | 'month'\n periods: number\n retentionType: 'classic' | 'rolling'\n cohortFilters?: Filter | Filter[]\n activityFilters?: Filter | Filter[]\n breakdownDimensions?: string[]\n }\n}\n\n// --- Rules ---\n// 1. Fields are EXACTLY \"CubeName.fieldName\" (two parts, one dot). Copy verbatim from discover.\n// WRONG: \"Teams.Teams.name\" (double-prefixed!), \"PullRequests\" (bare cube), \"Teams_count\" (underscore)\n// RIGHT: \"Teams.name\", \"PullRequests.count\"\n// 2. Cross-cube joins: include dimensions from related cubes — the system auto-joins\n// 3. For AGGREGATED TOTALS: use filters with inDateRange (NOT timeDimensions)\n// 4. For TIME SERIES: use timeDimensions WITH granularity\n// 5. timeDimensions WITHOUT granularity = daily grouping (usually wrong)\n// 6. Order keys MUST appear in measures or dimensions of the same query\n// 7. Funnel/flow/retention are mutually exclusive with measures/dimensions\n// 8. Always discover cubes first — never guess field names", n = {
257
+ name: "drizzle-cube-mcp-guide",
258
+ description: "How to use drizzle-cube MCP tools to generate and run queries",
259
+ messages: [{
260
+ role: "user",
261
+ content: {
262
+ type: "text",
263
+ text: [
264
+ "You are an analyst agent using drizzle-cube MCP.",
265
+ "",
266
+ "Workflow:",
267
+ "1) tools/call name=discover {topic|intent} - Find cubes and understand schema",
268
+ "2) Construct your query using the schema from discover (see query language reference)",
269
+ "3) tools/call name=validate {query} - Optional: fix schema issues",
270
+ "4) tools/call name=load {query} - Execute and get results",
271
+ "",
272
+ "CROSS-CUBE JOINS:",
273
+ "The \"joins\" property in discover results shows relationships between cubes.",
274
+ "You can include dimensions from ANY related cube in your query — the system auto-joins.",
275
+ "Example: If Productivity joins to Employees, query:",
276
+ "{ \"measures\": [\"Productivity.totalPullRequests\"], \"dimensions\": [\"Employees.name\"] }",
277
+ "",
278
+ "Do NOT hallucinate cube/field names — always use discover first."
279
+ ].join("\n")
280
+ }
281
+ }]
282
+ }, r = {
283
+ name: "drizzle-cube-query-language",
284
+ description: "CRITICAL: Complete query language reference — types, operators, analysis modes, and rules",
285
+ messages: [{
286
+ role: "user",
287
+ content: {
288
+ type: "text",
289
+ text: t
290
+ }
291
+ }]
292
+ }, i = {
293
+ name: "drizzle-cube-date-filtering",
294
+ description: "CRITICAL: How to correctly filter by date vs group by time period - the #1 source of query mistakes",
295
+ messages: [{
296
+ role: "user",
297
+ content: {
298
+ type: "text",
299
+ text: [
300
+ "# Date Filtering vs Time Grouping",
301
+ "",
302
+ "```",
303
+ "User wants data over a time period?",
304
+ "|- AGGREGATED TOTALS (\"total sales last month\")",
305
+ "| -> filters with inDateRange (NOT timeDimensions)",
306
+ "|",
307
+ "|- TIME SERIES (\"daily sales last month\")",
308
+ "| -> timeDimensions WITH granularity",
309
+ "|",
310
+ "|- BOTH (\"monthly breakdown for last quarter\")",
311
+ " -> filters inDateRange + timeDimensions with granularity",
312
+ "```",
313
+ "",
314
+ "## Aggregated Totals (most common)",
315
+ "When: \"last 3 months\", \"over the past year\", \"in Q1\", \"since January\"",
316
+ "```json",
317
+ "{",
318
+ " \"measures\": [\"Sales.totalRevenue\"],",
319
+ " \"dimensions\": [\"Products.category\"],",
320
+ " \"filters\": [{ \"member\": \"Sales.date\", \"operator\": \"inDateRange\", \"values\": [\"last 3 months\"] }]",
321
+ "}",
322
+ "```",
323
+ "Result: One row per category with TOTAL revenue.",
324
+ "",
325
+ "## Time Series",
326
+ "When: \"by month\", \"per week\", \"daily trend\", \"over time\"",
327
+ "```json",
328
+ "{",
329
+ " \"measures\": [\"Sales.totalRevenue\"],",
330
+ " \"timeDimensions\": [{ \"dimension\": \"Sales.date\", \"dateRange\": \"last 3 months\", \"granularity\": \"month\" }]",
331
+ "}",
332
+ "```",
333
+ "Result: One row per month.",
334
+ "",
335
+ "## Period-over-Period Comparison",
336
+ "Use compareDateRange for side-by-side period analysis:",
337
+ "```json",
338
+ "{",
339
+ " \"measures\": [\"Sales.totalRevenue\"],",
340
+ " \"timeDimensions\": [{",
341
+ " \"dimension\": \"Sales.date\",",
342
+ " \"granularity\": \"day\",",
343
+ " \"compareDateRange\": [\"last 30 days\", [\"2024-01-01\", \"2024-01-30\"]]",
344
+ " }]",
345
+ "}",
346
+ "```",
347
+ "",
348
+ "## WRONG: timeDimensions without granularity",
349
+ "```json",
350
+ "// Returns ~90 rows (daily) instead of aggregates!",
351
+ "{ \"timeDimensions\": [{ \"dimension\": \"Sales.date\", \"dateRange\": \"last 3 months\" }] }",
352
+ "```",
353
+ "",
354
+ "## Date Range Values",
355
+ "- Relative: \"last 7 days\", \"last 3 months\", \"last year\", \"this week\", \"this month\", \"this quarter\", \"next week\", \"next month\"",
356
+ "- Absolute: [\"2024-01-01\", \"2024-03-31\"]",
357
+ "",
358
+ "| User Request | Approach |",
359
+ "|---|---|",
360
+ "| \"total for last 3 months\" | filters + inDateRange |",
361
+ "| \"top 5 last quarter\" | filters + inDateRange + order + limit |",
362
+ "| \"monthly trend\" | timeDimensions + granularity |",
363
+ "| \"daily breakdown last week\" | timeDimensions + dateRange + granularity |",
364
+ "| \"compare this month to last\" | timeDimensions + compareDateRange |"
365
+ ].join("\n")
366
+ }
367
+ }]
368
+ }, a = [
369
+ n,
370
+ r,
371
+ i
372
+ ];
373
+ function o() {
374
+ return a;
375
+ }
376
+ //#endregion
377
+ export { e as a, o as i, n, r, i as t };
@@ -0,0 +1,111 @@
1
+ var e={measures:{type:`array`,items:{type:`string`,pattern:`^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$`},description:`Aggregation measures — EXACTLY "CubeName.measureName" (two parts, one dot). Copy field names verbatim from discover results. WRONG: "Sales.Sales.count" (double-prefixed). RIGHT: "Sales.count".`},dimensions:{type:`array`,items:{type:`string`,pattern:`^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$`},description:`Grouping dimensions — EXACTLY "CubeName.dimensionName" (two parts, one dot). Copy from discover results. Can include dimensions from RELATED cubes via joins. WRONG: "Teams.Teams.name". RIGHT: "Teams.name".`},filters:{type:`array`,items:{type:`object`,properties:{member:{type:`string`,description:`"CubeName.fieldName"`},operator:{type:`string`,enum:`equals.notEquals.contains.notContains.startsWith.notStartsWith.endsWith.notEndsWith.gt.gte.lt.lte.between.notBetween.in.notIn.like.notLike.ilike.regex.notRegex.set.notSet.isEmpty.isNotEmpty.inDateRange.beforeDate.afterDate.arrayContains.arrayOverlaps.arrayContained`.split(`.`)},values:{type:`array`,items:{},description:`Filter values. Omit for set/notSet/isEmpty/isNotEmpty.`}},required:[`member`,`operator`]},description:`Filter conditions. Flat array — for AND/OR logic use { "and": [...] } or { "or": [...] } wrappers.`},timeDimensions:{type:`array`,items:{type:`object`,properties:{dimension:{type:`string`,description:`"CubeName.timeDimension"`},granularity:{type:`string`,enum:[`second`,`minute`,`hour`,`day`,`week`,`month`,`quarter`,`year`],description:`Time bucket size. REQUIRED for time series; omit only for date range filtering.`},dateRange:{description:`Relative string ("last 7 days", "this month", "last quarter") or absolute tuple ["YYYY-MM-DD", "YYYY-MM-DD"]`},fillMissingDates:{type:`boolean`,description:`Fill gaps in time series with fillMissingDatesValue (default: true). Requires granularity + dateRange.`},compareDateRange:{type:`array`,items:{},description:`Period-over-period comparison. Array of date ranges: ["last 30 days", ["2024-01-01", "2024-01-30"]]`}},required:[`dimension`]},description:`Time dimensions with optional granularity for time series. Use filters with inDateRange for aggregated totals instead.`},order:{type:`object`,description:`Sort order. Keys MUST be a measure or dimension already in this query, in "CubeName.fieldName" format. Values: "asc" or "desc". Example: {"Sales.revenue": "desc"}`},limit:{type:`number`,description:`Maximum rows to return`},offset:{type:`number`,description:`Number of rows to skip (for pagination)`},ungrouped:{type:`boolean`,description:`When true, returns raw row-level data without GROUP BY. Requires at least one dimension. Incompatible with count/countDistinct measures and analysis modes.`},funnel:{type:`object`,properties:{bindingKey:{type:`string`,description:`Entity identifier dimension (e.g., "Events.userId")`},timeDimension:{type:`string`,description:`Time ordering dimension (e.g., "Events.timestamp")`},steps:{type:`array`,items:{type:`object`,properties:{name:{type:`string`,description:`Human-readable step name`},filter:{description:`Filter or array of filters for this step`},timeToConvert:{type:`string`,description:`ISO 8601 duration — max time from previous step (e.g., "P7D" for 7 days, "PT1H" for 1 hour)`}},required:[`name`]},description:`Ordered funnel steps (minimum 2). Put inDateRange time filter ONLY on step 0.`},includeTimeMetrics:{type:`boolean`,description:`Include avg/median/p90 time-to-convert per step`},globalTimeWindow:{type:`string`,description:`ISO 8601 duration — all steps must complete within this window from step 0`}},required:[`bindingKey`,`timeDimension`,`steps`],description:`Funnel analysis. When provided, measures/dimensions are ignored.`},flow:{type:`object`,properties:{bindingKey:{type:`string`,description:`Entity identifier dimension (e.g., "Events.userId")`},timeDimension:{type:`string`,description:`Time ordering dimension (e.g., "Events.timestamp")`},eventDimension:{type:`string`,description:`Dimension whose values become node labels (e.g., "Events.eventType")`},startingStep:{type:`object`,properties:{name:{type:`string`,description:`Display name for the starting step`},filter:{description:`Filter(s) identifying the starting event`}},required:[`name`],description:`The anchor point — an object with { name, filter }, NOT a plain string.`},stepsBefore:{type:`number`,description:`Steps to explore before starting step (0-5)`},stepsAfter:{type:`number`,description:`Steps to explore after starting step (0-5)`},entityLimit:{type:`number`,description:`Max entities to process (performance tuning)`},outputMode:{type:`string`,enum:[`sankey`,`sunburst`],description:`Visualization mode (default: sankey)`}},required:[`bindingKey`,`timeDimension`,`eventDimension`,`startingStep`],description:`Flow (path) analysis. When provided, measures/dimensions are ignored.`},retention:{type:`object`,properties:{timeDimension:{type:`string`,description:`Timestamp dimension (e.g., "Events.timestamp")`},bindingKey:{type:`string`,description:`Entity identifier (e.g., "Events.userId")`},dateRange:{type:`object`,properties:{start:{type:`string`,description:`YYYY-MM-DD`},end:{type:`string`,description:`YYYY-MM-DD`}},required:[`start`,`end`],description:`Cohort date range — MUST be an object { start, end }, NOT an array or string.`},granularity:{type:`string`,enum:[`day`,`week`,`month`],description:`Period size for retention buckets`},periods:{type:`number`,description:`Number of retention periods to calculate`},retentionType:{type:`string`,enum:[`classic`,`rolling`],description:`classic = returned in period N exactly; rolling = returned in period N or later`},cohortFilters:{description:`Optional filters on cohort entry events`},activityFilters:{description:`Optional filters on return activity events`},breakdownDimensions:{type:`array`,items:{type:`string`},description:`Segment retention by these dimensions (e.g., ["Events.country"])`}},required:[`timeDimension`,`bindingKey`,`dateRange`,`granularity`,`periods`],description:`Retention (cohort) analysis. When provided, measures/dimensions are ignored.`}},t=`// === DRIZZLE CUBE QUERY LANGUAGE (TypeScript DSL) ===
2
+
3
+ type RegularQuery = {
4
+ measures?: string[] // "CubeName.measureName" — aggregations
5
+ dimensions?: string[] // "CubeName.dimensionName" — groupings (can cross cubes via joins)
6
+ filters?: (FilterCondition | LogicalFilter)[]
7
+ timeDimensions?: TimeDimension[]
8
+ order?: Record<string, 'asc' | 'desc'> // keys MUST be in measures or dimensions
9
+ limit?: number
10
+ offset?: number
11
+ ungrouped?: boolean // raw rows without GROUP BY
12
+ fillMissingDatesValue?: number | null
13
+ }
14
+
15
+ type FilterCondition = {
16
+ member: string // "CubeName.fieldName"
17
+ operator: FilterOperator
18
+ values?: any[] // omit for set/notSet/isEmpty/isNotEmpty
19
+ }
20
+
21
+ type LogicalFilter = { and: Filter[] } | { or: Filter[] }
22
+
23
+ type FilterOperator =
24
+ // String
25
+ | 'equals' | 'notEquals' | 'contains' | 'notContains'
26
+ | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith'
27
+ | 'like' | 'notLike' | 'ilike' | 'regex' | 'notRegex'
28
+ // Numeric
29
+ | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'notBetween'
30
+ // Set membership
31
+ | 'in' | 'notIn'
32
+ // Null/empty
33
+ | 'set' | 'notSet' | 'isEmpty' | 'isNotEmpty'
34
+ // Date
35
+ | 'inDateRange' | 'beforeDate' | 'afterDate'
36
+ // Array (PostgreSQL)
37
+ | 'arrayContains' | 'arrayOverlaps' | 'arrayContained'
38
+
39
+ type TimeDimension = {
40
+ dimension: string // "CubeName.timeDimension"
41
+ granularity?: Granularity // REQUIRED for time series; omit for date-range-only filtering
42
+ dateRange?: string | [string, string] // "last 7 days" | ["2024-01-01", "2024-03-31"]
43
+ fillMissingDates?: boolean // gap-fill (requires granularity + dateRange)
44
+ compareDateRange?: (string | [string, string])[] // period-over-period
45
+ }
46
+
47
+ type Granularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'
48
+
49
+ // --- Analysis Modes (mutually exclusive with measures/dimensions) ---
50
+
51
+ type FunnelQuery = {
52
+ funnel: {
53
+ bindingKey: string // "Events.userId"
54
+ timeDimension: string // "Events.timestamp"
55
+ steps: FunnelStep[] // min 2; put inDateRange filter ONLY on step 0
56
+ includeTimeMetrics?: boolean
57
+ globalTimeWindow?: string // ISO 8601 duration, e.g. "P30D"
58
+ }
59
+ }
60
+ type FunnelStep = {
61
+ name: string
62
+ filter?: Filter | Filter[]
63
+ timeToConvert?: string // ISO 8601 duration, e.g. "P7D", "PT1H"
64
+ }
65
+
66
+ type FlowQuery = {
67
+ flow: {
68
+ bindingKey: string // "Events.userId"
69
+ timeDimension: string // "Events.timestamp"
70
+ eventDimension: string // "Events.eventType" — values become node labels
71
+ startingStep: { // OBJECT, not a string!
72
+ name: string
73
+ filter?: Filter | Filter[]
74
+ }
75
+ stepsBefore: number // 0-5
76
+ stepsAfter: number // 0-5
77
+ entityLimit?: number
78
+ outputMode?: 'sankey' | 'sunburst'
79
+ }
80
+ }
81
+
82
+ type RetentionQuery = {
83
+ retention: {
84
+ timeDimension: string // "Events.timestamp"
85
+ bindingKey: string // "Events.userId"
86
+ dateRange: { // OBJECT with start/end, NOT array/string
87
+ start: string // "YYYY-MM-DD"
88
+ end: string // "YYYY-MM-DD"
89
+ }
90
+ granularity: 'day' | 'week' | 'month'
91
+ periods: number
92
+ retentionType: 'classic' | 'rolling'
93
+ cohortFilters?: Filter | Filter[]
94
+ activityFilters?: Filter | Filter[]
95
+ breakdownDimensions?: string[]
96
+ }
97
+ }
98
+
99
+ // --- Rules ---
100
+ // 1. Fields are EXACTLY "CubeName.fieldName" (two parts, one dot). Copy verbatim from discover.
101
+ // WRONG: "Teams.Teams.name" (double-prefixed!), "PullRequests" (bare cube), "Teams_count" (underscore)
102
+ // RIGHT: "Teams.name", "PullRequests.count"
103
+ // 2. Cross-cube joins: include dimensions from related cubes — the system auto-joins
104
+ // 3. For AGGREGATED TOTALS: use filters with inDateRange (NOT timeDimensions)
105
+ // 4. For TIME SERIES: use timeDimensions WITH granularity
106
+ // 5. timeDimensions WITHOUT granularity = daily grouping (usually wrong)
107
+ // 6. Order keys MUST appear in measures or dimensions of the same query
108
+ // 7. Funnel/flow/retention are mutually exclusive with measures/dimensions
109
+ // 8. Always discover cubes first — never guess field names`,n={name:`drizzle-cube-mcp-guide`,description:`How to use drizzle-cube MCP tools to generate and run queries`,messages:[{role:`user`,content:{type:`text`,text:[`You are an analyst agent using drizzle-cube MCP.`,``,`Workflow:`,`1) tools/call name=discover {topic|intent} - Find cubes and understand schema`,`2) Construct your query using the schema from discover (see query language reference)`,`3) tools/call name=validate {query} - Optional: fix schema issues`,`4) tools/call name=load {query} - Execute and get results`,``,`CROSS-CUBE JOINS:`,`The "joins" property in discover results shows relationships between cubes.`,`You can include dimensions from ANY related cube in your query — the system auto-joins.`,`Example: If Productivity joins to Employees, query:`,`{ "measures": ["Productivity.totalPullRequests"], "dimensions": ["Employees.name"] }`,``,`Do NOT hallucinate cube/field names — always use discover first.`].join(`
110
+ `)}}]},r={name:`drizzle-cube-query-language`,description:`CRITICAL: Complete query language reference — types, operators, analysis modes, and rules`,messages:[{role:`user`,content:{type:`text`,text:t}}]},i={name:`drizzle-cube-date-filtering`,description:`CRITICAL: How to correctly filter by date vs group by time period - the #1 source of query mistakes`,messages:[{role:`user`,content:{type:`text`,text:[`# Date Filtering vs Time Grouping`,``,"```",`User wants data over a time period?`,`|- AGGREGATED TOTALS ("total sales last month")`,`| -> filters with inDateRange (NOT timeDimensions)`,`|`,`|- TIME SERIES ("daily sales last month")`,`| -> timeDimensions WITH granularity`,`|`,`|- BOTH ("monthly breakdown for last quarter")`,` -> filters inDateRange + timeDimensions with granularity`,"```",``,`## Aggregated Totals (most common)`,`When: "last 3 months", "over the past year", "in Q1", "since January"`,"```json",`{`,` "measures": ["Sales.totalRevenue"],`,` "dimensions": ["Products.category"],`,` "filters": [{ "member": "Sales.date", "operator": "inDateRange", "values": ["last 3 months"] }]`,`}`,"```",`Result: One row per category with TOTAL revenue.`,``,`## Time Series`,`When: "by month", "per week", "daily trend", "over time"`,"```json",`{`,` "measures": ["Sales.totalRevenue"],`,` "timeDimensions": [{ "dimension": "Sales.date", "dateRange": "last 3 months", "granularity": "month" }]`,`}`,"```",`Result: One row per month.`,``,`## Period-over-Period Comparison`,`Use compareDateRange for side-by-side period analysis:`,"```json",`{`,` "measures": ["Sales.totalRevenue"],`,` "timeDimensions": [{`,` "dimension": "Sales.date",`,` "granularity": "day",`,` "compareDateRange": ["last 30 days", ["2024-01-01", "2024-01-30"]]`,` }]`,`}`,"```",``,`## WRONG: timeDimensions without granularity`,"```json",`// Returns ~90 rows (daily) instead of aggregates!`,`{ "timeDimensions": [{ "dimension": "Sales.date", "dateRange": "last 3 months" }] }`,"```",``,`## Date Range Values`,`- Relative: "last 7 days", "last 3 months", "last year", "this week", "this month", "this quarter", "next week", "next month"`,`- Absolute: ["2024-01-01", "2024-03-31"]`,``,`| User Request | Approach |`,`|---|---|`,`| "total for last 3 months" | filters + inDateRange |`,`| "top 5 last quarter" | filters + inDateRange + order + limit |`,`| "monthly trend" | timeDimensions + granularity |`,`| "daily breakdown last week" | timeDimensions + dateRange + granularity |`,`| "compare this month to last" | timeDimensions + compareDateRange |`].join(`
111
+ `)}}]},a=[n,r,i];function o(){return a}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./utils-tNZ6Cvzw.cjs`),t=require(`./mcp-transport-DPpBCNea.cjs`);function n(e){return{content:[{type:`text`,text:typeof e==`string`?e:JSON.stringify(e)}],isError:!1}}function r(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}function i(i){let{semanticLayer:o,getSecurityContext:s,toolPrefix:c=`drizzle_cube_`,tools:l=[`discover`,`validate`,`load`],prompts:u=t.c(),resources:d,app:f=!1}=i,p=d??t.l(),m=f?[...p,...a()]:p,h=t.o({appEnabled:f}),g=new Map(h.map(e=>[e.name,e])),_=l.filter(e=>g.has(e)).map(e=>{let t=g.get(e),n={name:`${c}${e}`,description:t.description,inputSchema:t.inputSchema},r=t._meta;return r&&(n._meta=r),n}),v=_.map(e=>e.name),y=new Set;for(let e of l)y.add(e),y.add(`${c}${e}`);function b(e){return y.has(e)}async function x(t,i,a){let u=t.startsWith(c)?t.slice(c.length):t;if(!l.includes(u))return r(`Unknown tool: ${t}`);try{switch(u){case`discover`:return n(await e.d(o,i||{}));case`validate`:{let t=i||{};return t.query?n(await e.h(o,t)):r(`query is required`)}case`load`:{let t=i||{};return t.query?n(await e.p(o,await s(a),t)):r(`query is required`)}default:return r(`Unknown tool: ${t}`)}}catch(e){return r(e)}}return{definitions:_,handle:x,handles:b,prompts:u,resources:m,toolNames:v}}function a(){let e=t.u();return e?[{uri:t.n,name:`Drizzle Cube Visualization`,description:`Interactive chart visualization for query results`,mimeType:t.t,text:e}]:[]}exports.getCubeTools=i;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./utils-CyBt-as9.cjs`),t=require(`./mcp-transport-Kk_HTCeR.cjs`);function n(e){return{content:[{type:`text`,text:typeof e==`string`?e:JSON.stringify(e)}],isError:!1}}function r(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}function i(i){let{semanticLayer:o,getSecurityContext:s,toolPrefix:c=`drizzle_cube_`,tools:l=[`discover`,`validate`,`load`],prompts:u=t.c(),resources:d,app:f=!1}=i,p=d??t.l(),m=f?[...p,...a()]:p,h=t.o({appEnabled:f}),g=new Map(h.map(e=>[e.name,e])),_=l.filter(e=>g.has(e)).map(e=>{let t=g.get(e),n={name:`${c}${e}`,description:t.description,inputSchema:t.inputSchema},r=t._meta;return r&&(n._meta=r),n}),v=_.map(e=>e.name),y=new Set;for(let e of l)y.add(e),y.add(`${c}${e}`);function b(e){return y.has(e)}async function x(t,i,a){let u=t.startsWith(c)?t.slice(c.length):t;if(!l.includes(u))return r(`Unknown tool: ${t}`);try{switch(u){case`discover`:return n(await e.d(o,i||{}));case`validate`:{let t=i||{};return t.query?n(await e.h(o,t)):r(`query is required`)}case`load`:{let t=i||{};return t.query?n(await e.p(o,await s(a),t)):r(`query is required`)}default:return r(`Unknown tool: ${t}`)}}catch(e){return r(e)}}return{definitions:_,handle:x,handles:b,prompts:u,resources:m,toolNames:v}}function a(){let e=t.u();return e?[{uri:t.n,name:`Drizzle Cube Visualization`,description:`Interactive chart visualization for query results`,mimeType:t.t,text:e}]:[]}exports.getCubeTools=i;
@@ -1,5 +1,5 @@
1
- import { d as e, h as t, p as n } from "./utils-C7Nrw9Wb.js";
2
- import { c as r, l as i, n as a, o, t as s, u as c } from "./mcp-transport-Cim_5cBN.js";
1
+ import { d as e, h as t, p as n } from "./utils-IH1ePsBd.js";
2
+ import { c as r, l as i, n as a, o, t as s, u as c } from "./mcp-transport-BhPqU0Ft.js";
3
3
  //#region src/adapters/mcp-tools.ts
4
4
  function l(e) {
5
5
  return {