@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.
Files changed (50) hide show
  1. package/README.md +70 -0
  2. package/dist/executor.d.ts +3 -3
  3. package/dist/executor.d.ts.map +1 -1
  4. package/dist/http-executor.d.ts +40 -0
  5. package/dist/http-executor.d.ts.map +1 -0
  6. package/dist/http-executor.js +178 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/protocol-server.js +3 -3
  11. package/dist/tools/args.d.ts +65 -123
  12. package/dist/tools/args.d.ts.map +1 -1
  13. package/dist/tools/args.js +3 -1
  14. package/dist/tools/tool-manifest.d.ts +1 -1
  15. package/dist/tools/tool-manifest.d.ts.map +1 -1
  16. package/dist/tools/tool-manifest.js +1 -47
  17. package/dist/tools/utils/canonical-query-schemas.d.ts +1 -1
  18. package/dist/tools/utils/canonical-query-schemas.d.ts.map +1 -1
  19. package/dist/tools/utils/canonical-query-schemas.js +1 -1
  20. package/dist/tools/utils/result-schema.d.ts +5 -0
  21. package/dist/tools/utils/result-schema.d.ts.map +1 -0
  22. package/dist/tools/utils/result-schema.js +47 -0
  23. package/dist/utils/http-client/arguments.d.ts +3 -0
  24. package/dist/utils/http-client/arguments.d.ts.map +1 -0
  25. package/dist/utils/http-client/arguments.js +26 -0
  26. package/dist/utils/http-client/discovery.d.ts +6 -0
  27. package/dist/utils/http-client/discovery.d.ts.map +1 -0
  28. package/dist/utils/http-client/discovery.js +24 -0
  29. package/dist/utils/http-client/json.d.ts +6 -0
  30. package/dist/utils/http-client/json.d.ts.map +1 -0
  31. package/dist/utils/http-client/json.js +79 -0
  32. package/dist/utils/http-client/manifest.d.ts +5 -0
  33. package/dist/utils/http-client/manifest.d.ts.map +1 -0
  34. package/dist/utils/http-client/manifest.js +53 -0
  35. package/dist/utils/http-client/profile.d.ts +5 -0
  36. package/dist/utils/http-client/profile.d.ts.map +1 -0
  37. package/dist/utils/http-client/profile.js +29 -0
  38. package/dist/utils/http-client/result.d.ts +4 -0
  39. package/dist/utils/http-client/result.d.ts.map +1 -0
  40. package/dist/utils/http-client/result.js +98 -0
  41. package/dist/utils/http-client/transport.d.ts +12 -0
  42. package/dist/utils/http-client/transport.d.ts.map +1 -0
  43. package/dist/utils/http-client/transport.js +88 -0
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.d.ts.map +1 -1
  46. package/dist/version.js +1 -1
  47. package/package.json +12 -6
  48. package/dist/api.type-test.d.ts +0 -2
  49. package/dist/api.type-test.d.ts.map +0 -1
  50. 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;
@@ -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>): Promise<GetPromptResult>;
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
  /**
@@ -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,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;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"}
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
@@ -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';
@@ -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();
@@ -1,146 +1,88 @@
1
1
  import { z } from 'zod';
2
- import { type MetricFilter, type TimeGrain } from '@hypequery/datasets';
2
+ import { type MetricFilter } from '@hypequery/datasets';
3
3
  export declare const queryMetricArgsSchema: z.ZodObject<{
4
- dimensions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
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<["eq", "neq", "gt", "gte", "lt", "lte", "in", "notIn", "between", "like"]>;
8
- value: z.ZodEffects<z.ZodAny, any, any>;
9
- }, "strict", z.ZodTypeAny, {
10
- field: string;
11
- operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
12
- value?: any;
13
- }, {
14
- field: string;
15
- operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
16
- value?: any;
17
- }>, "many">>;
18
- segments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
19
- grain: z.ZodOptional<z.ZodEnum<[TimeGrain, ...TimeGrain[]]>>;
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<["asc", "desc"]>;
24
- }, "strict", z.ZodTypeAny, {
25
- field: string;
26
- direction: "asc" | "desc";
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
- }, "strict", z.ZodTypeAny, {
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, "many">>;
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<["eq", "neq", "gt", "gte", "lt", "lte", "in", "notIn", "between", "like"]>;
78
- value: z.ZodEffects<z.ZodAny, any, any>;
79
- }, "strict", z.ZodTypeAny, {
80
- field: string;
81
- operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
82
- value?: any;
83
- }, {
84
- field: string;
85
- operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "between" | "like";
86
- value?: any;
87
- }>, "many">>;
88
- segments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
89
- grain: z.ZodOptional<z.ZodEnum<[TimeGrain, ...TimeGrain[]]>>;
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<["asc", "desc"]>;
94
- }, "strict", z.ZodTypeAny, {
95
- field: string;
96
- direction: "asc" | "desc";
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, "many">>;
106
- }, "strict", z.ZodTypeAny, {
107
- dataset?: string | undefined;
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'];
@@ -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,EACjB,KAAK,SAAS,EACf,MAAM,qBAAqB,CAAC;AAmC7B,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGjC,CAAC;AAWH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,CASzF;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"}
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"}
@@ -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 { type CanonicalSemanticQuerySchemas } from '@hypequery/datasets';
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":"AAAA,OAAO,EAAyB,KAAK,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,KAAK,EAAE,eAAe,EAAQ,MAAM,oCAAoC,CAAC;AAiEhF,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"}
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"}