@neuraltrust/trustgate 0.1.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.
Files changed (63) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +132 -0
  3. package/dist/agent.d.ts +156 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +216 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/client.d.ts +88 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +152 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/config.d.ts +44 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +40 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/connections.d.ts +13 -0
  16. package/dist/connections.d.ts.map +1 -0
  17. package/dist/connections.js +46 -0
  18. package/dist/connections.js.map +1 -0
  19. package/dist/errors.d.ts +91 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +144 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/formats.d.ts +44 -0
  24. package/dist/formats.d.ts.map +1 -0
  25. package/dist/formats.js +299 -0
  26. package/dist/formats.js.map +1 -0
  27. package/dist/http.d.ts +23 -0
  28. package/dist/http.d.ts.map +1 -0
  29. package/dist/http.js +94 -0
  30. package/dist/http.js.map +1 -0
  31. package/dist/index.d.ts +10 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +10 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/mcp.d.ts +43 -0
  36. package/dist/mcp.d.ts.map +1 -0
  37. package/dist/mcp.js +175 -0
  38. package/dist/mcp.js.map +1 -0
  39. package/dist/schema.d.ts +52 -0
  40. package/dist/schema.d.ts.map +1 -0
  41. package/dist/schema.js +168 -0
  42. package/dist/schema.js.map +1 -0
  43. package/dist/types.d.ts +78 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +52 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/whoami.d.ts +73 -0
  48. package/dist/whoami.d.ts.map +1 -0
  49. package/dist/whoami.js +79 -0
  50. package/dist/whoami.js.map +1 -0
  51. package/package.json +29 -0
  52. package/src/agent.ts +267 -0
  53. package/src/client.ts +203 -0
  54. package/src/config.ts +78 -0
  55. package/src/connections.ts +90 -0
  56. package/src/errors.ts +154 -0
  57. package/src/formats.ts +362 -0
  58. package/src/http.ts +106 -0
  59. package/src/index.ts +41 -0
  60. package/src/mcp.ts +203 -0
  61. package/src/schema.ts +193 -0
  62. package/src/types.ts +98 -0
  63. package/src/whoami.ts +172 -0
