@hypequery/mcp 0.8.6 → 0.10.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 +70 -0
- package/dist/executor.d.ts +3 -3
- package/dist/executor.d.ts.map +1 -1
- package/dist/http-executor.d.ts +40 -0
- package/dist/http-executor.d.ts.map +1 -0
- package/dist/http-executor.js +178 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/protocol-server.js +3 -3
- package/dist/tools/args.d.ts +65 -123
- package/dist/tools/args.d.ts.map +1 -1
- package/dist/tools/args.js +3 -1
- package/dist/tools/query-dataset.d.ts.map +1 -1
- package/dist/tools/query-dataset.js +4 -1
- package/dist/tools/tool-manifest.d.ts +1 -1
- package/dist/tools/tool-manifest.d.ts.map +1 -1
- package/dist/tools/tool-manifest.js +1 -47
- package/dist/tools/utils/canonical-query-schemas.d.ts +10 -2
- package/dist/tools/utils/canonical-query-schemas.d.ts.map +1 -1
- package/dist/tools/utils/canonical-query-schemas.js +10 -3
- package/dist/tools/utils/result-schema.d.ts +5 -0
- package/dist/tools/utils/result-schema.d.ts.map +1 -0
- package/dist/tools/utils/result-schema.js +47 -0
- package/dist/utils/http-client/arguments.d.ts +3 -0
- package/dist/utils/http-client/arguments.d.ts.map +1 -0
- package/dist/utils/http-client/arguments.js +26 -0
- package/dist/utils/http-client/discovery.d.ts +6 -0
- package/dist/utils/http-client/discovery.d.ts.map +1 -0
- package/dist/utils/http-client/discovery.js +24 -0
- package/dist/utils/http-client/json.d.ts +6 -0
- package/dist/utils/http-client/json.d.ts.map +1 -0
- package/dist/utils/http-client/json.js +79 -0
- package/dist/utils/http-client/manifest.d.ts +5 -0
- package/dist/utils/http-client/manifest.d.ts.map +1 -0
- package/dist/utils/http-client/manifest.js +53 -0
- package/dist/utils/http-client/profile.d.ts +5 -0
- package/dist/utils/http-client/profile.d.ts.map +1 -0
- package/dist/utils/http-client/profile.js +29 -0
- package/dist/utils/http-client/result.d.ts +4 -0
- package/dist/utils/http-client/result.d.ts.map +1 -0
- package/dist/utils/http-client/result.js +98 -0
- package/dist/utils/http-client/transport.d.ts +12 -0
- package/dist/utils/http-client/transport.d.ts.map +1 -0
- package/dist/utils/http-client/transport.js +88 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -8
- package/dist/api.type-test.d.ts +0 -2
- package/dist/api.type-test.d.ts.map +0 -1
- package/dist/api.type-test.js +0 -31
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { MCPExecutionBudgetError, MCPToolError } from '../../errors.js';
|
|
2
|
+
export function backendUrl(input) {
|
|
3
|
+
try {
|
|
4
|
+
const url = new URL(input);
|
|
5
|
+
const local = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
6
|
+
if (url.protocol !== 'https:' && !(url.protocol === 'http:' && local)
|
|
7
|
+
|| url.username || url.password || url.hash || url.search)
|
|
8
|
+
throw new Error();
|
|
9
|
+
return url;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error('Use an HTTPS discovery URL (HTTP is allowed only on loopback), without credentials, query or fragment.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function backendRequest(backend, url, signal, body, schemaHash) {
|
|
16
|
+
if (url.origin !== backend.discovery.origin)
|
|
17
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend origin changed.', { retryable: false });
|
|
18
|
+
try {
|
|
19
|
+
signal.throwIfAborted();
|
|
20
|
+
const configured = typeof backend.headers === 'function' ? await backend.headers() : backend.headers;
|
|
21
|
+
const headers = new Headers(configured);
|
|
22
|
+
headers.set('accept', 'application/json');
|
|
23
|
+
if (body)
|
|
24
|
+
headers.set('content-type', 'application/json');
|
|
25
|
+
if (schemaHash)
|
|
26
|
+
headers.set('x-hypequery-client-schema', schemaHash);
|
|
27
|
+
signal.throwIfAborted();
|
|
28
|
+
return await backend.fetchFn(url.href, {
|
|
29
|
+
method: body ? 'POST' : 'GET', headers, body: body ? JSON.stringify(body) : undefined,
|
|
30
|
+
redirect: 'manual', credentials: 'omit', cache: 'no-store', signal,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
if (signal.aborted)
|
|
35
|
+
throw signal.reason;
|
|
36
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend request failed.', { retryable: true });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function discardBackendBody(response) {
|
|
40
|
+
void response.body?.cancel().catch(() => undefined);
|
|
41
|
+
}
|
|
42
|
+
/** Bound decompressed streamed bytes, including when Content-Length is absent or dishonest. */
|
|
43
|
+
export async function backendText(response, maximum, signal) {
|
|
44
|
+
const length = response.headers.get('content-length');
|
|
45
|
+
const type = response.headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase();
|
|
46
|
+
if (type !== 'application/json' && !/^application\/[a-z0-9.+-]+\+json$/.test(type ?? '')) {
|
|
47
|
+
discardBackendBody(response);
|
|
48
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend did not return JSON.', { retryable: false });
|
|
49
|
+
}
|
|
50
|
+
if (length !== null && (!/^\d+$/.test(length) || Number(length) > maximum)) {
|
|
51
|
+
discardBackendBody(response);
|
|
52
|
+
throw new MCPExecutionBudgetError('MCP_RESULT_TOO_LARGE', 'Backend response exceeds its byte limit.');
|
|
53
|
+
}
|
|
54
|
+
const reader = response.body?.getReader();
|
|
55
|
+
if (!reader)
|
|
56
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend returned no response body.', { retryable: false });
|
|
57
|
+
const cancel = () => { void reader.cancel().catch(() => undefined); };
|
|
58
|
+
signal.addEventListener('abort', cancel, { once: true });
|
|
59
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
60
|
+
let size = 0;
|
|
61
|
+
let text = '';
|
|
62
|
+
try {
|
|
63
|
+
signal.throwIfAborted();
|
|
64
|
+
while (true) {
|
|
65
|
+
const chunk = await reader.read();
|
|
66
|
+
signal.throwIfAborted();
|
|
67
|
+
if (chunk.done)
|
|
68
|
+
break;
|
|
69
|
+
size += chunk.value.byteLength;
|
|
70
|
+
if (size > maximum)
|
|
71
|
+
throw new MCPExecutionBudgetError('MCP_RESULT_TOO_LARGE', 'Backend response exceeds its byte limit.');
|
|
72
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
73
|
+
}
|
|
74
|
+
return text + decoder.decode();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
cancel();
|
|
78
|
+
if (signal.aborted)
|
|
79
|
+
throw signal.reason;
|
|
80
|
+
if (error instanceof MCPExecutionBudgetError)
|
|
81
|
+
throw error;
|
|
82
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend response could not be read.', { retryable: false });
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
signal.removeEventListener('abort', cancel);
|
|
86
|
+
reader.releaseLock();
|
|
87
|
+
}
|
|
88
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MCP_PACKAGE_VERSION = "0.
|
|
1
|
+
export declare const MCP_PACKAGE_VERSION = "0.10.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,mBAAmB,WAAW,CAAC"}
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Governed ClickHouse MCP server for semantic metrics, datasets, Claude, Cursor, and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -30,18 +30,24 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
|
-
"dist"
|
|
33
|
+
"dist",
|
|
34
|
+
"!dist/**/*.test.*",
|
|
35
|
+
"!dist/**/*.type-test.*",
|
|
36
|
+
"!dist/**/__tests__/**",
|
|
37
|
+
"!dist/**/tests/**",
|
|
38
|
+
"!dist/**/type-tests/**"
|
|
34
39
|
],
|
|
35
40
|
"dependencies": {
|
|
36
|
-
"@hypequery/datasets": "^0.
|
|
37
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
38
|
-
"zod": "^
|
|
41
|
+
"@hypequery/datasets": "^0.24.0",
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
43
|
+
"zod": "^4.6.6",
|
|
44
|
+
"@hypequery/protocol": "0.21.0"
|
|
39
45
|
},
|
|
40
46
|
"devDependencies": {
|
|
41
47
|
"@types/node": "^20.11.30",
|
|
42
|
-
"typescript": "^
|
|
43
|
-
"vitest": "^5.0.
|
|
44
|
-
"@hypequery/clickhouse": "2.
|
|
48
|
+
"typescript": "^6.0.3",
|
|
49
|
+
"vitest": "^5.0.5",
|
|
50
|
+
"@hypequery/clickhouse": "2.14.0"
|
|
45
51
|
},
|
|
46
52
|
"repository": {
|
|
47
53
|
"type": "git",
|
package/dist/api.type-test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.type-test.d.ts","sourceRoot":"","sources":["../src/api.type-test.ts"],"names":[],"mappings":""}
|
package/dist/api.type-test.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, it } from 'vitest';
|
|
2
|
-
import { dataset, dimension, measure } from '@hypequery/datasets';
|
|
3
|
-
import { HypequeryMCPExecutor, HypequeryMCPProtocolServer, HypequeryMCPServer, MCPToolError, connectMCPServerStdio, createMCPExecutor, createMCPProtocolServer, createMCPServer, startStdioMCPServer, } from './index.js';
|
|
4
|
-
it('exports the transport-neutral and backwards-compatible MCP APIs', () => {
|
|
5
|
-
expectTypeOf(HypequeryMCPExecutor).toBeConstructibleWith({});
|
|
6
|
-
expectTypeOf(HypequeryMCPProtocolServer).toBeConstructibleWith({});
|
|
7
|
-
expectTypeOf(HypequeryMCPServer).toBeConstructibleWith({});
|
|
8
|
-
expectTypeOf(createMCPExecutor).returns.toMatchTypeOf();
|
|
9
|
-
expectTypeOf(createMCPProtocolServer).returns.toMatchTypeOf();
|
|
10
|
-
expectTypeOf(connectMCPServerStdio).returns.toMatchTypeOf();
|
|
11
|
-
expectTypeOf(startStdioMCPServer).returns.toMatchTypeOf();
|
|
12
|
-
expectTypeOf(createMCPServer).returns.toMatchTypeOf();
|
|
13
|
-
expectTypeOf(new MCPToolError('MCP_UNAUTHORIZED', 'Forbidden').code)
|
|
14
|
-
.toMatchTypeOf();
|
|
15
|
-
});
|
|
16
|
-
it('accepts segments in the exported query argument types', () => {
|
|
17
|
-
expectTypeOf().toEqualTypeOf();
|
|
18
|
-
expectTypeOf().toEqualTypeOf();
|
|
19
|
-
const _orders = dataset('orders', {
|
|
20
|
-
source: 'orders',
|
|
21
|
-
dimensions: { status: dimension.string() },
|
|
22
|
-
measures: { revenue: measure.sum('amount') },
|
|
23
|
-
segments: { paid: { filters: [{ field: 'status', operator: 'eq', value: 'paid' }] } },
|
|
24
|
-
});
|
|
25
|
-
expectTypeOf()
|
|
26
|
-
.toEqualTypeOf();
|
|
27
|
-
expectTypeOf()
|
|
28
|
-
.toEqualTypeOf();
|
|
29
|
-
expectTypeOf().toEqualTypeOf();
|
|
30
|
-
expectTypeOf().toEqualTypeOf();
|
|
31
|
-
});
|