@hypequery/mcp 0.5.4 → 0.6.0
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/README.md +103 -230
- package/dist/api.type-test.d.ts +2 -0
- package/dist/api.type-test.d.ts.map +1 -0
- package/dist/api.type-test.js +14 -0
- package/dist/bin.js +2 -1
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +65 -0
- package/dist/executor.d.ts +50 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +103 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/protocol-server.d.ts +26 -0
- package/dist/protocol-server.d.ts.map +1 -0
- package/dist/protocol-server.js +48 -0
- package/dist/server.d.ts +15 -44
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +21 -271
- package/dist/stdio.d.ts +8 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +14 -0
- package/dist/tools/args.d.ts +16 -16
- package/dist/tools/args.d.ts.map +1 -1
- package/dist/tools/args.js +9 -8
- package/dist/tools/introspect.d.ts +8 -6
- package/dist/tools/introspect.d.ts.map +1 -1
- package/dist/tools/introspect.js +45 -168
- package/dist/tools/list-datasets.d.ts.map +1 -1
- package/dist/tools/list-datasets.js +2 -8
- package/dist/tools/query-dataset.d.ts.map +1 -1
- package/dist/tools/query-dataset.js +26 -30
- package/dist/tools/query-metric.d.ts.map +1 -1
- package/dist/tools/query-metric.js +27 -31
- package/dist/tools/tool-manifest.d.ts +28 -0
- package/dist/tools/tool-manifest.d.ts.map +1 -0
- package/dist/tools/tool-manifest.js +302 -0
- package/dist/tools/utils/canonical-query-schemas.d.ts +9 -0
- package/dist/tools/utils/canonical-query-schemas.d.ts.map +1 -0
- package/dist/tools/utils/canonical-query-schemas.js +108 -0
- package/dist/tools/utils/execution-budget.d.ts +11 -0
- package/dist/tools/utils/execution-budget.d.ts.map +1 -0
- package/dist/tools/utils/execution-budget.js +71 -0
- package/dist/tools/utils/legacy-agent-catalog.d.ts +19 -0
- package/dist/tools/utils/legacy-agent-catalog.d.ts.map +1 -0
- package/dist/tools/utils/legacy-agent-catalog.js +171 -0
- package/dist/tools/utils/query-limits.d.ts +25 -0
- package/dist/tools/utils/query-limits.d.ts.map +1 -0
- package/dist/tools/utils/query-limits.js +69 -0
- package/dist/tools/utils/query-result.d.ts +4 -0
- package/dist/tools/utils/query-result.d.ts.map +1 -0
- package/dist/tools/utils/query-result.js +19 -0
- package/dist/tools/utils/query-schema.d.ts +8 -0
- package/dist/tools/utils/query-schema.d.ts.map +1 -0
- package/dist/tools/utils/query-schema.js +36 -0
- package/dist/tools/utils/tool-response.d.ts +6 -0
- package/dist/tools/utils/tool-response.d.ts.map +1 -0
- package/dist/tools/utils/tool-response.js +33 -0
- package/dist/types.d.ts +54 -58
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -0
- package/dist/utils/tenant-config.d.ts +3 -0
- package/dist/utils/tenant-config.d.ts.map +1 -0
- package/dist/utils/tenant-config.js +23 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/package.json +23 -5
package/README.md
CHANGED
|
@@ -1,293 +1,166 @@
|
|
|
1
1
|
# @hypequery/mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A governed ClickHouse MCP server for AI agents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`@hypequery/mcp` turns your hypequery datasets and metrics into Model Context Protocol tools for Claude, Cursor, and other MCP clients. Agents can discover and query approved analytics without receiving unrestricted SQL access or database credentials.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Natural Language**: AI-friendly prompts and responses
|
|
9
|
-
- **Type-Safe**: Full TypeScript support with the Hypequery semantic layer
|
|
10
|
-
- **ClickHouse Native**: Optimized for ClickHouse analytics workloads
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
7
|
+
## Install
|
|
13
8
|
|
|
14
9
|
```bash
|
|
15
|
-
npm install @hypequery/mcp
|
|
16
|
-
# or
|
|
17
|
-
pnpm add @hypequery/mcp
|
|
10
|
+
npm install @hypequery/mcp @hypequery/datasets @hypequery/clickhouse
|
|
18
11
|
```
|
|
19
12
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
### 1. Create an MCP Config File
|
|
13
|
+
## Expose your semantic layer
|
|
23
14
|
|
|
24
|
-
|
|
15
|
+
```ts
|
|
16
|
+
// mcp-config.ts
|
|
17
|
+
import { publishDatasets } from '@hypequery/datasets';
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
import { OrdersDataset, CustomersDataset } from './datasets/index.js';
|
|
30
|
-
|
|
31
|
-
const revenue = OrdersDataset.metric('revenue', { measure: 'revenue' });
|
|
32
|
-
const customerCount = CustomersDataset.metric('customerCount', {
|
|
33
|
-
measure: 'customerCount',
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Export your datasets
|
|
37
|
-
export const datasets = {
|
|
38
|
-
orders: {
|
|
39
|
-
...OrdersDataset,
|
|
40
|
-
metrics: { revenue },
|
|
41
|
-
},
|
|
42
|
-
customers: {
|
|
43
|
-
...CustomersDataset,
|
|
44
|
-
metrics: { customerCount },
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// Export the semantic runner consumed by the MCP server
|
|
49
|
-
const db = createQueryBuilder({
|
|
50
|
-
url: process.env.CLICKHOUSE_URL,
|
|
51
|
-
username: process.env.CLICKHOUSE_USER,
|
|
52
|
-
password: process.env.CLICKHOUSE_PASSWORD,
|
|
53
|
-
database: process.env.CLICKHOUSE_DATABASE,
|
|
54
|
-
});
|
|
19
|
+
export const datasets = publishDatasets()
|
|
20
|
+
.publish(Orders, { metrics: { revenue } })
|
|
21
|
+
.build();
|
|
55
22
|
|
|
56
23
|
export const analytics = createDatasetClient({ queryBuilder: db });
|
|
57
24
|
```
|
|
58
25
|
|
|
59
|
-
|
|
26
|
+
Compile the config, then start the stdio server:
|
|
60
27
|
|
|
61
28
|
```bash
|
|
62
|
-
npx hypequery-mcp --config
|
|
29
|
+
npx hypequery-mcp --config /absolute/path/to/mcp-config.js
|
|
63
30
|
```
|
|
64
31
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
32
|
+
Add it to an MCP client:
|
|
68
33
|
|
|
69
34
|
```json
|
|
70
35
|
{
|
|
71
36
|
"mcpServers": {
|
|
72
|
-
"hypequery": {
|
|
37
|
+
"hypequery-clickhouse": {
|
|
73
38
|
"command": "npx",
|
|
74
|
-
"args": [
|
|
39
|
+
"args": [
|
|
40
|
+
"hypequery-mcp",
|
|
41
|
+
"--config",
|
|
42
|
+
"/absolute/path/to/mcp-config.js"
|
|
43
|
+
]
|
|
75
44
|
}
|
|
76
45
|
}
|
|
77
46
|
}
|
|
78
47
|
```
|
|
79
48
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
Now you can ask Claude to query your data:
|
|
83
|
-
|
|
84
|
-
> "Show me revenue by region for the last month"
|
|
85
|
-
|
|
86
|
-
> "What are the top 10 customers by order count?"
|
|
87
|
-
|
|
88
|
-
> "List all available datasets"
|
|
89
|
-
|
|
90
|
-
## Available Tools
|
|
91
|
-
|
|
92
|
-
### `list_datasets`
|
|
93
|
-
|
|
94
|
-
Lists all available datasets with their descriptions.
|
|
95
|
-
|
|
96
|
-
**Example:**
|
|
97
|
-
```typescript
|
|
98
|
-
{
|
|
99
|
-
"name": "list_datasets"
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**Response:**
|
|
104
|
-
```json
|
|
105
|
-
{
|
|
106
|
-
"datasets": [
|
|
107
|
-
{
|
|
108
|
-
"name": "orders",
|
|
109
|
-
"description": "Customer orders and revenue data",
|
|
110
|
-
"dimensionCount": 5,
|
|
111
|
-
"measureCount": 4,
|
|
112
|
-
"metricCount": 4
|
|
113
|
-
}
|
|
114
|
-
],
|
|
115
|
-
"total": 1
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### `get_dataset_schema`
|
|
120
|
-
|
|
121
|
-
Gets the complete schema for a dataset.
|
|
122
|
-
|
|
123
|
-
**Example:**
|
|
124
|
-
```typescript
|
|
125
|
-
{
|
|
126
|
-
"name": "get_dataset_schema",
|
|
127
|
-
"arguments": {
|
|
128
|
-
"dataset": "orders"
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
```
|
|
49
|
+
Now an agent can ask, “Show revenue by region for the last month,” using the same metric definition as your backend and dashboard.
|
|
132
50
|
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
{
|
|
136
|
-
"name": "orders",
|
|
137
|
-
"dimensions": {
|
|
138
|
-
"region": { "type": "string", "label": "Region" },
|
|
139
|
-
"status": { "type": "string", "label": "Order Status" }
|
|
140
|
-
},
|
|
141
|
-
"measures": {
|
|
142
|
-
"revenue": { "aggregation": "sum", "field": "amount", "label": "Revenue" },
|
|
143
|
-
"orderCount": { "aggregation": "count", "field": "id", "label": "Order Count" }
|
|
144
|
-
},
|
|
145
|
-
"metrics": {
|
|
146
|
-
"totalRevenue": { "type": "metric", "aggregation": "revenue", "label": "Total Revenue" }
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
```
|
|
51
|
+
## Tools agents receive
|
|
150
52
|
|
|
151
|
-
|
|
53
|
+
- `list_datasets` discovers available analytics models;
|
|
54
|
+
- `get_dataset_schema` explains dimensions, measures, metrics, and relationships;
|
|
55
|
+
- `query_metric` executes named KPIs;
|
|
56
|
+
- `query_dataset` explores the fields you chose to publish.
|
|
152
57
|
|
|
153
|
-
|
|
58
|
+
Every tool declares an output schema and returns the same result twice: MCP
|
|
59
|
+
`structuredContent` for clients that support typed results, and compact JSON in
|
|
60
|
+
the text content block for compatibility. Query metadata includes row count,
|
|
61
|
+
timing when available, pagination state, and cache outcome. Tools also advertise
|
|
62
|
+
human-readable titles and read-only, non-destructive, idempotent annotations.
|
|
154
63
|
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
{
|
|
158
|
-
"name": "query_metric",
|
|
159
|
-
"arguments": {
|
|
160
|
-
"dataset": "orders",
|
|
161
|
-
"metric": "revenue",
|
|
162
|
-
"dimensions": ["region"],
|
|
163
|
-
"filters": [
|
|
164
|
-
{ "field": "status", "operator": "eq", "value": "completed" }
|
|
165
|
-
],
|
|
166
|
-
"grain": "month",
|
|
167
|
-
"orderBy": [
|
|
168
|
-
{ "field": "revenue", "direction": "desc" }
|
|
169
|
-
],
|
|
170
|
-
"limit": 10
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
```
|
|
64
|
+
Tool failures use a stable structured envelope:
|
|
174
65
|
|
|
175
|
-
**Response:**
|
|
176
66
|
```json
|
|
177
67
|
{
|
|
178
|
-
"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
"
|
|
184
|
-
"timingMs": 45,
|
|
185
|
-
"rowCount": 2
|
|
68
|
+
"error": {
|
|
69
|
+
"code": "MCP_INVALID_ARGUMENTS",
|
|
70
|
+
"category": "correctable_input",
|
|
71
|
+
"message": "Invalid query_dataset arguments: ...",
|
|
72
|
+
"retryable": false,
|
|
73
|
+
"correctable": true
|
|
186
74
|
}
|
|
187
75
|
}
|
|
188
76
|
```
|
|
189
77
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
Executes an ad-hoc dataset query with custom dimensions and measures.
|
|
193
|
-
|
|
194
|
-
**Example:**
|
|
195
|
-
```typescript
|
|
196
|
-
{
|
|
197
|
-
"name": "query_dataset",
|
|
198
|
-
"arguments": {
|
|
199
|
-
"dataset": "orders",
|
|
200
|
-
"dimensions": ["region", "status"],
|
|
201
|
-
"measures": ["revenue", "orderCount"],
|
|
202
|
-
"limit": 100
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
```
|
|
78
|
+
Unclassified backend failures are redacted to `MCP_EXECUTION_FAILED`; database
|
|
79
|
+
error details and physical SQL are not copied into agent-facing errors.
|
|
206
80
|
|
|
207
|
-
##
|
|
81
|
+
## Safer than raw SQL access
|
|
208
82
|
|
|
209
|
-
|
|
83
|
+
- Tool schemas come from your TypeScript semantic layer.
|
|
84
|
+
- Advertised schemas and runtime validation use the same canonical catalog
|
|
85
|
+
compiler and deterministic manifest hash.
|
|
86
|
+
- Filters, fields, ordering, and limits are validated.
|
|
87
|
+
- ClickHouse credentials remain in the server process.
|
|
88
|
+
- SQL text stays hidden by default.
|
|
89
|
+
- Tenant identity comes from trusted host configuration, never the prompt.
|
|
210
90
|
|
|
211
|
-
|
|
212
|
-
import { createMCPServer } from '@hypequery/mcp';
|
|
213
|
-
import { createDatasetClient } from '@hypequery/datasets';
|
|
214
|
-
import { datasets } from './datasets/index.js';
|
|
91
|
+
For tenant-scoped datasets, run the server programmatically with the trusted tenant ID:
|
|
215
92
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
username: process.env.CLICKHOUSE_USER,
|
|
219
|
-
password: process.env.CLICKHOUSE_PASSWORD,
|
|
220
|
-
database: process.env.CLICKHOUSE_DATABASE,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
const server = await createMCPServer({
|
|
93
|
+
```ts
|
|
94
|
+
await createMCPServer({
|
|
224
95
|
datasets,
|
|
225
96
|
analytics,
|
|
226
|
-
name: '
|
|
97
|
+
name: 'acme-analytics',
|
|
227
98
|
version: '1.0.0',
|
|
99
|
+
tenantId: session.accountId,
|
|
100
|
+
queryLimits: {
|
|
101
|
+
defaultResultSize: 100,
|
|
102
|
+
maxResultSize: 1_000,
|
|
103
|
+
maxOffset: 10_000,
|
|
104
|
+
},
|
|
105
|
+
executionBudget: {
|
|
106
|
+
timeoutMs: 30_000,
|
|
107
|
+
maxResponseBytes: 1_048_576,
|
|
108
|
+
},
|
|
228
109
|
});
|
|
229
|
-
|
|
230
|
-
// Server is now running via stdio transport
|
|
231
110
|
```
|
|
232
111
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
- `gt`: Greater than
|
|
238
|
-
- `gte`: Greater than or equal to
|
|
239
|
-
- `lt`: Less than
|
|
240
|
-
- `lte`: Less than or equal to
|
|
241
|
-
- `in`: In list
|
|
242
|
-
- `notIn`: Not in list
|
|
243
|
-
- `between`: Between two values
|
|
244
|
-
- `like`: Pattern match (SQL LIKE)
|
|
245
|
-
|
|
246
|
-
## Time Grains
|
|
247
|
-
|
|
248
|
-
- `day`: Daily aggregation
|
|
249
|
-
- `week`: Weekly aggregation
|
|
250
|
-
- `month`: Monthly aggregation
|
|
251
|
-
- `quarter`: Quarterly aggregation
|
|
252
|
-
- `year`: Yearly aggregation
|
|
112
|
+
Every query receives a server-side limit even when the agent omits one. The
|
|
113
|
+
effective ceiling is the lowest applicable server or Dataset limit. Dimensions,
|
|
114
|
+
measures, filters, ordering, and pagination offsets also have hard package
|
|
115
|
+
ceilings that server configuration may lower but cannot raise.
|
|
253
116
|
|
|
254
|
-
|
|
117
|
+
Query calls also have a hard wall-clock deadline and UTF-8 response-byte
|
|
118
|
+
ceiling. Client cancellation and local deadlines propagate through the semantic
|
|
119
|
+
client to the backing ClickHouse request. Budget failures use the stable
|
|
120
|
+
`MCP_REQUEST_CANCELLED`, `MCP_QUERY_TIMEOUT`, and `MCP_RESULT_TOO_LARGE`
|
|
121
|
+
classifications.
|
|
255
122
|
|
|
256
|
-
|
|
123
|
+
## Embed in another MCP transport
|
|
257
124
|
|
|
258
|
-
|
|
125
|
+
The semantic executor is independent of stdio and network lifecycle. A hosted
|
|
126
|
+
gateway can inject its own MCP transport without reimplementing Hypequery's
|
|
127
|
+
tools, prompts, catalog schemas, or validation:
|
|
259
128
|
|
|
260
|
-
|
|
129
|
+
```ts
|
|
130
|
+
import {
|
|
131
|
+
createMCPExecutor,
|
|
132
|
+
createMCPProtocolServer,
|
|
133
|
+
} from '@hypequery/mcp';
|
|
261
134
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
password: process.env.CLICKHOUSE_PASSWORD,
|
|
267
|
-
database: process.env.CLICKHOUSE_DATABASE || 'default',
|
|
135
|
+
const executor = createMCPExecutor({
|
|
136
|
+
datasets,
|
|
137
|
+
analytics,
|
|
138
|
+
tenantId: trustedPrincipal.tenantId,
|
|
268
139
|
});
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
## Troubleshooting
|
|
272
140
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
141
|
+
const server = createMCPProtocolServer({
|
|
142
|
+
executor,
|
|
143
|
+
name: 'acme-hosted-analytics',
|
|
144
|
+
version: '1.0.0',
|
|
145
|
+
});
|
|
278
146
|
|
|
279
|
-
|
|
147
|
+
await server.connect(hostTransport);
|
|
148
|
+
```
|
|
280
149
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
150
|
+
`hostTransport` can be an MCP SDK in-memory, stdio, or hosted transport. This
|
|
151
|
+
package does not create an HTTP endpoint; authentication, routing, and network
|
|
152
|
+
lifecycle remain responsibilities of the host. For an explicit local adapter,
|
|
153
|
+
use `startStdioMCPServer(config)`. The existing `createMCPServer(config)` and
|
|
154
|
+
`HypequeryMCPServer.start()` APIs remain available for compatibility.
|
|
284
155
|
|
|
285
|
-
##
|
|
156
|
+
## Learn more
|
|
286
157
|
|
|
287
|
-
-
|
|
288
|
-
-
|
|
289
|
-
-
|
|
158
|
+
- [ClickHouse MCP overview](https://hypequery.com/clickhouse-mcp)
|
|
159
|
+
- [MCP configuration](https://hypequery.com/docs/mcp/configuration)
|
|
160
|
+
- [MCP tools](https://hypequery.com/docs/mcp/tools)
|
|
161
|
+
- [MCP safety](https://hypequery.com/docs/mcp/safety)
|
|
162
|
+
- [Current capabilities](https://hypequery.com/docs/capabilities)
|
|
290
163
|
|
|
291
164
|
## License
|
|
292
165
|
|
|
293
|
-
Apache-2.0
|
|
166
|
+
Apache-2.0.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.type-test.d.ts","sourceRoot":"","sources":["../src/api.type-test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { expectTypeOf, it } from 'vitest';
|
|
2
|
+
import { HypequeryMCPExecutor, HypequeryMCPProtocolServer, HypequeryMCPServer, MCPToolError, connectMCPServerStdio, createMCPExecutor, createMCPProtocolServer, createMCPServer, startStdioMCPServer, } from './index.js';
|
|
3
|
+
it('exports the transport-neutral and backwards-compatible MCP APIs', () => {
|
|
4
|
+
expectTypeOf(HypequeryMCPExecutor).toBeConstructibleWith({});
|
|
5
|
+
expectTypeOf(HypequeryMCPProtocolServer).toBeConstructibleWith({});
|
|
6
|
+
expectTypeOf(HypequeryMCPServer).toBeConstructibleWith({});
|
|
7
|
+
expectTypeOf(createMCPExecutor).returns.toMatchTypeOf();
|
|
8
|
+
expectTypeOf(createMCPProtocolServer).returns.toMatchTypeOf();
|
|
9
|
+
expectTypeOf(connectMCPServerStdio).returns.toMatchTypeOf();
|
|
10
|
+
expectTypeOf(startStdioMCPServer).returns.toMatchTypeOf();
|
|
11
|
+
expectTypeOf(createMCPServer).returns.toMatchTypeOf();
|
|
12
|
+
expectTypeOf(new MCPToolError('MCP_UNAUTHORIZED', 'Forbidden').code)
|
|
13
|
+
.toMatchTypeOf();
|
|
14
|
+
});
|
package/dist/bin.js
CHANGED
|
@@ -12,6 +12,7 @@ import { createMCPServer } from './server.js';
|
|
|
12
12
|
import { pathToFileURL } from 'url';
|
|
13
13
|
import { resolve } from 'path';
|
|
14
14
|
import { format } from 'util';
|
|
15
|
+
import { MCP_PACKAGE_VERSION } from './version.js';
|
|
15
16
|
function routeConsoleOutputToStderr() {
|
|
16
17
|
const write = (...args) => {
|
|
17
18
|
process.stderr.write(`${format(...args)}\n`);
|
|
@@ -53,7 +54,7 @@ async function main() {
|
|
|
53
54
|
datasets,
|
|
54
55
|
analytics,
|
|
55
56
|
name: 'hypequery-mcp-server',
|
|
56
|
-
version:
|
|
57
|
+
version: MCP_PACKAGE_VERSION,
|
|
57
58
|
});
|
|
58
59
|
// Keep the process running
|
|
59
60
|
process.on('SIGINT', () => {
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type MCPToolErrorCode = 'MCP_INVALID_ARGUMENTS' | 'MCP_NOT_FOUND' | 'MCP_UNKNOWN_TOOL' | 'MCP_UNAUTHORIZED' | 'MCP_STALE_CONTRACT' | 'MCP_REQUEST_CANCELLED' | 'MCP_QUERY_TIMEOUT' | 'MCP_RESULT_TOO_LARGE' | 'MCP_EXECUTION_FAILED';
|
|
2
|
+
export type MCPToolErrorCategory = 'correctable_input' | 'unauthorized' | 'stale_contract' | 'budget' | 'internal';
|
|
3
|
+
/** @deprecated Use MCPToolErrorCode. */
|
|
4
|
+
export type MCPExecutionErrorCode = Extract<MCPToolErrorCode, 'MCP_REQUEST_CANCELLED' | 'MCP_QUERY_TIMEOUT' | 'MCP_RESULT_TOO_LARGE'>;
|
|
5
|
+
export interface MCPErrorDetails {
|
|
6
|
+
code: MCPToolErrorCode;
|
|
7
|
+
category: MCPToolErrorCategory;
|
|
8
|
+
message: string;
|
|
9
|
+
retryable: boolean;
|
|
10
|
+
correctable: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class MCPToolError extends Error {
|
|
13
|
+
readonly code: MCPToolErrorCode;
|
|
14
|
+
readonly category: MCPToolErrorCategory;
|
|
15
|
+
readonly retryable: boolean;
|
|
16
|
+
readonly correctable: boolean;
|
|
17
|
+
constructor(code: MCPToolErrorCode, message: string, options?: {
|
|
18
|
+
category?: MCPToolErrorCategory;
|
|
19
|
+
retryable?: boolean;
|
|
20
|
+
correctable?: boolean;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export declare class MCPExecutionBudgetError extends MCPToolError {
|
|
24
|
+
readonly code: MCPExecutionErrorCode;
|
|
25
|
+
constructor(code: MCPExecutionErrorCode, message: string);
|
|
26
|
+
}
|
|
27
|
+
export declare function classifyMCPToolError(error: unknown): MCPErrorDetails;
|
|
28
|
+
export declare function formatMCPToolError(error: unknown): string;
|
|
29
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,uBAAuB,GACvB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GACpB,uBAAuB,GACvB,mBAAmB,GACnB,sBAAsB,GACtB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,oBAAoB,GAC5B,mBAAmB,GACnB,cAAc,GACd,gBAAgB,GAChB,QAAQ,GACR,UAAU,CAAC;AAEf,wCAAwC;AACxC,MAAM,MAAM,qBAAqB,GAAG,OAAO,CACzC,gBAAgB,EAChB,uBAAuB,GAAG,mBAAmB,GAAG,sBAAsB,CACvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;CACtB;AAsBD,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;gBAG5B,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QACP,QAAQ,CAAC,EAAE,oBAAoB,CAAC;QAChC,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;KAClB;CAUT;AAED,qBAAa,uBAAwB,SAAQ,YAAY;IACvD,SAAiB,IAAI,EAAE,qBAAqB,CAAC;gBAEjC,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM;CAOzD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAkBpE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGzD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function defaultErrorMetadata(code) {
|
|
2
|
+
switch (code) {
|
|
3
|
+
case 'MCP_INVALID_ARGUMENTS':
|
|
4
|
+
case 'MCP_NOT_FOUND':
|
|
5
|
+
case 'MCP_UNKNOWN_TOOL':
|
|
6
|
+
return { category: 'correctable_input', retryable: false, correctable: true };
|
|
7
|
+
case 'MCP_UNAUTHORIZED':
|
|
8
|
+
return { category: 'unauthorized', retryable: false, correctable: false };
|
|
9
|
+
case 'MCP_STALE_CONTRACT':
|
|
10
|
+
return { category: 'stale_contract', retryable: true, correctable: false };
|
|
11
|
+
case 'MCP_REQUEST_CANCELLED':
|
|
12
|
+
case 'MCP_RESULT_TOO_LARGE':
|
|
13
|
+
return { category: 'budget', retryable: false, correctable: false };
|
|
14
|
+
case 'MCP_QUERY_TIMEOUT':
|
|
15
|
+
return { category: 'budget', retryable: true, correctable: false };
|
|
16
|
+
case 'MCP_EXECUTION_FAILED':
|
|
17
|
+
return { category: 'internal', retryable: true, correctable: false };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class MCPToolError extends Error {
|
|
21
|
+
code;
|
|
22
|
+
category;
|
|
23
|
+
retryable;
|
|
24
|
+
correctable;
|
|
25
|
+
constructor(code, message, options = {}) {
|
|
26
|
+
super(message);
|
|
27
|
+
const defaults = defaultErrorMetadata(code);
|
|
28
|
+
this.name = 'MCPToolError';
|
|
29
|
+
this.code = code;
|
|
30
|
+
this.category = options.category ?? defaults.category;
|
|
31
|
+
this.retryable = options.retryable ?? defaults.retryable;
|
|
32
|
+
this.correctable = options.correctable ?? defaults.correctable;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class MCPExecutionBudgetError extends MCPToolError {
|
|
36
|
+
constructor(code, message) {
|
|
37
|
+
super(code, message, {
|
|
38
|
+
category: 'budget',
|
|
39
|
+
retryable: code === 'MCP_QUERY_TIMEOUT',
|
|
40
|
+
});
|
|
41
|
+
this.name = 'MCPExecutionBudgetError';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function classifyMCPToolError(error) {
|
|
45
|
+
if (error instanceof MCPToolError) {
|
|
46
|
+
return {
|
|
47
|
+
code: error.code,
|
|
48
|
+
category: error.category,
|
|
49
|
+
message: error.message,
|
|
50
|
+
retryable: error.retryable,
|
|
51
|
+
correctable: error.correctable,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
code: 'MCP_EXECUTION_FAILED',
|
|
56
|
+
category: 'internal',
|
|
57
|
+
message: 'Query execution failed',
|
|
58
|
+
retryable: true,
|
|
59
|
+
correctable: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function formatMCPToolError(error) {
|
|
63
|
+
const classified = classifyMCPToolError(error);
|
|
64
|
+
return `Error [${classified.code}]: ${classified.message}`;
|
|
65
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CallToolResult, GetPromptResult, ListPromptsResult, ListToolsResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { DatasetClient } from '@hypequery/datasets';
|
|
3
|
+
import type { DatasetRegistry, MCPExecutionBudget, MCPQueryLimits } from './types.js';
|
|
4
|
+
export interface MCPExecutorConfig {
|
|
5
|
+
/** Dataset registry - map of dataset names to instances. */
|
|
6
|
+
datasets: DatasetRegistry;
|
|
7
|
+
/** Semantic analytics client for running metric and dataset queries. */
|
|
8
|
+
analytics: DatasetClient;
|
|
9
|
+
/** Trusted tenant id used to scope tenant-keyed datasets. */
|
|
10
|
+
tenantId?: string;
|
|
11
|
+
/** Include generated SQL in trusted-debug responses. Defaults to false. */
|
|
12
|
+
includeSql?: boolean;
|
|
13
|
+
/** Server-side query ceilings applied in addition to Dataset limits. */
|
|
14
|
+
queryLimits?: MCPQueryLimits;
|
|
15
|
+
/** Query deadline and serialized-result byte ceilings. */
|
|
16
|
+
executionBudget?: MCPExecutionBudget;
|
|
17
|
+
}
|
|
18
|
+
export interface MCPServerConfig extends MCPExecutorConfig {
|
|
19
|
+
/** Server name shown to MCP clients. */
|
|
20
|
+
name?: string;
|
|
21
|
+
/** Server version shown to MCP clients. */
|
|
22
|
+
version?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface MCPToolExecutor {
|
|
25
|
+
listTools(): Promise<ListToolsResult>;
|
|
26
|
+
callTool(name: string, args?: Record<string, unknown>, signal?: AbortSignal): Promise<CallToolResult>;
|
|
27
|
+
listPrompts(): Promise<ListPromptsResult>;
|
|
28
|
+
getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>;
|
|
29
|
+
getManifestHash(): string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Transport-neutral Hypequery MCP tool and prompt executor.
|
|
33
|
+
*
|
|
34
|
+
* This class owns semantic discovery and execution, but has no network or stdio
|
|
35
|
+
* lifecycle. It can be called directly or injected into any MCP transport
|
|
36
|
+
* adapter.
|
|
37
|
+
*/
|
|
38
|
+
export declare class HypequeryMCPExecutor implements MCPToolExecutor {
|
|
39
|
+
private readonly config;
|
|
40
|
+
private readonly querySchemas;
|
|
41
|
+
private readonly executionBudget;
|
|
42
|
+
constructor(config: MCPExecutorConfig);
|
|
43
|
+
getManifestHash(): string;
|
|
44
|
+
listTools(): Promise<ListToolsResult>;
|
|
45
|
+
callTool(name: string, args?: Record<string, unknown>, signal?: AbortSignal): Promise<CallToolResult>;
|
|
46
|
+
listPrompts(): Promise<ListPromptsResult>;
|
|
47
|
+
getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>;
|
|
48
|
+
}
|
|
49
|
+
export declare function createMCPExecutor(config: MCPExecutorConfig): HypequeryMCPExecutor;
|
|
50
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,eAAe,EAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAEV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAOtF,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,QAAQ,EAAE,eAAe,CAAC;IAC1B,wEAAwE;IACxE,SAAS,EAAE,aAAa,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IACtC,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3B,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACjF,eAAe,IAAI,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAI9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgC;IAC7D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;gBAE9B,MAAM,EAAE,iBAAiB;IAOtD,eAAe,IAAI,MAAM;IAInB,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAIrC,QAAQ,CACZ,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC;IAwDpB,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAkBzC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;CAOvF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,oBAAoB,CAEjF"}
|