package/dist/http.js ADDED
@@ -0,0 +1,94 @@
1
+ import { API_KEY_HEADER } from './config.js';
2
+ import { AuthenticationError, InvalidRequestError, RateLimitedError, ServiceUnavailableError, TrustGateError, TrustGateServerError, } from './errors.js';
3
+ /**
4
+ * A JSON request against the gateway's REST surface (the connections API).
5
+ *
6
+ * The gateway answers `{error, message}` on failure; this turns each `error`
7
+ * code into the type that says whose problem it is. `expect` names statuses
8
+ * the caller handles itself — the actor probe uses it to read a 409 as an
9
+ * answer rather than a failure.
10
+ */
11
+ export async function requestJSON(config, method, path, options = {}) {
12
+ const url = `${config.baseUrl}${path}`;
13
+ const controller = new AbortController();
14
+ const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
15
+ const signal = options.signal ? anySignal([options.signal, controller.signal]) : controller.signal;
16
+ let response;
17
+ try {
18
+ response = await config.fetch(url, {
19
+ method,
20
+ headers: {
21
+ [API_KEY_HEADER]: config.apiKey,
22
+ Accept: 'application/json',
23
+ ...(options.body === undefined ? {} : { 'Content-Type': 'application/json' }),
24
+ ...options.headers,
25
+ },
26
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
27
+ signal,
28
+ });
29
+ }
30
+ catch (cause) {
31
+ throw new TrustGateError(`${method} ${path} failed to reach the gateway`, { cause });
32
+ }
33
+ finally {
34
+ clearTimeout(timeout);
35
+ }
36
+ const text = await response.text();
37
+ const body = text ? safeParse(text) : undefined;
38
+ if (response.ok || options.expect?.includes(response.status)) {
39
+ return { status: response.status, body: body };
40
+ }
41
+ throw errorForResponse(response, body, text);
42
+ }
43
+ function errorForResponse(response, body, raw) {
44
+ const code = body?.error;
45
+ const message = body?.message ?? raw ?? response.statusText;
46
+ const status = response.status;
47
+ switch (code) {
48
+ case 'unauthenticated':
49
+ return new AuthenticationError(message, { status, code });
50
+ case 'invalid_request':
51
+ return new InvalidRequestError(message, { status, code });
52
+ case 'unavailable':
53
+ return new ServiceUnavailableError(message, { status, code });
54
+ }
55
+ if (status === 401 || status === 403)
56
+ return new AuthenticationError(message, { status, code });
57
+ if (status === 429) {
58
+ return new RateLimitedError(message, retryAfterMs(response));
59
+ }
60
+ if (status >= 500)
61
+ return new TrustGateServerError(message, { status, code });
62
+ return new TrustGateError(message, { status, code });
63
+ }
64
+ function retryAfterMs(response) {
65
+ const header = response.headers.get('Retry-After');
66
+ if (!header)
67
+ return undefined;
68
+ const seconds = Number(header);
69
+ return Number.isFinite(seconds) ? seconds * 1000 : undefined;
70
+ }
71
+ function safeParse(text) {
72
+ try {
73
+ return JSON.parse(text);
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ /** AbortSignal.any is not on every runtime the SDK supports yet. */
80
+ function anySignal(signals) {
81
+ const anyOf = AbortSignal.any;
82
+ if (typeof anyOf === 'function')
83
+ return anyOf(signals);
84
+ const controller = new AbortController();
85
+ for (const signal of signals) {
86
+ if (signal.aborted) {
87
+ controller.abort(signal.reason);
88
+ break;
89
+ }
90
+ signal.addEventListener('abort', () => controller.abort(signal.reason), { once: true });
91
+ }
92
+ return controller.signal;
93
+ }
94
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { TrustGate, type ConnectOptions, type LLMEndpoint } from './client.js';
2
+ export { whoAmI, selectConsumer, type KeyConsumer, type KeyIdentity, type KeyInfo, type KeyUpstream, type UpstreamAccount, type UpstreamBlockedBy, } from './whoami.js';
3
+ export { Agent, EndUserAgent, Toolkit, type ToolkitOptions } from './agent.js';
4
+ export { type TrustGateConfig, API_KEY_HEADER, END_USER_HEADER } from './config.js';
5
+ export { MCPTransport } from './mcp.js';
6
+ export { inlineRefs, stripInjectedNulls, toStrict, type StrictResult } from './schema.js';
7
+ export { adapterFor, resultToText, type ConversionWarning } from './formats.js';
8
+ export { Actor, ToolFormat, type ConnectLink, type Connection, type Endpoint, type GatewayTool, type JSONSchema, type ToolCall, } from './types.js';
9
+ export { AuthenticationError, ConsentRequiredError, InvalidRequestError, MissingToolsError, PlaneUnavailableError, PolicyBlockedError, RateLimitedError, ServiceUnavailableError, ToolNotFoundError, TrustGateError, TrustGateServerError, UpstreamNotConnectedError, type BlockedUpstream, } from './errors.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9E,OAAO,EACN,MAAM,EACN,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACtB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAE,KAAK,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACzF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC/E,OAAO,EACN,KAAK,EACL,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,GACb,MAAM,YAAY,CAAA;AACnB,OAAO,EACN,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,eAAe,GACpB,MAAM,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { TrustGate } from './client.js';
2
+ export { whoAmI, selectConsumer, } from './whoami.js';
3
+ export { Agent, EndUserAgent, Toolkit } from './agent.js';
4
+ export { API_KEY_HEADER, END_USER_HEADER } from './config.js';
5
+ export { MCPTransport } from './mcp.js';
6
+ export { inlineRefs, stripInjectedNulls, toStrict } from './schema.js';
7
+ export { adapterFor, resultToText } from './formats.js';
8
+ export { Actor, ToolFormat, } from './types.js';
9
+ export { AuthenticationError, ConsentRequiredError, InvalidRequestError, MissingToolsError, PlaneUnavailableError, PolicyBlockedError, RateLimitedError, ServiceUnavailableError, ToolNotFoundError, TrustGateError, TrustGateServerError, UpstreamNotConnectedError, } from './errors.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyC,MAAM,aAAa,CAAA;AAC9E,OAAO,EACN,MAAM,EACN,cAAc,GAOd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAuB,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAwB,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAA;AACzF,OAAO,EAAE,UAAU,EAAE,YAAY,EAA0B,MAAM,cAAc,CAAA;AAC/E,OAAO,EACN,KAAK,EACL,UAAU,GAOV,MAAM,YAAY,CAAA;AACnB,OAAO,EACN,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,yBAAyB,GAEzB,MAAM,aAAa,CAAA"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { type ResolvedConfig } from './config.js';
2
+ import type { GatewayTool } from './types.js';
3
+ type RPCError = {
4
+ code: number;
5
+ message: string;
6
+ data?: unknown;
7
+ };
8
+ type RPCResponse = {
9
+ id?: unknown;
10
+ result?: Record<string, unknown>;
11
+ error?: RPCError;
12
+ };
13
+ /**
14
+ * The gateway's MCP endpoint, spoken directly.
15
+ *
16
+ * Only two methods are needed to put a consumer's tools in front of a model —
17
+ * list them and call them — so this is a JSON-RPC client rather than a whole
18
+ * MCP implementation. The gateway is stateless: there is no handshake to do
19
+ * and no session to carry, so every call stands on its own.
20
+ */
21
+ export declare class MCPTransport {
22
+ private readonly config;
23
+ readonly url: string;
24
+ private readonly extraHeaders;
25
+ private nextId;
26
+ constructor(config: ResolvedConfig, url: string, extraHeaders?: Record<string, string>);
27
+ get headers(): Record<string, string>;
28
+ listTools(signal?: AbortSignal): Promise<GatewayTool[]>;
29
+ callTool(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<Record<string, unknown>>;
30
+ call(method: string, params: Record<string, unknown>, signal?: AbortSignal): Promise<Record<string, unknown>>;
31
+ }
32
+ /**
33
+ * Reads the response body, which is not always JSON.
34
+ *
35
+ * When a change to the surface has to be announced, the gateway answers the
36
+ * same request as an event stream and puts the response in a frame after the
37
+ * notification. Both shapes carry the same JSON-RPC object, so both are read
38
+ * here; a frame that is not this request's answer is skipped rather than
39
+ * mistaken for it.
40
+ */
41
+ export declare function parseRPCResponse(text: string, id: number): RPCResponse | undefined;
42
+ export {};
43
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAUjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,YAAY,CAAA;AAUzD,KAAK,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AACjE,KAAK,WAAW,GAAG;IAAE,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAA;AAEvF;;;;;;;GAOG;AACH,qBAAa,YAAY;IAIvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAL9B,OAAO,CAAC,MAAM,CAAI;gBAGA,MAAM,EAAE,cAAc,EAC9B,GAAG,EAAE,MAAM,EACH,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAG3D,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEpC;IAEK,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAevD,QAAQ,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,IAAI,CACT,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CA0CnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAoBlF"}
package/dist/mcp.js ADDED
@@ -0,0 +1,175 @@
1
+ import { API_KEY_HEADER } from './config.js';
2
+ import { AuthenticationError, ConsentRequiredError, InvalidRequestError, PolicyBlockedError, ToolNotFoundError, TrustGateError, TrustGateServerError, } from './errors.js';
3
+ /** JSON-RPC codes the gateway answers with, beyond the standard four. */
4
+ const CODE_CONSENT_REQUIRED = -32003;
5
+ const CODE_RESOURCE_NOT_FOUND = -32002;
6
+ const CODE_POLICY_BLOCKED = -32001;
7
+ const CODE_INVALID_REQUEST = -32600;
8
+ const CODE_INVALID_PARAMS = -32602;
9
+ const CODE_INTERNAL = -32603;
10
+ /**
11
+ * The gateway's MCP endpoint, spoken directly.
12
+ *
13
+ * Only two methods are needed to put a consumer's tools in front of a model —
14
+ * list them and call them — so this is a JSON-RPC client rather than a whole
15
+ * MCP implementation. The gateway is stateless: there is no handshake to do
16
+ * and no session to carry, so every call stands on its own.
17
+ */
18
+ export class MCPTransport {
19
+ config;
20
+ url;
21
+ extraHeaders;
22
+ nextId = 1;
23
+ constructor(config, url, extraHeaders = {}) {
24
+ this.config = config;
25
+ this.url = url;
26
+ this.extraHeaders = extraHeaders;
27
+ }
28
+ get headers() {
29
+ return { [API_KEY_HEADER]: this.config.apiKey, ...this.extraHeaders };
30
+ }
31
+ async listTools(signal) {
32
+ const result = await this.call('tools/list', {}, signal);
33
+ const tools = Array.isArray(result.tools) ? result.tools : [];
34
+ return tools.map((tool) => {
35
+ const raw = tool;
36
+ return {
37
+ name: String(raw.name ?? ''),
38
+ title: typeof raw.title === 'string' ? raw.title : undefined,
39
+ description: typeof raw.description === 'string' ? raw.description : undefined,
40
+ inputSchema: raw.inputSchema ?? { type: 'object', properties: {} },
41
+ outputSchema: raw.outputSchema,
42
+ };
43
+ });
44
+ }
45
+ async callTool(name, args, signal) {
46
+ try {
47
+ return await this.call('tools/call', { name, arguments: args }, signal);
48
+ }
49
+ catch (error) {
50
+ // The gateway reports an unknown tool as invalid params, which is
51
+ // true of the request and useless to the caller: what they need to
52
+ // know is which tool, because a toolkit can lose one under them.
53
+ if (error instanceof InvalidRequestError && error.code === String(CODE_INVALID_PARAMS)) {
54
+ throw new ToolNotFoundError(name, error.message);
55
+ }
56
+ throw error;
57
+ }
58
+ }
59
+ async call(method, params, signal) {
60
+ const id = this.nextId++;
61
+ const controller = new AbortController();
62
+ const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);
63
+ let response;
64
+ try {
65
+ response = await this.config.fetch(this.url, {
66
+ method: 'POST',
67
+ headers: {
68
+ ...this.headers,
69
+ 'Content-Type': 'application/json',
70
+ // A plain JSON answer is enough: the SDK re-lists on demand
71
+ // rather than listening for a change on the response.
72
+ Accept: 'application/json',
73
+ },
74
+ body: JSON.stringify({ jsonrpc: '2.0', id, method, params }),
75
+ signal: signal ?? controller.signal,
76
+ });
77
+ }
78
+ catch (cause) {
79
+ throw new TrustGateError(`MCP ${method} failed to reach ${this.url}`, { cause });
80
+ }
81
+ finally {
82
+ clearTimeout(timeout);
83
+ }
84
+ if (response.status === 401 || response.status === 403) {
85
+ throw new AuthenticationError(`the gateway refused this API key for ${this.url}`, { status: response.status });
86
+ }
87
+ const text = await response.text();
88
+ const rpc = parseRPCResponse(text, id);
89
+ if (!rpc) {
90
+ throw new TrustGateError(`MCP ${method} returned no JSON-RPC response (HTTP ${response.status})` +
91
+ whatItSaid(text), { status: response.status });
92
+ }
93
+ if (rpc.error)
94
+ throw errorForRPC(rpc.error);
95
+ return rpc.result ?? {};
96
+ }
97
+ }
98
+ /**
99
+ * Reads the response body, which is not always JSON.
100
+ *
101
+ * When a change to the surface has to be announced, the gateway answers the
102
+ * same request as an event stream and puts the response in a frame after the
103
+ * notification. Both shapes carry the same JSON-RPC object, so both are read
104
+ * here; a frame that is not this request's answer is skipped rather than
105
+ * mistaken for it.
106
+ */
107
+ export function parseRPCResponse(text, id) {
108
+ const trimmed = text.trim();
109
+ if (!trimmed)
110
+ return undefined;
111
+ if (trimmed.startsWith('{')) {
112
+ try {
113
+ return JSON.parse(trimmed);
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ }
119
+ for (const line of trimmed.split('\n')) {
120
+ if (!line.startsWith('data:'))
121
+ continue;
122
+ try {
123
+ const frame = JSON.parse(line.slice('data:'.length).trim());
124
+ if (frame.id === id)
125
+ return frame;
126
+ }
127
+ catch {
128
+ continue;
129
+ }
130
+ }
131
+ return undefined;
132
+ }
133
+ function errorForRPC(error) {
134
+ const data = (error.data ?? {});
135
+ switch (error.code) {
136
+ case CODE_CONSENT_REQUIRED:
137
+ return new ConsentRequiredError(String(data.provider ?? 'this provider'), String(data.connect_url ?? ''), String(data.cause ?? ''), error.message);
138
+ case CODE_POLICY_BLOCKED:
139
+ return new PolicyBlockedError(error.message, { code: String(error.code) });
140
+ case CODE_INTERNAL:
141
+ return new TrustGateServerError(error.message, { code: String(error.code) });
142
+ case CODE_INVALID_PARAMS:
143
+ case CODE_INVALID_REQUEST:
144
+ case CODE_RESOURCE_NOT_FOUND:
145
+ return new InvalidRequestError(error.message, { code: String(error.code) });
146
+ default:
147
+ return new TrustGateError(error.message, { code: String(error.code) });
148
+ }
149
+ }
150
+ /**
151
+ * The reason the endpoint gave, when it did not give it in JSON-RPC.
152
+ *
153
+ * A plain HTTP error — a missing header, a path that is no virtual MCP — says
154
+ * why in its body, and dropping that leaves a status code to guess from.
155
+ */
156
+ function whatItSaid(text) {
157
+ let said = (text ?? '').split(/\s+/).filter(Boolean).join(' ');
158
+ try {
159
+ const body = JSON.parse(text);
160
+ if (body && typeof body === 'object') {
161
+ for (const key of ['error', 'message', 'detail']) {
162
+ const value = body[key];
163
+ if (typeof value === 'string' && value.trim()) {
164
+ said = value.trim();
165
+ break;
166
+ }
167
+ }
168
+ }
169
+ }
170
+ catch {
171
+ // Not JSON. The whitespace-collapsed body is the best there is.
172
+ }
173
+ return said ? `: ${said.slice(0, 200)}` : '';
174
+ }
175
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { JSONSchema } from './types.js';
2
+ /**
3
+ * Turning a tool's JSON Schema into what a provider will accept.
4
+ *
5
+ * The gateway relays an upstream server's schema exactly as that server wrote
6
+ * it — that is the honest thing for a gateway to do, and it means the schema
7
+ * can use anything JSON Schema allows. Every provider's function calling
8
+ * accepts a smaller language than that. Translating is therefore the client's
9
+ * job, done here, once, against the format the caller asked for.
10
+ *
11
+ * Two of these conversions lose information, so both are reversible and the
12
+ * original schema is kept: what goes out closed and nullable has to come back
13
+ * open and absent, or the upstream rejects the call it was asked to make.
14
+ */
15
+ export type StrictResult = {
16
+ schema: JSONSchema;
17
+ /** False when the schema uses something strict mode cannot express. */
18
+ strict: boolean;
19
+ /** Why not, for the warning the caller gets. */
20
+ reason?: string;
21
+ };
22
+ /**
23
+ * Inlines local `$ref`s.
24
+ *
25
+ * Nothing is lost: a reference and its target describe the same thing. It is
26
+ * separated from the strict pass because every provider needs it and none of
27
+ * them object to the result — which is why this is also the part that could
28
+ * one day move into the gateway.
29
+ */
30
+ export declare function inlineRefs(schema: JSONSchema): JSONSchema;
31
+ /**
32
+ * Rewrites a schema for OpenAI's strict function calling.
33
+ *
34
+ * Strict buys a guarantee worth having — the model cannot invent an argument —
35
+ * and charges for it in expressiveness: every object closed, every property
36
+ * required, and optionality expressed by accepting null. Schemas that use what
37
+ * strict cannot say are returned untouched with `strict: false`, because a tool
38
+ * the model can still call imperfectly beats a tool it cannot call at all.
39
+ */
40
+ export declare function toStrict(schema: JSONSchema): StrictResult;
41
+ /**
42
+ * Removes the nulls strict mode asked the model to send.
43
+ *
44
+ * `toStrict` made every optional property nullable so it could be named in
45
+ * `required`. The model takes that literally and sends `null` for the ones it
46
+ * has no value for — and the upstream server, which never agreed to any of
47
+ * this, rejects them. So the arguments are compared against the *original*
48
+ * schema on the way back, and a null it never permitted is dropped rather than
49
+ * forwarded.
50
+ */
51
+ export declare function stripInjectedNulls(args: unknown, original: JSONSchema | undefined): unknown;
52
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,UAAU,CAAA;IAClB,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAgCzD;AAQD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAgDzD;AAcD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAc3F"}
package/dist/schema.js ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Inlines local `$ref`s.
3
+ *
4
+ * Nothing is lost: a reference and its target describe the same thing. It is
5
+ * separated from the strict pass because every provider needs it and none of
6
+ * them object to the result — which is why this is also the part that could
7
+ * one day move into the gateway.
8
+ */
9
+ export function inlineRefs(schema) {
10
+ const defs = {
11
+ ...(schema.$defs ?? {}),
12
+ ...(schema.definitions ?? {}),
13
+ };
14
+ const seen = new Set();
15
+ const walk = (node) => {
16
+ if (Array.isArray(node))
17
+ return node.map(walk);
18
+ if (!isObject(node))
19
+ return node;
20
+ const ref = node.$ref;
21
+ if (typeof ref === 'string') {
22
+ const target = resolveRef(ref, defs);
23
+ // A cycle cannot be inlined; leaving the $ref in place makes the
24
+ // strict pass refuse the tool, which is better than looping.
25
+ if (target && !seen.has(ref)) {
26
+ seen.add(ref);
27
+ const resolved = walk({ ...target, ...omit(node, ['$ref']) });
28
+ seen.delete(ref);
29
+ return resolved;
30
+ }
31
+ return node;
32
+ }
33
+ const out = {};
34
+ for (const [key, value] of Object.entries(node)) {
35
+ if (key === '$defs' || key === 'definitions')
36
+ continue;
37
+ out[key] = walk(value);
38
+ }
39
+ return out;
40
+ };
41
+ return walk(schema);
42
+ }
43
+ function resolveRef(ref, defs) {
44
+ const match = /^#\/(?:\$defs|definitions)\/(.+)$/.exec(ref);
45
+ if (!match)
46
+ return undefined;
47
+ return defs[decodeURIComponent(match[1])];
48
+ }
49
+ /**
50
+ * Rewrites a schema for OpenAI's strict function calling.
51
+ *
52
+ * Strict buys a guarantee worth having — the model cannot invent an argument —
53
+ * and charges for it in expressiveness: every object closed, every property
54
+ * required, and optionality expressed by accepting null. Schemas that use what
55
+ * strict cannot say are returned untouched with `strict: false`, because a tool
56
+ * the model can still call imperfectly beats a tool it cannot call at all.
57
+ */
58
+ export function toStrict(schema) {
59
+ const inlined = inlineRefs(schema);
60
+ let reason;
61
+ const walk = (node) => {
62
+ if (Array.isArray(node))
63
+ return node.map(walk);
64
+ if (!isObject(node))
65
+ return node;
66
+ if ('$ref' in node) {
67
+ reason ??= 'it carries a $ref that does not resolve inside the schema';
68
+ return node;
69
+ }
70
+ if ('allOf' in node) {
71
+ reason ??= 'it composes with allOf';
72
+ return node;
73
+ }
74
+ if ('prefixItems' in node) {
75
+ reason ??= 'it uses prefixItems (tuple typing)';
76
+ return node;
77
+ }
78
+ const out = {};
79
+ for (const [key, value] of Object.entries(node)) {
80
+ out[key] = key === 'required' ? value : walk(value);
81
+ }
82
+ if (out.type !== 'object' && !isObject(out.properties))
83
+ return out;
84
+ if (out.additionalProperties !== undefined && out.additionalProperties !== false) {
85
+ reason ??= 'it accepts properties that are not in its schema';
86
+ return out;
87
+ }
88
+ out.additionalProperties = false;
89
+ const properties = out.properties ?? {};
90
+ const required = new Set(Array.isArray(out.required) ? out.required : []);
91
+ const rewritten = {};
92
+ for (const [name, property] of Object.entries(properties)) {
93
+ rewritten[name] = required.has(name) ? property : nullable(property);
94
+ }
95
+ out.properties = rewritten;
96
+ // Strict wants every property named as required. What used to be
97
+ // optional stays optional in effect, by accepting null.
98
+ out.required = Object.keys(rewritten);
99
+ return out;
100
+ };
101
+ const rewritten = walk(inlined);
102
+ return reason ? { schema: inlined, strict: false, reason } : { schema: rewritten, strict: true };
103
+ }
104
+ /** Lets a schema accept null without losing what it already said. */
105
+ function nullable(schema) {
106
+ if (typeof schema.type === 'string') {
107
+ return { ...schema, type: [schema.type, 'null'] };
108
+ }
109
+ if (Array.isArray(schema.type)) {
110
+ return schema.type.includes('null') ? schema : { ...schema, type: [...schema.type, 'null'] };
111
+ }
112
+ // No plain type to widen (an enum, an anyOf): offer null beside it.
113
+ return { anyOf: [schema, { type: 'null' }] };
114
+ }
115
+ /**
116
+ * Removes the nulls strict mode asked the model to send.
117
+ *
118
+ * `toStrict` made every optional property nullable so it could be named in
119
+ * `required`. The model takes that literally and sends `null` for the ones it
120
+ * has no value for — and the upstream server, which never agreed to any of
121
+ * this, rejects them. So the arguments are compared against the *original*
122
+ * schema on the way back, and a null it never permitted is dropped rather than
123
+ * forwarded.
124
+ */
125
+ export function stripInjectedNulls(args, original) {
126
+ if (Array.isArray(args)) {
127
+ const items = original?.items;
128
+ return args.map((item) => stripInjectedNulls(item, items));
129
+ }
130
+ if (!isObject(args))
131
+ return args;
132
+ const properties = original?.properties ?? {};
133
+ const out = {};
134
+ for (const [key, value] of Object.entries(args)) {
135
+ const property = properties[key];
136
+ if (value === null && !permitsNull(property))
137
+ continue;
138
+ out[key] = stripInjectedNulls(value, property);
139
+ }
140
+ return out;
141
+ }
142
+ function permitsNull(schema) {
143
+ // An unknown property is left alone: the upstream may accept keys this
144
+ // schema does not describe, and dropping one would lose a real argument.
145
+ if (!schema)
146
+ return true;
147
+ if (schema.type === 'null')
148
+ return true;
149
+ if (Array.isArray(schema.type) && schema.type.includes('null'))
150
+ return true;
151
+ const anyOf = schema.anyOf ?? schema.oneOf;
152
+ if (Array.isArray(anyOf)) {
153
+ return anyOf.some((option) => permitsNull(option));
154
+ }
155
+ return false;
156
+ }
157
+ function isObject(value) {
158
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
159
+ }
160
+ function omit(source, keys) {
161
+ const out = {};
162
+ for (const [key, value] of Object.entries(source)) {
163
+ if (!keys.includes(key))
164
+ out[key] = value;
165
+ }
166
+ return out;
167
+ }
168
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}