@decocms/bindings 0.2.4-beta.4 → 1.0.0-test.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 (54) hide show
  1. package/README.md +3 -3
  2. package/package.json +9 -35
  3. package/src/core/binder.ts +241 -0
  4. package/src/core/client/README.md +3 -0
  5. package/{dist/core/client/http-client-transport.js → src/core/client/http-client-transport.ts} +24 -12
  6. package/src/core/client/index.ts +1 -0
  7. package/src/core/client/mcp-client.ts +149 -0
  8. package/src/core/client/mcp.ts +93 -0
  9. package/src/core/client/proxy.ts +151 -0
  10. package/src/core/connection.ts +38 -0
  11. package/src/core/subset.ts +514 -0
  12. package/src/index.ts +15 -0
  13. package/src/well-known/agent.ts +60 -0
  14. package/src/well-known/collections.ts +416 -0
  15. package/src/well-known/language-model.ts +383 -0
  16. package/test/index.test.ts +942 -0
  17. package/tsconfig.json +11 -0
  18. package/vitest.config.ts +8 -0
  19. package/dist/core/binder.d.ts +0 -3
  20. package/dist/core/binder.js +0 -81
  21. package/dist/core/binder.js.map +0 -1
  22. package/dist/core/client/http-client-transport.d.ts +0 -12
  23. package/dist/core/client/http-client-transport.js.map +0 -1
  24. package/dist/core/client/index.d.ts +0 -3
  25. package/dist/core/client/index.js +0 -5
  26. package/dist/core/client/index.js.map +0 -1
  27. package/dist/core/client/mcp-client.d.ts +0 -233
  28. package/dist/core/client/mcp-client.js +0 -99
  29. package/dist/core/client/mcp-client.js.map +0 -1
  30. package/dist/core/client/mcp.d.ts +0 -3
  31. package/dist/core/client/mcp.js +0 -29
  32. package/dist/core/client/mcp.js.map +0 -1
  33. package/dist/core/client/proxy.d.ts +0 -10
  34. package/dist/core/client/proxy.js +0 -104
  35. package/dist/core/client/proxy.js.map +0 -1
  36. package/dist/core/connection.d.ts +0 -30
  37. package/dist/core/connection.js +0 -1
  38. package/dist/core/connection.js.map +0 -1
  39. package/dist/core/subset.d.ts +0 -17
  40. package/dist/core/subset.js +0 -321
  41. package/dist/core/subset.js.map +0 -1
  42. package/dist/index-D0aUdNls.d.ts +0 -153
  43. package/dist/index.d.ts +0 -3
  44. package/dist/index.js +0 -7
  45. package/dist/index.js.map +0 -1
  46. package/dist/well-known/agent.d.ts +0 -903
  47. package/dist/well-known/agent.js +0 -27
  48. package/dist/well-known/agent.js.map +0 -1
  49. package/dist/well-known/collections.d.ts +0 -537
  50. package/dist/well-known/collections.js +0 -134
  51. package/dist/well-known/collections.js.map +0 -1
  52. package/dist/well-known/language-model.d.ts +0 -2836
  53. package/dist/well-known/language-model.js +0 -209
  54. package/dist/well-known/language-model.js.map +0 -1
package/README.md CHANGED
@@ -36,7 +36,7 @@ Bindings are a core concept for defining and enforcing standardized interfaces t
36
36
  A binding is an array of tool definitions, each specifying a name and input/output schemas:
37
37
 
38
38
  ```typescript
39
- import { z } from "zod/v3";
39
+ import { z } from "zod";
40
40
  import type { Binder } from "@decocms/bindings";
41
41
 
42
42
  // Define input/output schemas
@@ -242,7 +242,7 @@ All collection entity schemas must extend `BaseCollectionEntitySchema`, which re
242
242
  Collection bindings are a well-known binding pattern for SQL table-like structures. Import them from the well-known collections module:
243
243
 
244
244
  ```typescript
245
- import { z } from "zod/v3";
245
+ import { z } from "zod";
246
246
  import { createCollectionBindings } from "@decocms/bindings/collections";
