@hypequery/mcp 0.0.0-canary-20260603161950
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.
- package/LICENSE +201 -0
- package/README.md +285 -0
- package/dist/bin.d.ts +12 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +72 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/prompts/dataset-guide.d.ts +16 -0
- package/dist/prompts/dataset-guide.d.ts.map +1 -0
- package/dist/prompts/dataset-guide.js +130 -0
- package/dist/server.d.ts +45 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +257 -0
- package/dist/tools/introspect.d.ts +9 -0
- package/dist/tools/introspect.d.ts.map +1 -0
- package/dist/tools/introspect.js +76 -0
- package/dist/tools/list-datasets.d.ts +8 -0
- package/dist/tools/list-datasets.d.ts.map +1 -0
- package/dist/tools/list-datasets.js +32 -0
- package/dist/tools/query-dataset.d.ts +9 -0
- package/dist/tools/query-dataset.d.ts.map +1 -0
- package/dist/tools/query-dataset.js +57 -0
- package/dist/tools/query-metric.d.ts +9 -0
- package/dist/tools/query-metric.d.ts.map +1 -0
- package/dist/tools/query-metric.js +62 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/package.json +51 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dataset Guide Prompt
|
|
3
|
+
*
|
|
4
|
+
* Provides natural language guidance for querying datasets.
|
|
5
|
+
*/
|
|
6
|
+
export function datasetGuidePrompt(datasets, datasetName) {
|
|
7
|
+
if (datasetName) {
|
|
8
|
+
const dataset = datasets[datasetName];
|
|
9
|
+
if (!dataset) {
|
|
10
|
+
throw new Error(`Dataset not found: ${datasetName}`);
|
|
11
|
+
}
|
|
12
|
+
const datasetAny = dataset;
|
|
13
|
+
// Generate dataset-specific guide
|
|
14
|
+
const dimensions = datasetAny.dimensions ? Object.keys(datasetAny.dimensions) : [];
|
|
15
|
+
const metrics = datasetAny.metrics ? Object.keys(datasetAny.metrics) : [];
|
|
16
|
+
const guide = `# Querying the ${datasetName} dataset
|
|
17
|
+
|
|
18
|
+
## Available Dimensions
|
|
19
|
+
${dimensions.map((d) => `- ${d}`).join('\n')}
|
|
20
|
+
|
|
21
|
+
## Available Metrics
|
|
22
|
+
${metrics.map((m) => `- ${m}`).join('\n')}
|
|
23
|
+
|
|
24
|
+
## Example Queries
|
|
25
|
+
|
|
26
|
+
### Simple metric query
|
|
27
|
+
\`\`\`
|
|
28
|
+
Query the "${metrics[0] || 'revenue'}" metric
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
### Grouped by dimension
|
|
32
|
+
\`\`\`
|
|
33
|
+
Show "${metrics[0] || 'revenue'}" by "${dimensions[0] || 'region'}"
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
### With filters
|
|
37
|
+
\`\`\`
|
|
38
|
+
Show "${metrics[0] || 'revenue'}" by "${dimensions[0] || 'region'}" where ${dimensions[1] || 'status'} = 'active'
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
### Time-series
|
|
42
|
+
\`\`\`
|
|
43
|
+
Show "${metrics[0] || 'revenue'}" by month for the last year
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
## Tips
|
|
47
|
+
- Use natural language to describe what you want to see
|
|
48
|
+
- The system will translate your query into the appropriate dataset query
|
|
49
|
+
- You can filter, group, and aggregate data using the available dimensions and metrics
|
|
50
|
+
`;
|
|
51
|
+
return {
|
|
52
|
+
messages: [
|
|
53
|
+
{
|
|
54
|
+
role: 'user',
|
|
55
|
+
content: {
|
|
56
|
+
type: 'text',
|
|
57
|
+
text: guide,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Generate general guide for all datasets
|
|
64
|
+
const datasetList = Object.keys(datasets);
|
|
65
|
+
const guide = `# Hypequery Semantic Layer Guide
|
|
66
|
+
|
|
67
|
+
## Available Datasets
|
|
68
|
+
${datasetList.map((name) => `- ${name}`).join('\n')}
|
|
69
|
+
|
|
70
|
+
## How to Query
|
|
71
|
+
|
|
72
|
+
1. **List datasets**: Use the \`list_datasets\` tool to see all available datasets
|
|
73
|
+
2. **Get schema**: Use the \`get_dataset_schema\` tool to see dimensions and metrics for a dataset
|
|
74
|
+
3. **Query metric**: Use the \`query_metric\` tool to execute a pre-defined metric
|
|
75
|
+
4. **Query dataset**: Use the \`query_dataset\` tool for ad-hoc queries with custom dimensions and metrics
|
|
76
|
+
|
|
77
|
+
## Example Workflow
|
|
78
|
+
|
|
79
|
+
1. First, explore available datasets:
|
|
80
|
+
\`\`\`
|
|
81
|
+
list_datasets()
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
2. Get the schema for a specific dataset:
|
|
85
|
+
\`\`\`
|
|
86
|
+
get_dataset_schema({ dataset: "orders" })
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
3. Query a metric:
|
|
90
|
+
\`\`\`
|
|
91
|
+
query_metric({
|
|
92
|
+
dataset: "orders",
|
|
93
|
+
metric: "revenue",
|
|
94
|
+
dimensions: ["region"],
|
|
95
|
+
filters: [{ field: "status", operator: "eq", value: "completed" }],
|
|
96
|
+
grain: "month"
|
|
97
|
+
})
|
|
98
|
+
\`\`\`
|
|
99
|
+
|
|
100
|
+
## Filter Operators
|
|
101
|
+
- \`eq\`: Equal to
|
|
102
|
+
- \`neq\`: Not equal to
|
|
103
|
+
- \`gt\`: Greater than
|
|
104
|
+
- \`gte\`: Greater than or equal to
|
|
105
|
+
- \`lt\`: Less than
|
|
106
|
+
- \`lte\`: Less than or equal to
|
|
107
|
+
- \`in\`: In list
|
|
108
|
+
- \`notIn\`: Not in list
|
|
109
|
+
- \`between\`: Between two values
|
|
110
|
+
- \`like\`: Pattern match
|
|
111
|
+
|
|
112
|
+
## Time Grains
|
|
113
|
+
- \`day\`: Daily aggregation
|
|
114
|
+
- \`week\`: Weekly aggregation
|
|
115
|
+
- \`month\`: Monthly aggregation
|
|
116
|
+
- \`quarter\`: Quarterly aggregation
|
|
117
|
+
- \`year\`: Yearly aggregation
|
|
118
|
+
`;
|
|
119
|
+
return {
|
|
120
|
+
messages: [
|
|
121
|
+
{
|
|
122
|
+
role: 'user',
|
|
123
|
+
content: {
|
|
124
|
+
type: 'text',
|
|
125
|
+
text: guide,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server for Hypequery Semantic Layer
|
|
3
|
+
*
|
|
4
|
+
* Exposes datasets and metrics via Model Context Protocol (MCP)
|
|
5
|
+
* for use with Claude Desktop, Cursor, and other MCP-compatible tools.
|
|
6
|
+
*/
|
|
7
|
+
import type { DatasetClient } from '@hypequery/datasets';
|
|
8
|
+
import type { DatasetRegistry } from './types.js';
|
|
9
|
+
export interface MCPServerConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Dataset registry - map of dataset names to instances
|
|
12
|
+
*/
|
|
13
|
+
datasets: DatasetRegistry;
|
|
14
|
+
/**
|
|
15
|
+
* Semantic analytics for running metric and dataset queries
|
|
16
|
+
*/
|
|
17
|
+
analytics: DatasetClient;
|
|
18
|
+
/**
|
|
19
|
+
* Server name (shown in MCP client)
|
|
20
|
+
*/
|
|
21
|
+
name?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Server version
|
|
24
|
+
*/
|
|
25
|
+
version?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare class HypequeryMCPServer {
|
|
28
|
+
private server;
|
|
29
|
+
private config;
|
|
30
|
+
constructor(config: MCPServerConfig);
|
|
31
|
+
private setupHandlers;
|
|
32
|
+
/**
|
|
33
|
+
* Start the MCP server with stdio transport
|
|
34
|
+
*/
|
|
35
|
+
start(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Stop the MCP server
|
|
38
|
+
*/
|
|
39
|
+
stop(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create and start an MCP server
|
|
43
|
+
*/
|
|
44
|
+
export declare function createMCPServer(config: MCPServerConfig): Promise<HypequeryMCPServer>;
|
|
45
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;IAE1B;;OAEG;IACH,SAAS,EAAE,aAAa,CAAC;IAEzB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,eAAe;IAmBnC,OAAO,CAAC,aAAa;IA6NrB;;OAEG;IACG,KAAK;IAQX;;OAEG;IACG,IAAI;CAGX;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAI1F"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server for Hypequery Semantic Layer
|
|
3
|
+
*
|
|
4
|
+
* Exposes datasets and metrics via Model Context Protocol (MCP)
|
|
5
|
+
* for use with Claude Desktop, Cursor, and other MCP-compatible tools.
|
|
6
|
+
*/
|
|
7
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import { listDatasetsTool } from './tools/list-datasets.js';
|
|
11
|
+
import { getDatasetSchemaTool } from './tools/introspect.js';
|
|
12
|
+
import { queryMetricTool } from './tools/query-metric.js';
|
|
13
|
+
import { queryDatasetTool } from './tools/query-dataset.js';
|
|
14
|
+
import { datasetGuidePrompt } from './prompts/dataset-guide.js';
|
|
15
|
+
export class HypequeryMCPServer {
|
|
16
|
+
server;
|
|
17
|
+
config;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.server = new Server({
|
|
21
|
+
name: config.name ?? 'hypequery-mcp-server',
|
|
22
|
+
version: config.version ?? '0.1.0',
|
|
23
|
+
}, {
|
|
24
|
+
capabilities: {
|
|
25
|
+
tools: {},
|
|
26
|
+
prompts: {},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
this.setupHandlers();
|
|
30
|
+
}
|
|
31
|
+
setupHandlers() {
|
|
32
|
+
// List available tools
|
|
33
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
34
|
+
tools: [
|
|
35
|
+
{
|
|
36
|
+
name: 'list_datasets',
|
|
37
|
+
description: 'List all available datasets in the semantic layer',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'get_dataset_schema',
|
|
45
|
+
description: 'Get the schema (dimensions, metrics, relationships) for a specific dataset',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
dataset: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Name of the dataset to introspect',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ['dataset'],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'query_metric',
|
|
59
|
+
description: 'Execute a metric query with optional dimensions, filters, time grain, and sorting',
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
dataset: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Name of the dataset containing the metric',
|
|
66
|
+
},
|
|
67
|
+
metric: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Name of the metric to query',
|
|
70
|
+
},
|
|
71
|
+
dimensions: {
|
|
72
|
+
type: 'array',
|
|
73
|
+
items: { type: 'string' },
|
|
74
|
+
description: 'Dimensions to group by (optional)',
|
|
75
|
+
},
|
|
76
|
+
filters: {
|
|
77
|
+
type: 'array',
|
|
78
|
+
items: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
field: { type: 'string' },
|
|
82
|
+
operator: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn', 'between', 'like']
|
|
85
|
+
},
|
|
86
|
+
value: {},
|
|
87
|
+
},
|
|
88
|
+
required: ['field', 'operator', 'value'],
|
|
89
|
+
},
|
|
90
|
+
description: 'Filters to apply (optional)',
|
|
91
|
+
},
|
|
92
|
+
grain: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
enum: ['day', 'week', 'month', 'quarter', 'year'],
|
|
95
|
+
description: 'Time grain for time-series queries (optional)',
|
|
96
|
+
},
|
|
97
|
+
orderBy: {
|
|
98
|
+
type: 'array',
|
|
99
|
+
items: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
field: { type: 'string' },
|
|
103
|
+
direction: { type: 'string', enum: ['asc', 'desc'] },
|
|
104
|
+
},
|
|
105
|
+
required: ['field', 'direction'],
|
|
106
|
+
},
|
|
107
|
+
description: 'Sort order (optional)',
|
|
108
|
+
},
|
|
109
|
+
limit: {
|
|
110
|
+
type: 'number',
|
|
111
|
+
description: 'Maximum number of rows to return (optional)',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
required: ['dataset', 'metric'],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'query_dataset',
|
|
119
|
+
description: 'Execute an ad-hoc dataset query with custom dimensions and metrics',
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: 'object',
|
|
122
|
+
properties: {
|
|
123
|
+
dataset: {
|
|
124
|
+
type: 'string',
|
|
125
|
+
description: 'Name of the dataset to query',
|
|
126
|
+
},
|
|
127
|
+
dimensions: {
|
|
128
|
+
type: 'array',
|
|
129
|
+
items: { type: 'string' },
|
|
130
|
+
description: 'Dimensions to select',
|
|
131
|
+
},
|
|
132
|
+
metrics: {
|
|
133
|
+
type: 'array',
|
|
134
|
+
items: { type: 'string' },
|
|
135
|
+
description: 'Metrics to calculate',
|
|
136
|
+
},
|
|
137
|
+
filters: {
|
|
138
|
+
type: 'array',
|
|
139
|
+
items: {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
field: { type: 'string' },
|
|
143
|
+
operator: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn', 'between', 'like']
|
|
146
|
+
},
|
|
147
|
+
value: {},
|
|
148
|
+
},
|
|
149
|
+
required: ['field', 'operator', 'value'],
|
|
150
|
+
},
|
|
151
|
+
description: 'Filters to apply (optional)',
|
|
152
|
+
},
|
|
153
|
+
grain: {
|
|
154
|
+
type: 'string',
|
|
155
|
+
enum: ['day', 'week', 'month', 'quarter', 'year'],
|
|
156
|
+
description: 'Time grain for time-series queries (optional)',
|
|
157
|
+
},
|
|
158
|
+
orderBy: {
|
|
159
|
+
type: 'array',
|
|
160
|
+
items: {
|
|
161
|
+
type: 'object',
|
|
162
|
+
properties: {
|
|
163
|
+
field: { type: 'string' },
|
|
164
|
+
direction: { type: 'string', enum: ['asc', 'desc'] },
|
|
165
|
+
},
|
|
166
|
+
required: ['field', 'direction'],
|
|
167
|
+
},
|
|
168
|
+
description: 'Sort order (optional)',
|
|
169
|
+
},
|
|
170
|
+
limit: {
|
|
171
|
+
type: 'number',
|
|
172
|
+
description: 'Maximum number of rows to return (optional)',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
required: ['dataset'],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
}));
|
|
180
|
+
// Handle tool calls
|
|
181
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
182
|
+
const { name, arguments: args } = request.params;
|
|
183
|
+
try {
|
|
184
|
+
switch (name) {
|
|
185
|
+
case 'list_datasets':
|
|
186
|
+
return await listDatasetsTool(this.config.datasets);
|
|
187
|
+
case 'get_dataset_schema':
|
|
188
|
+
return await getDatasetSchemaTool(this.config.datasets, args);
|
|
189
|
+
case 'query_metric':
|
|
190
|
+
return await queryMetricTool(this.config.datasets, this.config.analytics, args);
|
|
191
|
+
case 'query_dataset':
|
|
192
|
+
return await queryDatasetTool(this.config.datasets, this.config.analytics, args);
|
|
193
|
+
default:
|
|
194
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
return {
|
|
199
|
+
content: [
|
|
200
|
+
{
|
|
201
|
+
type: 'text',
|
|
202
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
isError: true,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// List available prompts
|
|
210
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
211
|
+
prompts: [
|
|
212
|
+
{
|
|
213
|
+
name: 'dataset_guide',
|
|
214
|
+
description: 'Guide for querying datasets with natural language',
|
|
215
|
+
arguments: [
|
|
216
|
+
{
|
|
217
|
+
name: 'dataset',
|
|
218
|
+
description: 'Name of the dataset to get guidance for',
|
|
219
|
+
required: false,
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
}));
|
|
225
|
+
// Get prompt content
|
|
226
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
227
|
+
const { name, arguments: args } = request.params;
|
|
228
|
+
if (name === 'dataset_guide') {
|
|
229
|
+
return datasetGuidePrompt(this.config.datasets, args?.dataset);
|
|
230
|
+
}
|
|
231
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Start the MCP server with stdio transport
|
|
236
|
+
*/
|
|
237
|
+
async start() {
|
|
238
|
+
const transport = new StdioServerTransport();
|
|
239
|
+
await this.server.connect(transport);
|
|
240
|
+
// Log to stderr (stdout is used for MCP protocol)
|
|
241
|
+
console.error('Hypequery MCP Server started');
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Stop the MCP server
|
|
245
|
+
*/
|
|
246
|
+
async stop() {
|
|
247
|
+
await this.server.close();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Create and start an MCP server
|
|
252
|
+
*/
|
|
253
|
+
export async function createMCPServer(config) {
|
|
254
|
+
const server = new HypequeryMCPServer(config);
|
|
255
|
+
await server.start();
|
|
256
|
+
return server;
|
|
257
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Dataset Schema Tool
|
|
3
|
+
*
|
|
4
|
+
* Returns the complete schema for a dataset including dimensions, metrics,
|
|
5
|
+
* and relationships.
|
|
6
|
+
*/
|
|
7
|
+
import type { DatasetRegistry, MCPToolResponse } from '../types.js';
|
|
8
|
+
export declare function getDatasetSchemaTool(datasets: DatasetRegistry, args: unknown): Promise<MCPToolResponse>;
|
|
9
|
+
//# sourceMappingURL=introspect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAwB,eAAe,EAAoE,MAAM,aAAa,CAAC;AAE5J,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,eAAe,CAAC,CA6E1B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Dataset Schema Tool
|
|
3
|
+
*
|
|
4
|
+
* Returns the complete schema for a dataset including dimensions, metrics,
|
|
5
|
+
* and relationships.
|
|
6
|
+
*/
|
|
7
|
+
export async function getDatasetSchemaTool(datasets, args) {
|
|
8
|
+
// Parse and validate args
|
|
9
|
+
const validatedArgs = args;
|
|
10
|
+
const datasetName = validatedArgs.dataset;
|
|
11
|
+
if (!datasetName) {
|
|
12
|
+
throw new Error('dataset parameter is required');
|
|
13
|
+
}
|
|
14
|
+
const dataset = datasets[datasetName];
|
|
15
|
+
if (!dataset) {
|
|
16
|
+
throw new Error(`Dataset not found: ${datasetName}`);
|
|
17
|
+
}
|
|
18
|
+
// Build schema response with proper types
|
|
19
|
+
const datasetAny = dataset;
|
|
20
|
+
const schema = {
|
|
21
|
+
name: datasetName,
|
|
22
|
+
description: datasetAny.description || datasetAny.config?.description || '',
|
|
23
|
+
source: datasetAny.source || datasetAny.config?.source || '',
|
|
24
|
+
timeKey: datasetAny.timeKey || datasetAny.config?.timeKey || null,
|
|
25
|
+
tenantKey: datasetAny.tenantKey || datasetAny.config?.tenantKey || null,
|
|
26
|
+
dimensions: {},
|
|
27
|
+
metrics: {},
|
|
28
|
+
relationships: {},
|
|
29
|
+
};
|
|
30
|
+
// Extract dimensions with proper typing
|
|
31
|
+
if (datasetAny.dimensions) {
|
|
32
|
+
for (const [name, dimension] of Object.entries(datasetAny.dimensions)) {
|
|
33
|
+
const dimSchema = {
|
|
34
|
+
type: dimension.fieldType || dimension.type || 'unknown',
|
|
35
|
+
column: dimension.column || name,
|
|
36
|
+
label: dimension.label || name,
|
|
37
|
+
description: dimension.description || '',
|
|
38
|
+
examples: dimension.examples || [],
|
|
39
|
+
};
|
|
40
|
+
schema.dimensions[name] = dimSchema;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Extract metrics with proper typing
|
|
44
|
+
if (datasetAny.metrics) {
|
|
45
|
+
for (const [name, metric] of Object.entries(datasetAny.metrics)) {
|
|
46
|
+
const metSchema = {
|
|
47
|
+
type: metric.spec?.__type || metric.type || 'unknown',
|
|
48
|
+
aggregation: metric.spec?.aggregation || metric.aggregation || metric.type || '',
|
|
49
|
+
label: metric.label || name,
|
|
50
|
+
description: metric.description || '',
|
|
51
|
+
format: metric.format || null,
|
|
52
|
+
};
|
|
53
|
+
schema.metrics[name] = metSchema;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Extract relationships with proper typing
|
|
57
|
+
if (datasetAny.relationships) {
|
|
58
|
+
for (const [name, relationship] of Object.entries(datasetAny.relationships)) {
|
|
59
|
+
const rel = relationship;
|
|
60
|
+
const relSchema = {
|
|
61
|
+
type: rel.type || rel.kind || 'unknown',
|
|
62
|
+
target: typeof rel.target === 'function' ? rel.target()?.name || '' : rel.target || rel.dataset?.name || '',
|
|
63
|
+
description: rel.description || '',
|
|
64
|
+
};
|
|
65
|
+
schema.relationships[name] = relSchema;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: JSON.stringify(schema, null, 2),
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List Datasets Tool
|
|
3
|
+
*
|
|
4
|
+
* Returns a list of all available datasets with their descriptions.
|
|
5
|
+
*/
|
|
6
|
+
import type { DatasetRegistry, MCPToolResponse } from '../types.js';
|
|
7
|
+
export declare function listDatasetsTool(datasets: DatasetRegistry): Promise<MCPToolResponse>;
|
|
8
|
+
//# sourceMappingURL=list-datasets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-datasets.d.ts","sourceRoot":"","sources":["../../src/tools/list-datasets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAyC,MAAM,aAAa,CAAC;AAE3G,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CA6B1F"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List Datasets Tool
|
|
3
|
+
*
|
|
4
|
+
* Returns a list of all available datasets with their descriptions.
|
|
5
|
+
*/
|
|
6
|
+
export async function listDatasetsTool(datasets) {
|
|
7
|
+
const datasetList = Object.entries(datasets).map(([name, dataset]) => {
|
|
8
|
+
const datasetAny = dataset;
|
|
9
|
+
// Try to extract description from dataset instance
|
|
10
|
+
const description = datasetAny.description || datasetAny.config?.description || 'No description available';
|
|
11
|
+
const dimensionCount = datasetAny.dimensions ? Object.keys(datasetAny.dimensions).length : 0;
|
|
12
|
+
const metricCount = datasetAny.metrics ? Object.keys(datasetAny.metrics).length : 0;
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
description,
|
|
16
|
+
dimensionCount,
|
|
17
|
+
metricCount,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
const response = {
|
|
21
|
+
datasets: datasetList,
|
|
22
|
+
total: datasetList.length,
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: JSON.stringify(response, null, 2),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Dataset Tool
|
|
3
|
+
*
|
|
4
|
+
* Executes an ad-hoc dataset query with custom dimensions and metrics.
|
|
5
|
+
*/
|
|
6
|
+
import type { DatasetClient } from '@hypequery/datasets';
|
|
7
|
+
import type { DatasetRegistry, MCPToolResponse } from '../types.js';
|
|
8
|
+
export declare function queryDatasetTool(datasets: DatasetRegistry, analytics: DatasetClient, args: unknown): Promise<MCPToolResponse>;
|
|
9
|
+
//# sourceMappingURL=query-dataset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-dataset.d.ts","sourceRoot":"","sources":["../../src/tools/query-dataset.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAoB,eAAe,EAAwC,MAAM,aAAa,CAAC;AAE5H,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,eAAe,EACzB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,eAAe,CAAC,CA6D1B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Dataset Tool
|
|
3
|
+
*
|
|
4
|
+
* Executes an ad-hoc dataset query with custom dimensions and metrics.
|
|
5
|
+
*/
|
|
6
|
+
export async function queryDatasetTool(datasets, analytics, args) {
|
|
7
|
+
// Parse and validate args
|
|
8
|
+
const validatedArgs = args;
|
|
9
|
+
const { dataset: datasetName, dimensions, metrics, filters, grain, orderBy, limit } = validatedArgs;
|
|
10
|
+
if (!datasetName) {
|
|
11
|
+
throw new Error('dataset parameter is required');
|
|
12
|
+
}
|
|
13
|
+
const dataset = datasets[datasetName];
|
|
14
|
+
if (!dataset) {
|
|
15
|
+
throw new Error(`Dataset not found: ${datasetName}`);
|
|
16
|
+
}
|
|
17
|
+
if (!dimensions?.length && !metrics?.length) {
|
|
18
|
+
throw new Error('At least one dimension or metric must be specified');
|
|
19
|
+
}
|
|
20
|
+
// Build the query with proper types
|
|
21
|
+
const query = {
|
|
22
|
+
dimensions: dimensions || [],
|
|
23
|
+
measures: metrics || [],
|
|
24
|
+
filters: filters || [],
|
|
25
|
+
orderBy: orderBy || [],
|
|
26
|
+
};
|
|
27
|
+
if (grain) {
|
|
28
|
+
query.by = grain;
|
|
29
|
+
}
|
|
30
|
+
// Apply limit with maximum cap
|
|
31
|
+
const MAX_LIMIT = 10000;
|
|
32
|
+
if (limit !== undefined) {
|
|
33
|
+
query.limit = Math.min(limit, MAX_LIMIT);
|
|
34
|
+
}
|
|
35
|
+
const result = await analytics.execute(dataset, query, {
|
|
36
|
+
runtime: {
|
|
37
|
+
tenant: undefined,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
// Format the response with proper types
|
|
41
|
+
const response = {
|
|
42
|
+
data: result.data,
|
|
43
|
+
meta: {
|
|
44
|
+
sql: result.meta?.sql,
|
|
45
|
+
timingMs: result.meta?.timingMs,
|
|
46
|
+
rowCount: result.data.length,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: JSON.stringify(response, null, 2),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Metric Tool
|
|
3
|
+
*
|
|
4
|
+
* Executes a metric query with optional dimensions, filters, grain, and sorting.
|
|
5
|
+
*/
|
|
6
|
+
import type { DatasetClient } from '@hypequery/datasets';
|
|
7
|
+
import type { DatasetRegistry, MCPToolResponse } from '../types.js';
|
|
8
|
+
export declare function queryMetricTool(datasets: DatasetRegistry, analytics: DatasetClient, args: unknown): Promise<MCPToolResponse>;
|
|
9
|
+
//# sourceMappingURL=query-metric.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-metric.d.ts","sourceRoot":"","sources":["../../src/tools/query-metric.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAmB,eAAe,EAAwC,MAAM,aAAa,CAAC;AAE3H,wBAAsB,eAAe,CACnC,QAAQ,EAAE,eAAe,EACzB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,eAAe,CAAC,CAoE1B"}
|