@hypequery/mcp 0.9.0 → 0.10.1
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/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 +1 -1
- package/dist/tools/utils/canonical-query-schemas.d.ts.map +1 -1
- package/dist/tools/utils/canonical-query-schemas.js +1 -1
- 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 +12 -6
- 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
package/README.md
CHANGED
|
@@ -48,6 +48,76 @@ Add it to an MCP client:
|
|
|
48
48
|
|
|
49
49
|
Now an agent can ask, “Show revenue by region for the last month,” using the same metric definition as your backend and dashboard.
|
|
50
50
|
|
|
51
|
+
## Connect to a Python HTTP backend
|
|
52
|
+
|
|
53
|
+
An existing Python server can own definitions, authorization, tenancy and SQL.
|
|
54
|
+
Opt its dataset/metric routes into `ClientQueryProfile` and install
|
|
55
|
+
`add_client_contract_endpoint` from `hypequery.serve`. The MCP bridge consumes that
|
|
56
|
+
endpoint's **authorized** view; an offline `build` export cannot be used as live
|
|
57
|
+
discovery. No Node dataset registry or Python application import is needed.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import {
|
|
61
|
+
createMCPHttpExecutor,
|
|
62
|
+
createMCPProtocolServer,
|
|
63
|
+
connectMCPServerStdio,
|
|
64
|
+
} from '@hypequery/mcp';
|
|
65
|
+
|
|
66
|
+
const url = process.env.HYPEQUERY_CLIENT_CONTRACT_URL;
|
|
67
|
+
const token = process.env.HYPEQUERY_API_KEY;
|
|
68
|
+
if (!url || !token) throw new Error('Configure the backend discovery URL and credential.');
|
|
69
|
+
|
|
70
|
+
const executor = await createMCPHttpExecutor({
|
|
71
|
+
url,
|
|
72
|
+
headers: { authorization: `Bearer ${token}` },
|
|
73
|
+
});
|
|
74
|
+
await connectMCPServerStdio(createMCPProtocolServer({ executor }));
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The URL is the full discovery endpoint, including its mount prefix. HTTPS is
|
|
78
|
+
required except for loopback development. URLs cannot contain credentials,
|
|
79
|
+
queries or fragments. Keep one executor/session per principal; a headers resolver
|
|
80
|
+
may rotate credentials for that same principal. Browser code must never contain
|
|
81
|
+
this server credential. Remote MCP hosting/OAuth is a separate transport concern.
|
|
82
|
+
|
|
83
|
+
The four tool names stay the same. HTTP mode preserves each authorized endpoint's
|
|
84
|
+
own dimensions, filters, defaults, time controls and limits. Metric-only groups
|
|
85
|
+
have `queryable: false`; inspecting them does not invent a dataset query route.
|
|
86
|
+
Introspection returns endpoint profiles rather than merging permissions into a
|
|
87
|
+
synthetic dataset. Unsupported segments, SQL/debug options and caller-selected
|
|
88
|
+
tenants, tokens, origins or routes are rejected before backend requests.
|
|
89
|
+
|
|
90
|
+
HTTP query responses preserve the public backend envelope: `data`, plus optional
|
|
91
|
+
`meta` with request ID, timing, row count, pagination and optional `cache.hit`.
|
|
92
|
+
Set `includeMeta: true` when the endpoint advertises metadata and use
|
|
93
|
+
`pagination.hasMore` with `offset + limit` to page. This differs from the existing
|
|
94
|
+
local executor's cache-status metadata; each mode advertises its actual output
|
|
95
|
+
schema. Returned rows must contain exactly the selected fields with their
|
|
96
|
+
advertised scalar/null types. Private diagnostics or unexpected fields fail
|
|
97
|
+
closed instead of being forwarded to the agent.
|
|
98
|
+
|
|
99
|
+
The bridge rechecks its authorized view before discovery, prompts and execution,
|
|
100
|
+
and pins each POST to the validated endpoint schema. View changes or backend 409s
|
|
101
|
+
invalidate the session: construct a new executor/reconnect explicitly. No query
|
|
102
|
+
is automatically replayed with new semantics. Backend authorization is still
|
|
103
|
+
checked on every request, including after discovery.
|
|
104
|
+
|
|
105
|
+
Budgets default to 30 seconds, 256 KiB discovery/manifests and 1 MiB query responses.
|
|
106
|
+
HTTP mode has a hard 1 MiB response ceiling, 1,000 rows/filter-list values, 10,000
|
|
107
|
+
JSON nodes, 1,000 members per JSON collection and nesting depth 16. Query ceilings
|
|
108
|
+
can be lowered through `queryLimits`; unservable default selections are refused
|
|
109
|
+
rather than silently changed. `executionBudget`, `catalogBudget` and
|
|
110
|
+
`maxContractBytes` allow smaller deployment limits. Streamed/decompressed bytes
|
|
111
|
+
are bounded even without Content-Length, and redirects are refused. Request
|
|
112
|
+
cancellation aborts the HTTP operation; the backend must carry disconnection into
|
|
113
|
+
its executor (the Python serve adapter does so). End-to-end ClickHouse cancellation
|
|
114
|
+
remains part of integrated release qualification.
|
|
115
|
+
|
|
116
|
+
A TypeScript consumer of the MCP SDK needs its Fetch/DOM type names available
|
|
117
|
+
(e.g. `lib: ["ES2022", "DOM"]` alongside Node types); this does not require a browser
|
|
118
|
+
runtime. The repository's installed-artifact smoke checks this without
|
|
119
|
+
`skipLibCheck` and without the optional ClickHouse peer installed.
|
|
120
|
+
|
|
51
121
|
## Tools agents receive
|
|
52
122
|
|
|
53
123
|
- `list_datasets` discovers available analytics models;
|
package/dist/executor.d.ts
CHANGED
|
@@ -24,10 +24,10 @@ export interface MCPServerConfig extends MCPExecutorConfig {
|
|
|
24
24
|
version?: string;
|
|
25
25
|
}
|
|
26
26
|
export interface MCPToolExecutor {
|
|
27
|
-
listTools(): Promise<ListToolsResult>;
|
|
27
|
+
listTools(signal?: AbortSignal): Promise<ListToolsResult>;
|
|
28
28
|
callTool(name: string, args?: Record<string, unknown>, signal?: AbortSignal): Promise<CallToolResult>;
|
|
29
|
-
listPrompts(): Promise<ListPromptsResult>;
|
|
30
|
-
getPrompt(name: string, args?: Record<string, string
|
|
29
|
+
listPrompts(signal?: AbortSignal): Promise<ListPromptsResult>;
|
|
30
|
+
getPrompt(name: string, args?: Record<string, string>, signal?: AbortSignal): Promise<GetPromptResult>;
|
|
31
31
|
getManifestHash(): string;
|
|
32
32
|
}
|
|
33
33
|
/**
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +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;AAoB7B,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAOpB,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;IACrC,wEAAwE;IACxE,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC;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,
|
|
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;AAoB7B,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAOpB,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;IACrC,wEAAwE;IACxE,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC;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,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1D,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,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACvG,eAAe,IAAI,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAK9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgC;IAC7D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;gBAE1B,MAAM,EAAE,iBAAiB;IAQtD,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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CallToolResult, GetPromptResult, ListPromptsResult, ListToolsResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { MCPToolExecutor } from './executor.js';
|
|
3
|
+
import type { MCPCatalogBudget, MCPExecutionBudget, MCPQueryLimits } from './types.js';
|
|
4
|
+
import { type BackendHeaders } from './utils/http-client/transport.js';
|
|
5
|
+
export interface MCPHttpExecutorConfig {
|
|
6
|
+
/** Full authorized discovery URL. Backend origin and credentials are host configuration. */
|
|
7
|
+
url: string;
|
|
8
|
+
/** One principal's credentials; a resolver may rotate credentials for that same principal. */
|
|
9
|
+
headers?: BackendHeaders;
|
|
10
|
+
fetchFn?: typeof fetch;
|
|
11
|
+
queryLimits?: MCPQueryLimits;
|
|
12
|
+
executionBudget?: MCPExecutionBudget;
|
|
13
|
+
catalogBudget?: MCPCatalogBudget;
|
|
14
|
+
/** Whole discovery response ceiling, default 256 KiB, hard maximum 1 MiB. */
|
|
15
|
+
maxContractBytes?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Execute client tools through an authorized HTTP backend; Python retains SQL/auth/tenant ownership. */
|
|
18
|
+
export declare class HypequeryMCPHttpExecutor implements MCPToolExecutor {
|
|
19
|
+
private readonly backend;
|
|
20
|
+
private readonly authorizedHash;
|
|
21
|
+
private readonly budget;
|
|
22
|
+
private readonly contractBytes;
|
|
23
|
+
private readonly schemas;
|
|
24
|
+
private readonly manifest;
|
|
25
|
+
private invalidated;
|
|
26
|
+
private constructor();
|
|
27
|
+
static create(config: MCPHttpExecutorConfig, signal?: AbortSignal): Promise<HypequeryMCPHttpExecutor>;
|
|
28
|
+
private static decodeContract;
|
|
29
|
+
private static requireSuccess;
|
|
30
|
+
private assertActive;
|
|
31
|
+
private current;
|
|
32
|
+
getManifestHash(): string;
|
|
33
|
+
listTools(signal?: AbortSignal): Promise<ListToolsResult>;
|
|
34
|
+
private plan;
|
|
35
|
+
callTool(name: string, args?: Record<string, unknown>, signal?: AbortSignal): Promise<CallToolResult>;
|
|
36
|
+
listPrompts(signal?: AbortSignal): Promise<ListPromptsResult>;
|
|
37
|
+
getPrompt(name: string, args?: Record<string, string>, signal?: AbortSignal): Promise<GetPromptResult>;
|
|
38
|
+
}
|
|
39
|
+
export declare function createMCPHttpExecutor(config: MCPHttpExecutorConfig, signal?: AbortSignal): Promise<HypequeryMCPHttpExecutor>;
|
|
40
|
+
//# sourceMappingURL=http-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-executor.d.ts","sourceRoot":"","sources":["../src/http-executor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC9H,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKvF,OAAO,EAA+D,KAAK,cAAc,EAAoB,MAAM,kCAAkC,CAAC;AAOtJ,MAAM,WAAW,qBAAqB;IACpC,4FAA4F;IAC5F,GAAG,EAAE,MAAM,CAAC;IACZ,8FAA8F;IAC9F,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,eAAe,CAAC,EAAE,kBAAkB,CAAC;IACrC,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,yGAAyG;AACzG,qBAAa,wBAAyB,YAAW,eAAe;IAM5D,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,aAAa;IARhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO;WAYM,MAAM,CAAC,MAAM,EAAE,qBAAqB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAiB3G,OAAO,CAAC,MAAM,CAAC,cAAc;IAQ7B,OAAO,CAAC,MAAM,CAAC,cAAc;IAQ7B,OAAO,CAAC,YAAY;YAIN,OAAO;IAarB,eAAe,IAAI,MAAM;IAEnB,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IAK/D,OAAO,CAAC,IAAI;IAWN,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAiCrG,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAK7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;CAS7G;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAE5H"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { buildClientContractQuerySchemas } from '@hypequery/datasets/schemas';
|
|
2
|
+
import { decodeProtocolClientContract, validateProtocolClientQuery } from '@hypequery/protocol';
|
|
3
|
+
import { MCPToolError } from './errors.js';
|
|
4
|
+
import { assertWithinBudget, executeWithinBudget, resolveExecutionBudget } from './tools/utils/execution-budget.js';
|
|
5
|
+
import { resolveQueryLimits } from './tools/utils/query-limits.js';
|
|
6
|
+
import { assertManifestWithinBudget, resolveCatalogBudget } from './tools/utils/catalog-budget.js';
|
|
7
|
+
import { createMCPErrorResponse, createMCPResultTooLargeResponse, createMCPToolResponse } from './tools/utils/tool-response.js';
|
|
8
|
+
import { backendRequest, backendText, backendUrl, discardBackendBody } from './utils/http-client/transport.js';
|
|
9
|
+
import { httpToolArguments } from './utils/http-client/arguments.js';
|
|
10
|
+
import { mcpClientProfile } from './utils/http-client/profile.js';
|
|
11
|
+
import { clientHttpResult } from './utils/http-client/result.js';
|
|
12
|
+
import { clientHttpManifest } from './utils/http-client/manifest.js';
|
|
13
|
+
import { clientDatasetList, clientDatasetNames, clientDatasetProfile } from './utils/http-client/discovery.js';
|
|
14
|
+
/** Execute client tools through an authorized HTTP backend; Python retains SQL/auth/tenant ownership. */
|
|
15
|
+
export class HypequeryMCPHttpExecutor {
|
|
16
|
+
backend;
|
|
17
|
+
authorizedHash;
|
|
18
|
+
budget;
|
|
19
|
+
contractBytes;
|
|
20
|
+
schemas;
|
|
21
|
+
manifest;
|
|
22
|
+
invalidated = false;
|
|
23
|
+
constructor(backend, authorizedHash, budget, contractBytes, contract, config) {
|
|
24
|
+
this.backend = backend;
|
|
25
|
+
this.authorizedHash = authorizedHash;
|
|
26
|
+
this.budget = budget;
|
|
27
|
+
this.contractBytes = contractBytes;
|
|
28
|
+
this.schemas = buildClientContractQuerySchemas(mcpClientProfile(contract, config.queryLimits));
|
|
29
|
+
this.manifest = assertManifestWithinBudget(clientHttpManifest(this.schemas, authorizedHash), resolveCatalogBudget(config.catalogBudget));
|
|
30
|
+
}
|
|
31
|
+
static async create(config, signal) {
|
|
32
|
+
const backend = { discovery: backendUrl(config.url), headers: config.headers, fetchFn: config.fetchFn ?? fetch };
|
|
33
|
+
const configuredBudget = resolveExecutionBudget(config.executionBudget);
|
|
34
|
+
const budget = Object.freeze({ ...configuredBudget, maxResponseBytes: Math.min(configuredBudget.maxResponseBytes, 1_048_576) });
|
|
35
|
+
const maximum = config.maxContractBytes ?? 262_144;
|
|
36
|
+
if (!Number.isSafeInteger(maximum) || maximum < 1 || maximum > 1_048_576)
|
|
37
|
+
throw new Error('maxContractBytes must be an integer between 1 and 1048576.');
|
|
38
|
+
// Validate all host limits before making a request.
|
|
39
|
+
resolveCatalogBudget(config.catalogBudget);
|
|
40
|
+
resolveQueryLimits(undefined, config.queryLimits);
|
|
41
|
+
return executeWithinBudget(async (requestSignal) => {
|
|
42
|
+
const response = await backendRequest(backend, backend.discovery, requestSignal);
|
|
43
|
+
HypequeryMCPHttpExecutor.requireSuccess(response);
|
|
44
|
+
const contract = HypequeryMCPHttpExecutor.decodeContract(await backendText(response, maximum, requestSignal));
|
|
45
|
+
return new HypequeryMCPHttpExecutor(backend, contract.contentHash, budget, maximum, contract, config);
|
|
46
|
+
}, budget, signal);
|
|
47
|
+
}
|
|
48
|
+
static decodeContract(text) {
|
|
49
|
+
try {
|
|
50
|
+
const contract = decodeProtocolClientContract(text);
|
|
51
|
+
if (contract.view !== 'authorized')
|
|
52
|
+
throw new Error();
|
|
53
|
+
return contract;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend returned an invalid authorized client contract.', { retryable: false });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
static requireSuccess(response) {
|
|
60
|
+
if (response.status === 200)
|
|
61
|
+
return;
|
|
62
|
+
discardBackendBody(response);
|
|
63
|
+
if (response.status === 401 || response.status === 403)
|
|
64
|
+
throw new MCPToolError('MCP_UNAUTHORIZED', 'Backend access denied.');
|
|
65
|
+
if (response.status === 409)
|
|
66
|
+
throw new MCPToolError('MCP_STALE_CONTRACT', 'Backend client contract changed; reconnect.', { retryable: false });
|
|
67
|
+
throw new MCPToolError('MCP_EXECUTION_FAILED', 'Backend request was refused.', { retryable: response.status === 429 || response.status >= 500 });
|
|
68
|
+
}
|
|
69
|
+
assertActive() {
|
|
70
|
+
if (this.invalidated)
|
|
71
|
+
throw new MCPToolError('MCP_STALE_CONTRACT', 'Backend client contract changed; reconnect.', { retryable: false });
|
|
72
|
+
}
|
|
73
|
+
async current(signal) {
|
|
74
|
+
this.assertActive();
|
|
75
|
+
const response = await backendRequest(this.backend, this.backend.discovery, signal);
|
|
76
|
+
try {
|
|
77
|
+
HypequeryMCPHttpExecutor.requireSuccess(response);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (response.status === 409)
|
|
81
|
+
this.invalidated = true;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
const contract = HypequeryMCPHttpExecutor.decodeContract(await backendText(response, this.contractBytes, signal));
|
|
85
|
+
if (contract.contentHash !== this.authorizedHash) {
|
|
86
|
+
this.invalidated = true;
|
|
87
|
+
this.assertActive();
|
|
88
|
+
}
|
|
89
|
+
this.assertActive();
|
|
90
|
+
}
|
|
91
|
+
getManifestHash() { return this.schemas.manifestHash; }
|
|
92
|
+
async listTools(signal) {
|
|
93
|
+
await executeWithinBudget(requestSignal => this.current(requestSignal), this.budget, signal);
|
|
94
|
+
return structuredClone(this.manifest);
|
|
95
|
+
}
|
|
96
|
+
plan(name, args) {
|
|
97
|
+
const input = httpToolArguments(args, ['dataset', 'metric', 'dimensions', 'measures', 'filters', 'having', 'grain', 'timezone', 'orderBy', 'limit', 'offset', 'includeMeta']);
|
|
98
|
+
const endpoint = this.schemas.contract.endpoints.find(item => name === 'query_dataset'
|
|
99
|
+
? item.kind === 'dataset' && item.id === input.dataset
|
|
100
|
+
: item.kind === 'metric' && item.dataset === input.dataset && item.id === input.metric);
|
|
101
|
+
if (!endpoint)
|
|
102
|
+
throw new MCPToolError('MCP_INVALID_ARGUMENTS', 'Invalid client query target.');
|
|
103
|
+
try {
|
|
104
|
+
return validateProtocolClientQuery(this.schemas.contract, endpoint.id, input, 'mcp');
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
throw new MCPToolError('MCP_INVALID_ARGUMENTS', 'Invalid client query.');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async callTool(name, args, signal) {
|
|
111
|
+
try {
|
|
112
|
+
this.assertActive();
|
|
113
|
+
let plan;
|
|
114
|
+
let dataset;
|
|
115
|
+
if (name === 'query_dataset' || name === 'query_metric')
|
|
116
|
+
plan = this.plan(name, args);
|
|
117
|
+
else if (name === 'list_datasets')
|
|
118
|
+
httpToolArguments(args, []);
|
|
119
|
+
else if (name === 'get_dataset_schema') {
|
|
120
|
+
const input = httpToolArguments(args, ['dataset']);
|
|
121
|
+
if (typeof input.dataset !== 'string' || !clientDatasetNames(this.schemas.contract).includes(input.dataset))
|
|
122
|
+
throw new MCPToolError('MCP_NOT_FOUND', 'Dataset group not found.');
|
|
123
|
+
dataset = input.dataset;
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
throw new MCPToolError('MCP_UNKNOWN_TOOL', 'Unknown client tool.');
|
|
127
|
+
return await executeWithinBudget(async (requestSignal) => {
|
|
128
|
+
await this.current(requestSignal);
|
|
129
|
+
let value;
|
|
130
|
+
if (plan) {
|
|
131
|
+
const url = new URL(plan.endpoint.path, this.backend.discovery.origin);
|
|
132
|
+
const response = await backendRequest(this.backend, url, requestSignal, plan.body, plan.endpoint.schemaHash);
|
|
133
|
+
try {
|
|
134
|
+
HypequeryMCPHttpExecutor.requireSuccess(response);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (response.status === 409)
|
|
138
|
+
this.invalidated = true;
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
value = clientHttpResult(await backendText(response, this.budget.maxResponseBytes, requestSignal), plan);
|
|
142
|
+
}
|
|
143
|
+
else
|
|
144
|
+
value = dataset === undefined ? clientDatasetList(this.schemas.contract) : clientDatasetProfile(this.schemas.contract, dataset);
|
|
145
|
+
const result = createMCPToolResponse(value);
|
|
146
|
+
assertWithinBudget(result, this.budget);
|
|
147
|
+
return result;
|
|
148
|
+
}, this.budget, signal);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const result = createMCPErrorResponse(error);
|
|
152
|
+
try {
|
|
153
|
+
assertWithinBudget(result, this.budget);
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return createMCPResultTooLargeResponse();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async listPrompts(signal) {
|
|
162
|
+
await executeWithinBudget(requestSignal => this.current(requestSignal), this.budget, signal);
|
|
163
|
+
return { prompts: [{ name: 'dataset_guide', description: 'Guide to the authorized client endpoint profiles', arguments: [{ name: 'dataset', required: false }] }] };
|
|
164
|
+
}
|
|
165
|
+
async getPrompt(name, args, signal) {
|
|
166
|
+
const input = httpToolArguments(args, ['dataset']);
|
|
167
|
+
if (name !== 'dataset_guide' || input.dataset !== undefined && (typeof input.dataset !== 'string' || !clientDatasetNames(this.schemas.contract).includes(input.dataset)))
|
|
168
|
+
throw new MCPToolError('MCP_NOT_FOUND', 'Client guide not found.');
|
|
169
|
+
await executeWithinBudget(requestSignal => this.current(requestSignal), this.budget, signal);
|
|
170
|
+
const profiles = input.dataset === undefined ? clientDatasetList(this.schemas.contract) : clientDatasetProfile(this.schemas.contract, input.dataset);
|
|
171
|
+
const result = { messages: [{ role: 'user', content: { type: 'text', text: 'Use only the authorized endpoints and tool schemas. A metric-only dataset group is not a query_dataset target. Each endpoint retains its own fields, defaults and limits. For pagination, request includeMeta=true when metadata is available and follow pagination.hasMore and offset + limit. Tenant and credentials belong to the backend host. Reconnect explicitly after a stale-contract error.\n' + JSON.stringify(profiles) } }] };
|
|
172
|
+
assertWithinBudget(result, this.budget);
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export function createMCPHttpExecutor(config, signal) {
|
|
177
|
+
return HypequeryMCPHttpExecutor.create(config, signal);
|
|
178
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -18,4 +18,5 @@ export type { DatasetRegistry, QueryMetricArgs, QueryDatasetArgs, QueryToolOptio
|
|
|
18
18
|
export { MCPCatalogBudgetError, MCPExecutionBudgetError, MCPToolError, classifyMCPToolError, formatMCPToolError, type MCPErrorDetails, type MCPExecutionErrorCode, type MCPToolErrorCode, type MCPToolErrorCategory, } from './errors.js';
|
|
19
19
|
export { MAX_QUERY_LIMIT, DEFAULT_QUERY_LIMIT, MAX_QUERY_OFFSET, MAX_QUERY_DIMENSIONS, MAX_QUERY_MEASURES, MAX_QUERY_FILTERS, MAX_QUERY_ORDER_BY, DEFAULT_QUERY_TIMEOUT_MS, MAX_QUERY_TIMEOUT_MS, DEFAULT_RESPONSE_BYTES, MAX_RESPONSE_BYTES, DEFAULT_MANIFEST_TOOLS, MAX_MANIFEST_TOOLS, DEFAULT_MANIFEST_BYTES, MAX_MANIFEST_BYTES, } from './types.js';
|
|
20
20
|
export { MCP_PACKAGE_VERSION } from './version.js';
|
|
21
|
+
export { HypequeryMCPHttpExecutor, createMCPHttpExecutor, type MCPHttpExecutorConfig } from './http-executor.js';
|
|
21
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,wBAAwB,GAC9B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGhE,YAAY,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,wBAAwB,GAC9B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGhE,YAAY,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,3 +17,4 @@ export { datasetGuidePrompt } from './prompts/dataset-guide.js';
|
|
|
17
17
|
export { MCPCatalogBudgetError, MCPExecutionBudgetError, MCPToolError, classifyMCPToolError, formatMCPToolError, } from './errors.js';
|
|
18
18
|
export { MAX_QUERY_LIMIT, DEFAULT_QUERY_LIMIT, MAX_QUERY_OFFSET, MAX_QUERY_DIMENSIONS, MAX_QUERY_MEASURES, MAX_QUERY_FILTERS, MAX_QUERY_ORDER_BY, DEFAULT_QUERY_TIMEOUT_MS, MAX_QUERY_TIMEOUT_MS, DEFAULT_RESPONSE_BYTES, MAX_RESPONSE_BYTES, DEFAULT_MANIFEST_TOOLS, MAX_MANIFEST_TOOLS, DEFAULT_MANIFEST_BYTES, MAX_MANIFEST_BYTES, } from './types.js';
|
|
19
19
|
export { MCP_PACKAGE_VERSION } from './version.js';
|
|
20
|
+
export { HypequeryMCPHttpExecutor, createMCPHttpExecutor } from './http-executor.js';
|
package/dist/protocol-server.js
CHANGED
|
@@ -24,10 +24,10 @@ export class HypequeryMCPProtocolServer {
|
|
|
24
24
|
this.setupHandlers();
|
|
25
25
|
}
|
|
26
26
|
setupHandlers() {
|
|
27
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => (this.executor.listTools()));
|
|
27
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async (_request, extra) => (this.executor.listTools(extra?.signal)));
|
|
28
28
|
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => (this.executor.callTool(request.params.name, request.params.arguments, extra?.signal)));
|
|
29
|
-
this.server.setRequestHandler(ListPromptsRequestSchema, async () => (this.executor.listPrompts()));
|
|
30
|
-
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => (this.executor.getPrompt(request.params.name, request.params.arguments)));
|
|
29
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, async (_request, extra) => (this.executor.listPrompts(extra?.signal)));
|
|
30
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => (this.executor.getPrompt(request.params.name, request.params.arguments, extra?.signal)));
|
|
31
31
|
}
|
|
32
32
|
getManifestHash() {
|
|
33
33
|
return this.executor.getManifestHash();
|
package/dist/tools/args.d.ts
CHANGED
|
@@ -1,146 +1,88 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { type MetricFilter
|
|
2
|
+
import { type MetricFilter } from '@hypequery/datasets';
|
|
3
3
|
export declare const queryMetricArgsSchema: z.ZodObject<{
|
|
4
|
-
dimensions: z.ZodOptional<z.ZodArray<z.ZodString
|
|
4
|
+
dimensions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
5
5
|
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
6
6
|
field: z.ZodString;
|
|
7
|
-
operator: z.ZodEnum<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
operator: z.ZodEnum<{
|
|
8
|
+
eq: "eq";
|
|
9
|
+
neq: "neq";
|
|
10
|
+
gt: "gt";
|
|
11
|
+
gte: "gte";
|
|
12
|
+
lt: "lt";
|
|
13
|
+
lte: "lte";
|
|
14
|
+
in: "in";
|
|
15
|
+
notIn: "notIn";
|
|
16
|
+
between: "between";
|
|
17
|
+
like: "like";
|
|
18
|
+
}>;
|
|
19
|
+
value: z.ZodAny;
|
|
20
|
+
}, z.core.$strict>>>;
|
|
21
|
+
segments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
22
|
+
grain: z.ZodOptional<z.ZodEnum<{
|
|
23
|
+
minute: "minute";
|
|
24
|
+
hour: "hour";
|
|
25
|
+
day: "day";
|
|
26
|
+
week: "week";
|
|
27
|
+
month: "month";
|
|
28
|
+
quarter: "quarter";
|
|
29
|
+
year: "year";
|
|
30
|
+
}>>;
|
|
20
31
|
timezone: z.ZodOptional<z.ZodString>;
|
|
21
32
|
orderBy: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
22
33
|
field: z.ZodString;
|
|
23
|
-
direction: z.ZodEnum<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
},
|
|
28
|
-
field: string;
|
|
29
|
-
direction: "asc" | "desc";
|
|
30
|
-
}>, "many">>;
|
|
34
|
+
direction: z.ZodEnum<{
|
|
35
|
+
asc: "asc";
|
|
36
|
+
desc: "desc";
|
|
37
|
+
}>;
|
|
38
|
+
}, z.core.$strict>>>;
|
|
31
39
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
32
40
|
offset: z.ZodOptional<z.ZodNumber>;
|
|
33
|
-
} & {
|
|
34
41
|
dataset: z.ZodOptional<z.ZodString>;
|
|
35
42
|
metric: z.ZodOptional<z.ZodString>;
|
|
36
|
-
},
|
|
37
|
-
dataset?: string | undefined;
|
|
38
|
-
dimensions?: string[] | undefined;
|
|
39
|
-
filters?: {
|
|
40
|
-
field: string;
|
|
41
|
-
operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
|
|
42
|
-
value?: any;
|
|
43
|
-
}[] | undefined;
|
|
44
|
-
segments?: string[] | undefined;
|
|
45
|
-
metric?: string | undefined;
|
|
46
|
-
grain?: TimeGrain | undefined;
|
|
47
|
-
timezone?: string | undefined;
|
|
48
|
-
orderBy?: {
|
|
49
|
-
field: string;
|
|
50
|
-
direction: "asc" | "desc";
|
|
51
|
-
}[] | undefined;
|
|
52
|
-
limit?: number | undefined;
|
|
53
|
-
offset?: number | undefined;
|
|
54
|
-
}, {
|
|
55
|
-
dataset?: string | undefined;
|
|
56
|
-
dimensions?: string[] | undefined;
|
|
57
|
-
filters?: {
|
|
58
|
-
field: string;
|
|
59
|
-
operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
|
|
60
|
-
value?: any;
|
|
61
|
-
}[] | undefined;
|
|
62
|
-
segments?: string[] | undefined;
|
|
63
|
-
metric?: string | undefined;
|
|
64
|
-
grain?: TimeGrain | undefined;
|
|
65
|
-
timezone?: string | undefined;
|
|
66
|
-
orderBy?: {
|
|
67
|
-
field: string;
|
|
68
|
-
direction: "asc" | "desc";
|
|
69
|
-
}[] | undefined;
|
|
70
|
-
limit?: number | undefined;
|
|
71
|
-
offset?: number | undefined;
|
|
72
|
-
}>;
|
|
43
|
+
}, z.core.$strict>;
|
|
73
44
|
export declare const queryDatasetArgsSchema: z.ZodObject<{
|
|
74
|
-
dimensions: z.ZodOptional<z.ZodArray<z.ZodString
|
|
45
|
+
dimensions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
75
46
|
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
76
47
|
field: z.ZodString;
|
|
77
|
-
operator: z.ZodEnum<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
48
|
+
operator: z.ZodEnum<{
|
|
49
|
+
eq: "eq";
|
|
50
|
+
neq: "neq";
|
|
51
|
+
gt: "gt";
|
|
52
|
+
gte: "gte";
|
|
53
|
+
lt: "lt";
|
|
54
|
+
lte: "lte";
|
|
55
|
+
in: "in";
|
|
56
|
+
notIn: "notIn";
|
|
57
|
+
between: "between";
|
|
58
|
+
like: "like";
|
|
59
|
+
}>;
|
|
60
|
+
value: z.ZodAny;
|
|
61
|
+
}, z.core.$strict>>>;
|
|
62
|
+
segments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
63
|
+
grain: z.ZodOptional<z.ZodEnum<{
|
|
64
|
+
minute: "minute";
|
|
65
|
+
hour: "hour";
|
|
66
|
+
day: "day";
|
|
67
|
+
week: "week";
|
|
68
|
+
month: "month";
|
|
69
|
+
quarter: "quarter";
|
|
70
|
+
year: "year";
|
|
71
|
+
}>>;
|
|
90
72
|
timezone: z.ZodOptional<z.ZodString>;
|
|
91
73
|
orderBy: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
92
74
|
field: z.ZodString;
|
|
93
|
-
direction: z.ZodEnum<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
},
|
|
98
|
-
field: string;
|
|
99
|
-
direction: "asc" | "desc";
|
|
100
|
-
}>, "many">>;
|
|
75
|
+
direction: z.ZodEnum<{
|
|
76
|
+
asc: "asc";
|
|
77
|
+
desc: "desc";
|
|
78
|
+
}>;
|
|
79
|
+
}, z.core.$strict>>>;
|
|
101
80
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
102
81
|
offset: z.ZodOptional<z.ZodNumber>;
|
|
103
|
-
} & {
|
|
104
82
|
dataset: z.ZodOptional<z.ZodString>;
|
|
105
|
-
measures: z.ZodOptional<z.ZodArray<z.ZodString
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
dimensions?: string[] | undefined;
|
|
109
|
-
measures?: string[] | undefined;
|
|
110
|
-
filters?: {
|
|
111
|
-
field: string;
|
|
112
|
-
operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
|
|
113
|
-
value?: any;
|
|
114
|
-
}[] | undefined;
|
|
115
|
-
segments?: string[] | undefined;
|
|
116
|
-
grain?: TimeGrain | undefined;
|
|
117
|
-
timezone?: string | undefined;
|
|
118
|
-
orderBy?: {
|
|
119
|
-
field: string;
|
|
120
|
-
direction: "asc" | "desc";
|
|
121
|
-
}[] | undefined;
|
|
122
|
-
limit?: number | undefined;
|
|
123
|
-
offset?: number | undefined;
|
|
124
|
-
}, {
|
|
125
|
-
dataset?: string | undefined;
|
|
126
|
-
dimensions?: string[] | undefined;
|
|
127
|
-
measures?: string[] | undefined;
|
|
128
|
-
filters?: {
|
|
129
|
-
field: string;
|
|
130
|
-
operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
|
|
131
|
-
value?: any;
|
|
132
|
-
}[] | undefined;
|
|
133
|
-
segments?: string[] | undefined;
|
|
134
|
-
grain?: TimeGrain | undefined;
|
|
135
|
-
timezone?: string | undefined;
|
|
136
|
-
orderBy?: {
|
|
137
|
-
field: string;
|
|
138
|
-
direction: "asc" | "desc";
|
|
139
|
-
}[] | undefined;
|
|
140
|
-
limit?: number | undefined;
|
|
141
|
-
offset?: number | undefined;
|
|
142
|
-
}>;
|
|
143
|
-
export declare function parseToolArgs<T>(schema: z.ZodType<T>, toolName: string, args: unknown): T;
|
|
83
|
+
measures: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
84
|
+
}, z.core.$strict>;
|
|
85
|
+
export declare function parseToolArgs<T = any>(schema: z.ZodType, toolName: string, args: unknown): T;
|
|
144
86
|
export declare function toMetricFilters(filters?: Array<{
|
|
145
87
|
field: string;
|
|
146
88
|
operator: MetricFilter['operator'];
|
package/dist/tools/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/tools/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAGL,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/tools/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAmC7B,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAGhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAGjC,CAAC;AAaH,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,CAS5F;AAED,wBAAgB,eAAe,CAC7B,OAAO,GAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAM,GAC1F,YAAY,EAAE,CAMhB"}
|
package/dist/tools/args.js
CHANGED
|
@@ -34,11 +34,13 @@ export const queryDatasetArgsSchema = baseQuerySchema.extend({
|
|
|
34
34
|
function formatZodError(error) {
|
|
35
35
|
return error.issues
|
|
36
36
|
.map(issue => {
|
|
37
|
-
const path = issue.path.length > 0 ? issue.path.join('.') : 'arguments';
|
|
37
|
+
const path = issue.path.length > 0 ? issue.path.map(String).join('.') : 'arguments';
|
|
38
38
|
return `${path}: ${issue.message}`;
|
|
39
39
|
})
|
|
40
40
|
.join('; ');
|
|
41
41
|
}
|
|
42
|
+
// Canonical schemas are built per catalog, so their parsed type is not known
|
|
43
|
+
// statically; callers name it, as Zod 3's `ZodTypeAny` used to imply `any`.
|
|
42
44
|
export function parseToolArgs(schema, toolName, args) {
|
|
43
45
|
const result = schema.safeParse(args);
|
|
44
46
|
if (!result.success) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CanonicalSemanticQuerySchemas } from '@hypequery/datasets/schemas';
|
|
2
2
|
import type { ListToolsResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
export declare const DATASET_LIST_OUTPUT_SCHEMA: {
|
|
4
4
|
[x: string]: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/tool-manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/tool-manifest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAQ,MAAM,oCAAoC,CAAC;AAehF,eAAO,MAAM,0BAA0B;;;;;;;2BAsBrC,CAAC;AAEH,eAAO,MAAM,4BAA4B;;;;;;;2BAmIvC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;2BAwC9B,CAAC;AAYH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,6BAA6B,GAC1C,eAAe,CA8CjB"}
|