247
247
  import { createBindingChecker } from "@decocms/bindings";
248
248
 
@@ -491,7 +491,7 @@ Collection bindings are designed to work with TanStack DB's query-collection. Th
491
491
  Here's how you would implement a collection binding in an MCP server:
492
492
 
493
493
  ```typescript
494
- import { z } from "zod/v3";
494
+ import { z } from "zod";
495
495
  import { createCollectionBindings } from "@decocms/bindings/collections";
496
496
  import { impl } from "@decocms/sdk/mcp/bindings/binder";
497
497
 
package/package.json CHANGED
@@ -1,12 +1,10 @@
1
1
  {
2
2
  "name": "@decocms/bindings",
3
- "version": "0.2.4-beta.4",
3
+ "version": "1.0.0-test.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
- "build": "tsup",
7
6
  "test": "vitest run",
8
- "test:watch": "vitest",
9
- "prepublishOnly": "npm run build"
7
+ "test:watch": "vitest"
10
8
  },
11
9
  "dependencies": {
12
10
  "@modelcontextprotocol/sdk": "^1.20.2",
@@ -14,40 +12,16 @@
14
12
  "zod-from-json-schema": "^0.0.5",
15
13
  "zod-to-json-schema": "3.25.0"
16
14
  },
17
- "main": "./dist/index.js",
18
- "types": "./dist/index.d.ts",
19
- "files": [
20
- "dist/**/*"
21
- ],
22
15
  "exports": {
23
- ".": {
24
- "types": "./dist/index.d.ts",
25
- "default": "./dist/index.js"
26
- },
27
- "./client": {
28
- "types": "./dist/core/client/index.d.ts",
29
- "default": "./dist/core/client/index.js"
30
- },
31
- "./llm": {
32
- "types": "./dist/well-known/language-model.d.ts",
33
- "default": "./dist/well-known/language-model.js"
34
- },
35
- "./connection": {
36
- "types": "./dist/core/connection.d.ts",
37
- "default": "./dist/core/connection.js"
38
- },
39
- "./collections": {
40
- "types": "./dist/well-known/collections.d.ts",
41
- "default": "./dist/well-known/collections.js"
42
- },
43
- "./agent": {
44
- "types": "./dist/well-known/agent.d.ts",
45
- "default": "./dist/well-known/agent.js"
46
- }
16
+ ".": "./src/index.ts",
17
+ "./models": "./src/well-known/models.ts",
18
+ "./collections": "./src/well-known/collections.ts",
19
+ "./llm": "./src/well-known/language-model.ts",
20
+ "./connection": "./src/core/connection.ts",
21
+ "./client": "./src/core/client/index.ts",
22
+ "./agent": "./src/well-known/agent.ts"
47
23
  },
48
24
  "devDependencies": {
49
- "tsup": "^8.5.0",
50
- "typescript": "^5.9.3",
51
25
  "vitest": "3.2.4"
52
26
  },
53
27
  "engines": {
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Core Binder Types and Utilities
3
+ *
4
+ * This module provides the core types and utilities for the bindings system.
5
+ * Bindings define standardized interfaces that integrations (MCPs) can implement.
6
+ */
7
+
8
+ import type { ZodType } from "zod";
9
+ import { zodToJsonSchema } from "zod-to-json-schema";
10
+ import { createMCPFetchStub, MCPClientFetchStub } from "./client/mcp";
11
+ import { MCPConnection } from "./connection";
12
+ import { isSubset } from "./subset";
13
+
14
+ type JsonSchema = Record<string, unknown>;
15
+
16
+ /**
17
+ * Checks if a value is a Zod schema by looking for the _def property
18
+ */
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ function isZodSchema(value: unknown): value is ZodType<any> {
21
+ return (
22
+ value !== null &&
23
+ typeof value === "object" &&
24
+ "_def" in value &&
25
+ typeof (value as Record<string, unknown>)._def === "object"
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Normalizes a schema to JSON Schema format.
31
+ * Accepts either a Zod schema or a JSON schema and returns a JSON schema.
32
+ *
33
+ * @param schema - A Zod schema or JSON schema
34
+ * @returns The JSON schema representation, or null if input is null/undefined
35
+ */
36
+ export function normalizeToJsonSchema(
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ schema: ZodType<any> | JsonSchema | null | undefined,
39
+ ): JsonSchema | null {
40
+ if (schema == null) {
41
+ return null;
42
+ }
43
+
44
+ if (isZodSchema(schema)) {
45
+ return zodToJsonSchema(schema) as JsonSchema;
46
+ }
47
+
48
+ // Already a JSON schema
49
+ return schema as JsonSchema;
50
+ }
51
+
52
+ /**
53
+ * ToolBinder defines a single tool within a binding.
54
+ * It specifies the tool name, input/output schemas, and whether it's optional.
55
+ *
56
+ * @template TName - The tool name (can be a string or RegExp for pattern matching)
57
+ * @template TInput - The input type (inferred from inputSchema)
58
+ * @template TReturn - The return type (inferred from outputSchema)
59
+ */
60
+ export interface ToolBinder<
61
+ TName extends string | RegExp = string,
62
+ // biome-ignore lint/suspicious/noExplicitAny: Generic type parameter
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ TInput = any,
65
+ TReturn extends object | null | boolean = object,
66
+ TStreamable extends boolean = boolean,
67
+ > {
68
+ /** The name of the tool (e.g., "DECO_CHAT_CHANNELS_JOIN") */
69
+ name: TName;
70
+
71
+ /** Zod schema for validating tool input */
72
+ inputSchema: ZodType<TInput>;
73
+
74
+ /** Optional Zod schema for validating tool output */
75
+ outputSchema?: TStreamable extends true ? never : ZodType<TReturn>;
76
+
77
+ /**
78
+ * Whether this tool is streamable.
79
+ */
80
+ streamable?: TStreamable;
81
+
82
+ /**
83
+ * Whether this tool is optional in the binding.
84
+ * If true, an implementation doesn't need to provide this tool.
85
+ */
86
+ opt?: true;
87
+ }
88
+
89
+ /**
90
+ * Binder represents a collection of tool definitions that form a binding.
91
+ * A binding is like a TypeScript interface - it defines what tools must be implemented.
92
+ *
93
+ * @template TDefinition - Array of ToolBinder definitions
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const MY_BINDING = [{
98
+ * name: "MY_TOOL" as const,
99
+ * inputSchema: z.object({ id: z.string() }),
100
+ * outputSchema: z.object({ success: z.boolean() }),
101
+ * }] as const satisfies Binder;
102
+ * ```
103
+ */
104
+ export type Binder<
105
+ TDefinition extends readonly ToolBinder[] = readonly ToolBinder[],
106
+ > = TDefinition;
107
+
108
+ /**
109
+ * Tool with schemas for validation
110
+ */
111
+ export interface ToolWithSchemas {
112
+ name: string;
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ inputSchema?: ZodType<any> | Record<string, unknown>;
115
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
+ outputSchema?: ZodType<any> | Record<string, unknown>;
117
+ }
118
+
119
+ /**
120
+ * Binding checker interface
121
+ */
122
+ export interface BindingChecker {
123
+ /**
124
+ * Check if a set of tools implements the binding with full schema validation.
125
+ *
126
+ * Validates:
127
+ * - Tool name matches (exact or regex)
128
+ * - Input schema: Tool accepts what binder requires (no removals from binder to tool)
129
+ * - Output schema: Tool provides what binder expects (no removals from tool to binder)
130
+ *
131
+ * @param tools - Array of tools with names and schemas
132
+ * @returns Promise<boolean> - true if all tools implement the binding correctly
133
+ */
134
+ isImplementedBy: (tools: ToolWithSchemas[]) => boolean;
135
+ }
136
+
137
+ export const bindingClient = <TDefinition extends readonly ToolBinder[]>(
138
+ binder: TDefinition,
139
+ ) => {
140
+ return {
141
+ ...createBindingChecker(binder),
142
+ forConnection: (
143
+ mcpConnection: MCPConnection,
144
+ ): MCPClientFetchStub<TDefinition> => {
145
+ return createMCPFetchStub<TDefinition>({
146
+ connection: mcpConnection,
147
+ streamable: binder.reduce(
148
+ (acc, tool) => {
149
+ acc[tool.name] = tool.streamable === true;
150
+ return acc;
151
+ },
152
+ {} as Record<string, boolean>,
153
+ ),
154
+ });
155
+ },
156
+ };
157
+ };
158
+
159
+ export type MCPBindingClient<T extends ReturnType<typeof bindingClient>> =
160
+ ReturnType<T["forConnection"]>;
161
+
162
+ /**
163
+ * Creates a binding checker with full schema validation using structural subset checking.
164
+ *
165
+ * This performs strict compatibility checking:
166
+ * - For input schemas: Validates that the tool can accept what the binder requires (binder ⊆ tool)
167
+ * - For output schemas: Validates that the tool provides what the binder expects (binder ⊆ tool)
168
+ *
169
+ * @param binderTools - The binding definition to check against
170
+ * @returns A binding checker with an async isImplementedBy method
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * const checker = createBindingChecker(MY_BINDING);
175
+ * const isCompatible = await checker.isImplementedBy(availableTools);
176
+ * ```
177
+ */
178
+ export function createBindingChecker<TDefinition extends readonly ToolBinder[]>(
179
+ binderTools: TDefinition,
180
+ ): BindingChecker {
181
+ return {
182
+ isImplementedBy: (tools: ToolWithSchemas[]): boolean => {
183
+ for (const binderTool of binderTools) {
184
+ // Find matching tool by name (exact or regex)
185
+ const pattern =
186
+ typeof binderTool.name === "string"
187
+ ? new RegExp(`^${binderTool.name}$`)
188
+ : binderTool.name;
189
+
190
+ const matchedTool = tools.find((t) => pattern.test(t.name));
191
+
192
+ // Skip optional tools that aren't present
193
+ if (!matchedTool && binderTool.opt) {
194
+ continue;
195
+ }
196
+
197
+ // Required tool not found
198
+ if (!matchedTool) {
199
+ return false;
200
+ }
201
+
202
+ // === INPUT SCHEMA VALIDATION ===
203
+ // Tool must accept what binder requires
204
+ // Check: isSubset(binder, tool) - every value valid under binder is valid under tool
205
+ const binderInputSchema = normalizeToJsonSchema(binderTool.inputSchema);
206
+ const toolInputSchema = normalizeToJsonSchema(matchedTool.inputSchema);
207
+
208
+ if (binderInputSchema && toolInputSchema) {
209
+ // Check if binder input is a subset of tool input (tool accepts what binder requires)
210
+ if (!isSubset(binderInputSchema, toolInputSchema)) {
211
+ return false;
212
+ }
213
+ } else if (binderInputSchema && !toolInputSchema) {
214
+ // Binder requires input schema but tool doesn't have one
215
+ return false;
216
+ }
217
+
218
+ // === OUTPUT SCHEMA VALIDATION ===
219
+ // Tool must provide what binder expects (but can provide more)
220
+ // Check: isSubset(binder, tool) - tool provides at least what binder expects
221
+ const binderOutputSchema = normalizeToJsonSchema(
222
+ binderTool.outputSchema,
223
+ );
224
+ const toolOutputSchema = normalizeToJsonSchema(
225
+ matchedTool.outputSchema,
226
+ );
227
+
228
+ if (binderOutputSchema && toolOutputSchema) {
229
+ // Check if binder output is a subset of tool output (tool provides what binder expects)
230
+ if (!isSubset(binderOutputSchema, toolOutputSchema)) {
231
+ return false;
232
+ }
233
+ } else if (binderOutputSchema && !toolOutputSchema) {
234
+ // Binder expects output schema but tool doesn't have one
235
+ return false;
236
+ }
237
+ }
238
+ return true;
239
+ },
240
+ };
241
+ }
@@ -0,0 +1,3 @@
1
+ # Client Implementation
2
+
3
+ This folder is basically a duplicated implementation based on runtime/binder.ts client. We should duplicate for now since we are remodeling the way applications invoke mesh.
@@ -1,11 +1,21 @@
1
+ import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
1
2
  import {
2
- StreamableHTTPClientTransport
3
+ StreamableHTTPClientTransport,
4
+ type StreamableHTTPClientTransportOptions,
3
5
  } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
- class HTTPClientTransport extends StreamableHTTPClientTransport {
5
- constructor(url, opts) {
6
+
7
+ export class HTTPClientTransport extends StreamableHTTPClientTransport {
8
+ constructor(url: URL, opts?: StreamableHTTPClientTransportOptions) {
6
9
  super(url, opts);
7
10
  }
8
- send(message, options) {
11
+
12
+ override send(
13
+ message: JSONRPCMessage,
14
+ options?: {
15
+ resumptionToken?: string;
16
+ onresumptiontoken?: (token: string) => void;
17
+ },
18
+ ): Promise<void> {
9
19
  const mockAction = getMockActionFor(message);
10
20
  if (mockAction?.type === "emit") {
11
21
  this.onmessage?.(mockAction.message);
@@ -17,9 +27,15 @@ class HTTPClientTransport extends StreamableHTTPClientTransport {
17
27
  return super.send(message, options);
18
28
  }
19
29
  }
20
- function getMockActionFor(message) {
30
+
31
+ type MockAction =
32
+ | { type: "emit"; message: JSONRPCMessage }
33
+ | { type: "suppress" };
34
+
35
+ function getMockActionFor(message: JSONRPCMessage): MockAction | null {
21
36
  const m = message;
22
37
  if (!m || typeof m !== "object" || !("method" in m)) return null;
38
+
23
39
  switch (m.method) {
24
40
  case "initialize": {
25
41
  const protocolVersion = m?.params?.protocolVersion;
@@ -30,12 +46,12 @@ function getMockActionFor(message) {
30
46
  result: {
31
47
  protocolVersion,
32
48
  capabilities: { tools: {} },
33
- serverInfo: { name: "deco-chat-server", version: "1.0.0" }
49
+ serverInfo: { name: "deco-chat-server", version: "1.0.0" },
34
50
  },
35
51
  jsonrpc: m.jsonrpc ?? "2.0",
36
52
  // @ts-expect-error - id is not typed
37
- id: m.id
38
- }
53
+ id: m.id,
54
+ } as JSONRPCMessage,
39
55
  };
40
56
  }
41
57
  case "notifications/roots/list_changed":
@@ -48,7 +64,3 @@ function getMockActionFor(message) {
48
64
  return null;
49
65
  }
50
66
  }
51
- export {
52
- HTTPClientTransport
53
- };
54
- //# sourceMappingURL=http-client-transport.js.map
@@ -0,0 +1 @@
1
+ export { HTTPClientTransport } from "./http-client-transport";
@@ -0,0 +1,149 @@
1
+ import {
2
+ Client as BaseClient,
3
+ ClientOptions,
4
+ } from "@modelcontextprotocol/sdk/client/index.js";
5
+ import {
6
+ SSEClientTransport,
7
+ SSEClientTransportOptions,
8
+ } from "@modelcontextprotocol/sdk/client/sse.js";
9
+ import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
10
+ import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
11
+ import {
12
+ Implementation,
13
+ ListToolsRequest,
14
+ ListToolsResultSchema,
15
+ } from "@modelcontextprotocol/sdk/types.js";
16
+ import { MCPConnection } from "../connection";
17
+ import { HTTPClientTransport } from "./http-client-transport";
18
+
19
+ /**
20
+ * WARNNING: This is a hack to prevent schema compilation errors.
21
+ * More info at: https://github.com/modelcontextprotocol/typescript-sdk/issues/923
22
+ *
23
+ * Make sure to keep this updated with the right version of the SDK.
24
+ * https://github.com/modelcontextprotocol/typescript-sdk/blob/bf817939917277a4c59f2e19e7b44b8dd7ff140c/src/client/index.ts#L480
25
+ */
26
+ class Client extends BaseClient {
27
+ constructor(_clientInfo: Implementation, options?: ClientOptions) {
28
+ super(_clientInfo, options);
29
+ }
30
+
31
+ override async listTools(
32
+ params?: ListToolsRequest["params"],
33
+ options?: RequestOptions,
34
+ ) {
35
+ const result = await this.request(
36
+ { method: "tools/list", params },
37
+ ListToolsResultSchema,
38
+ options,
39
+ );
40
+
41
+ return result;
42
+ }
43
+ }
44
+
45
+ export interface ServerClient {
46
+ client: Client;
47
+ callStreamableTool: (tool: string, args: unknown) => Promise<Response>;
48
+ }
49
+ export const createServerClient = async (
50
+ mcpServer: { connection: MCPConnection; name?: string },
51
+ signal?: AbortSignal,
52
+ extraHeaders?: Record<string, string>,
53
+ ): Promise<ServerClient> => {
54
+ const transport = createTransport(mcpServer.connection, signal, extraHeaders);
55
+
56
+ if (!transport) {
57
+ throw new Error("Unknown MCP connection type");
58
+ }
59
+
60
+ const client = new Client({
61
+ name: mcpServer?.name ?? "MCP Client",
62
+ version: "1.0.0",
63
+ });
64
+
65
+ await client.connect(transport);
66
+
67
+ return {
68
+ client,
69
+ callStreamableTool: (tool, args) => {
70
+ if (mcpServer.connection.type !== "HTTP") {
71
+ throw new Error("HTTP connection required");
72
+ }
73
+
74
+ const headers = new Headers(extraHeaders);
75
+
76
+ if (!headers.has("Authorization")) {
77
+ headers.set("Authorization", `Bearer ${mcpServer.connection.token}`);
78
+ }
79
+
80
+ for (const [key, value] of Object.entries(
81
+ mcpServer.connection.headers ?? {},
82
+ )) {
83
+ headers.set(key, value);
84
+ }
85
+
86
+ return fetch(mcpServer.connection.url + `/call-tool/${tool}`, {
87
+ method: "POST",
88
+ redirect: "manual",
89
+ body: JSON.stringify(args),
90
+ headers,
91
+ });
92
+ },
93
+ };
94
+ };
95
+
96
+ export const createTransport = (
97
+ connection: MCPConnection,
98
+ signal?: AbortSignal,
99
+ extraHeaders?: Record<string, string>,
100
+ ) => {
101
+ if (connection.type === "Websocket") {
102
+ return new WebSocketClientTransport(new URL(connection.url));
103
+ }
104
+
105
+ if (connection.type !== "SSE" && connection.type !== "HTTP") {
106
+ return null;
107
+ }
108
+
109
+ const authHeaders: Record<string, string> = connection.token
110
+ ? { authorization: `Bearer ${connection.token}` }
111
+ : {};
112
+
113
+ const headers: Record<string, string> = {
114
+ ...authHeaders,
115
+ ...(extraHeaders ?? {}),
116
+ ...("headers" in connection ? connection.headers || {} : {}),
117
+ };
118
+
119
+ if (connection.type === "SSE") {
120
+ const config: SSEClientTransportOptions = {
121
+ requestInit: { headers, signal },
122
+ };
123
+
124
+ if (connection.token) {
125
+ config.eventSourceInit = {
126
+ fetch: (req, init) => {
127
+ return fetch(req, {
128
+ ...init,
129
+ headers: {
130
+ ...headers,
131
+ Accept: "text/event-stream",
132
+ },
133
+ signal,
134
+ });
135
+ },
136
+ };
137
+ }
138
+
139
+ return new SSEClientTransport(new URL(connection.url), config);
140
+ }
141
+ return new HTTPClientTransport(new URL(connection.url), {
142
+ requestInit: {
143
+ headers,
144
+ signal,
145
+ // @ts-ignore - this is a valid option for fetch
146
+ credentials: "include",
147
+ },
148
+ });
149
+ };
@@ -0,0 +1,93 @@
1
+ /* oxlint-disable no-explicit-any */
2
+ import { z } from "zod";
3
+ import type { MCPConnection } from "../connection";
4
+ import { createMCPClientProxy } from "./proxy";
5
+
6
+ export interface FetchOptions extends RequestInit {
7
+ path?: string;
8
+ segments?: string[];
9
+ }
10
+
11
+ // Default fetcher instance with API_SERVER_URL and API_HEADERS
12
+ export const MCPClient = new Proxy(
13
+ {} as {
14
+ forConnection: <TDefinition extends readonly ToolBinder[]>(
15
+ connection: MCPConnection,
16
+ ) => MCPClientFetchStub<TDefinition>;
17
+ },
18
+ {
19
+ get(_, name) {
20
+ if (name === "toJSON") {
21
+ return null;
22
+ }
23
+
24
+ if (name === "forConnection") {
25
+ return <TDefinition extends readonly ToolBinder[]>(
26
+ connection: MCPConnection,
27
+ ) =>
28
+ createMCPFetchStub<TDefinition>({
29
+ connection,
30
+ });
31
+ }
32
+ return global[name as keyof typeof global];
33
+ },
34
+ },
35
+ );
36
+
37
+ import type { ToolBinder } from "../binder";
38
+ export type { ToolBinder };
39
+
40
+ export const isStreamableToolBinder = (
41
+ toolBinder: ToolBinder,
42
+ ): toolBinder is ToolBinder<string, any, any, true> => {
43
+ return toolBinder.streamable === true;
44
+ };
45
+ export type MCPClientStub<TDefinition extends readonly ToolBinder[]> = {
46
+ [K in TDefinition[number] as K["name"]]: K extends ToolBinder<
47
+ string,
48
+ infer TInput,
49
+ infer TReturn
50
+ >
51
+ ? (params: TInput, init?: RequestInit) => Promise<TReturn>
52
+ : never;
53
+ };
54
+
55
+ export type MCPClientFetchStub<TDefinition extends readonly ToolBinder[]> = {
56
+ [K in TDefinition[number] as K["name"]]: K["streamable"] extends true
57
+ ? K extends ToolBinder<string, infer TInput, any, true>
58
+ ? (params: TInput, init?: RequestInit) => Promise<Response>
59
+ : never
60
+ : K extends ToolBinder<string, infer TInput, infer TReturn, any>
61
+ ? (params: TInput, init?: RequestInit) => Promise<Awaited<TReturn>>
62
+ : never;
63
+ };
64
+
65
+ export interface MCPClientRaw {
66
+ callTool: (tool: string, args: unknown) => Promise<unknown>;
67
+ listTools: () => Promise<
68
+ {
69
+ name: string;
70
+ inputSchema: any;
71
+ outputSchema?: any;
72
+ description: string;
73
+ }[]
74
+ >;
75
+ }
76
+ export type JSONSchemaToZodConverter = (jsonSchema: any) => z.ZodTypeAny;
77
+ export interface CreateStubAPIOptions {
78
+ connection: MCPConnection;
79
+ streamable?: Record<string, boolean>;
80
+ debugId?: () => string;
81
+ getErrorByStatusCode?: (
82
+ statusCode: number,
83
+ message?: string,
84
+ traceId?: string,
85
+ errorObject?: unknown,
86
+ ) => Error;
87
+ }
88
+
89
+ export function createMCPFetchStub<TDefinition extends readonly ToolBinder[]>(
90
+ options: CreateStubAPIOptions,
91
+ ): MCPClientFetchStub<TDefinition> {
92
+ return createMCPClientProxy<MCPClientFetchStub<TDefinition>>(options);
93
+ }