@decocms/bindings 1.0.1-alpha.4 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +8 -11
- package/src/core/binder.ts +15 -76
- package/src/core/client/index.ts +10 -0
- package/src/core/client/mcp-client.ts +18 -5
- package/src/core/client/mcp.ts +48 -11
- package/src/core/client/proxy.ts +64 -48
- package/src/index.ts +57 -0
- package/src/well-known/assistant.ts +87 -0
- package/src/well-known/collections.ts +84 -99
- package/src/well-known/event-bus.ts +454 -0
- package/src/well-known/event-subscriber.ts +259 -0
- package/src/well-known/language-model.ts +216 -5
- package/src/well-known/mcp.ts +2 -1
- package/src/well-known/prompt.ts +110 -0
- package/src/well-known/registry.ts +128 -0
- package/src/well-known/workflow.ts +669 -0
- package/test/index.test.ts +3 -2
- package/test/mcp.test.ts +1 -1
- package/src/core/subset.ts +0 -514
- package/src/well-known/agent.ts +0 -60
- package/vitest.config.ts +0 -8
package/README.md
CHANGED
|
@@ -621,14 +621,14 @@ This will generate ESM output in the `dist/` directory with TypeScript declarati
|
|
|
621
621
|
|
|
622
622
|
### Testing
|
|
623
623
|
|
|
624
|
-
Tests are written with [
|
|
624
|
+
Tests are written with [Bun](https://bun.com/docs/test):
|
|
625
625
|
|
|
626
626
|
```bash
|
|
627
627
|
# Run tests once
|
|
628
|
-
bun
|
|
628
|
+
bun test
|
|
629
629
|
|
|
630
630
|
# Watch mode
|
|
631
|
-
bun
|
|
631
|
+
bun test --watch
|
|
632
632
|
```
|
|
633
633
|
|
|
634
634
|
## Publishing
|
package/package.json
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/bindings",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"
|
|
7
|
-
"test
|
|
6
|
+
"check": "tsc --noEmit",
|
|
7
|
+
"test": "bun test"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@modelcontextprotocol/sdk": "
|
|
10
|
+
"@modelcontextprotocol/sdk": "1.20.2",
|
|
11
11
|
"zod": "^3.25.76",
|
|
12
|
-
"zod-from-json-schema": "^0.0.5"
|
|
13
|
-
"zod-to-json-schema": "3.25.0"
|
|
12
|
+
"zod-from-json-schema": "^0.0.5"
|
|
14
13
|
},
|
|
15
14
|
"exports": {
|
|
16
15
|
".": "./src/index.ts",
|
|
17
|
-
"./models": "./src/well-known/models.ts",
|
|
18
16
|
"./collections": "./src/well-known/collections.ts",
|
|
19
17
|
"./llm": "./src/well-known/language-model.ts",
|
|
20
18
|
"./connection": "./src/core/connection.ts",
|
|
21
19
|
"./client": "./src/core/client/index.ts",
|
|
22
20
|
"./mcp": "./src/well-known/mcp.ts",
|
|
23
|
-
"./
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"vitest": "3.2.4"
|
|
21
|
+
"./assistant": "./src/well-known/assistant.ts",
|
|
22
|
+
"./prompt": "./src/well-known/prompt.ts",
|
|
23
|
+
"./workflow": "./src/well-known/workflow.ts"
|
|
27
24
|
},
|
|
28
25
|
"engines": {
|
|
29
26
|
"node": ">=24.0.0"
|
package/src/core/binder.ts
CHANGED
|
@@ -6,47 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ZodType } from "zod";
|
|
9
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
10
9
|
import { createMCPFetchStub, MCPClientFetchStub } from "./client/mcp";
|
|
10
|
+
import { ServerClient } from "./client/mcp-client";
|
|
11
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
|
-
function isZodSchema(value: unknown): value is ZodType<unknown> {
|
|
20
|
-
return (
|
|
21
|
-
value !== null &&
|
|
22
|
-
typeof value === "object" &&
|
|
23
|
-
"_def" in value &&
|
|
24
|
-
typeof (value as Record<string, unknown>)._def === "object"
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Normalizes a schema to JSON Schema format.
|
|
30
|
-
* Accepts either a Zod schema or a JSON schema and returns a JSON schema.
|
|
31
|
-
*
|
|
32
|
-
* @param schema - A Zod schema or JSON schema
|
|
33
|
-
* @returns The JSON schema representation, or null if input is null/undefined
|
|
34
|
-
*/
|
|
35
|
-
export function normalizeToJsonSchema(
|
|
36
|
-
schema: ZodType<unknown> | JsonSchema | null | undefined,
|
|
37
|
-
): JsonSchema | null {
|
|
38
|
-
if (schema == null) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (isZodSchema(schema)) {
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
-
return zodToJsonSchema(schema as any) as JsonSchema;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Already a JSON schema
|
|
48
|
-
return schema as JsonSchema;
|
|
49
|
-
}
|
|
50
12
|
|
|
51
13
|
/**
|
|
52
14
|
* ToolBinder defines a single tool within a binding.
|
|
@@ -138,6 +100,18 @@ export const bindingClient = <TDefinition extends readonly ToolBinder[]>(
|
|
|
138
100
|
) => {
|
|
139
101
|
return {
|
|
140
102
|
...createBindingChecker(binder),
|
|
103
|
+
forClient: (client: ServerClient): MCPClientFetchStub<TDefinition> => {
|
|
104
|
+
return createMCPFetchStub<TDefinition>({
|
|
105
|
+
client,
|
|
106
|
+
streamable: binder.reduce(
|
|
107
|
+
(acc, tool) => {
|
|
108
|
+
acc[tool.name] = tool.streamable === true;
|
|
109
|
+
return acc;
|
|
110
|
+
},
|
|
111
|
+
{} as Record<string, boolean>,
|
|
112
|
+
),
|
|
113
|
+
});
|
|
114
|
+
},
|
|
141
115
|
forConnection: (
|
|
142
116
|
mcpConnection: MCPConnection,
|
|
143
117
|
): MCPClientFetchStub<TDefinition> => {
|
|
@@ -155,7 +129,7 @@ export const bindingClient = <TDefinition extends readonly ToolBinder[]>(
|
|
|
155
129
|
};
|
|
156
130
|
};
|
|
157
131
|
|
|
158
|
-
export type MCPBindingClient<T extends ReturnType<typeof bindingClient
|
|
132
|
+
export type MCPBindingClient<T extends ReturnType<typeof bindingClient<any>>> =
|
|
159
133
|
ReturnType<T["forConnection"]>;
|
|
160
134
|
|
|
161
135
|
/**
|
|
@@ -197,42 +171,7 @@ export function createBindingChecker<TDefinition extends readonly ToolBinder[]>(
|
|
|
197
171
|
if (!matchedTool) {
|
|
198
172
|
return false;
|
|
199
173
|
}
|
|
200
|
-
|
|
201
|
-
// === INPUT SCHEMA VALIDATION ===
|
|
202
|
-
// Tool must accept what binder requires
|
|
203
|
-
// Check: isSubset(binder, tool) - every value valid under binder is valid under tool
|
|
204
|
-
const binderInputSchema = normalizeToJsonSchema(binderTool.inputSchema);
|
|
205
|
-
const toolInputSchema = normalizeToJsonSchema(matchedTool.inputSchema);
|
|
206
|
-
|
|
207
|
-
if (binderInputSchema && toolInputSchema) {
|
|
208
|
-
// Check if binder input is a subset of tool input (tool accepts what binder requires)
|
|
209
|
-
if (!isSubset(binderInputSchema, toolInputSchema)) {
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
} else if (binderInputSchema && !toolInputSchema) {
|
|
213
|
-
// Binder requires input schema but tool doesn't have one
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// === OUTPUT SCHEMA VALIDATION ===
|
|
218
|
-
// Tool must provide what binder expects (but can provide more)
|
|
219
|
-
// Check: isSubset(binder, tool) - tool provides at least what binder expects
|
|
220
|
-
const binderOutputSchema = normalizeToJsonSchema(
|
|
221
|
-
binderTool.outputSchema,
|
|
222
|
-
);
|
|
223
|
-
const toolOutputSchema = normalizeToJsonSchema(
|
|
224
|
-
matchedTool.outputSchema,
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
if (binderOutputSchema && toolOutputSchema) {
|
|
228
|
-
// Check if binder output is a subset of tool output (tool provides what binder expects)
|
|
229
|
-
if (!isSubset(binderOutputSchema, toolOutputSchema)) {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
} else if (binderOutputSchema && !toolOutputSchema) {
|
|
233
|
-
// Binder expects output schema but tool doesn't have one
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
174
|
+
// FIXME @mcandeia Zod to JSONSchema converstion is creating inconsistent schemas
|
|
236
175
|
}
|
|
237
176
|
return true;
|
|
238
177
|
},
|
package/src/core/client/index.ts
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
1
|
export { HTTPClientTransport } from "./http-client-transport";
|
|
2
|
+
export {
|
|
3
|
+
createMCPFetchStub,
|
|
4
|
+
isStreamableToolBinder,
|
|
5
|
+
MCPClient,
|
|
6
|
+
type CreateStubAPIOptions,
|
|
7
|
+
type MCPClientFetchStub,
|
|
8
|
+
type MCPClientStub,
|
|
9
|
+
type ServerClient,
|
|
10
|
+
} from "./mcp";
|
|
11
|
+
export { bindingClient } from "../binder";
|
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
|
|
10
10
|
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
11
11
|
import {
|
|
12
|
+
CallToolRequest,
|
|
12
13
|
Implementation,
|
|
13
14
|
ListToolsRequest,
|
|
15
|
+
ListToolsResult,
|
|
14
16
|
ListToolsResultSchema,
|
|
15
17
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
16
18
|
import { MCPConnection } from "../connection";
|
|
@@ -41,10 +43,16 @@ class Client extends BaseClient {
|
|
|
41
43
|
return result;
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
|
-
|
|
46
|
+
type CallToolResponse = Awaited<ReturnType<Client["callTool"]>>;
|
|
45
47
|
export interface ServerClient {
|
|
46
|
-
client:
|
|
47
|
-
|
|
48
|
+
client: {
|
|
49
|
+
callTool: (params: CallToolRequest["params"]) => Promise<CallToolResponse>;
|
|
50
|
+
listTools: () => Promise<ListToolsResult>;
|
|
51
|
+
};
|
|
52
|
+
callStreamableTool: (
|
|
53
|
+
tool: string,
|
|
54
|
+
args: Record<string, unknown>,
|
|
55
|
+
) => Promise<Response>;
|
|
48
56
|
}
|
|
49
57
|
export const createServerClient = async (
|
|
50
58
|
mcpServer: { connection: MCPConnection; name?: string },
|
|
@@ -83,7 +91,12 @@ export const createServerClient = async (
|
|
|
83
91
|
headers.set(key, value);
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
|
|
94
|
+
const url = new URL(mcpServer.connection.url);
|
|
95
|
+
// Trim trailing slashes from pathname, ensuring it starts with '/'
|
|
96
|
+
const trimmedPath = url.pathname.replace(/\/+$/, "") || "/";
|
|
97
|
+
url.pathname = `${trimmedPath}/call-tool/${encodeURIComponent(tool)}`;
|
|
98
|
+
|
|
99
|
+
return fetch(url.href, {
|
|
87
100
|
method: "POST",
|
|
88
101
|
redirect: "manual",
|
|
89
102
|
body: JSON.stringify(args),
|
|
@@ -93,7 +106,7 @@ export const createServerClient = async (
|
|
|
93
106
|
};
|
|
94
107
|
};
|
|
95
108
|
|
|
96
|
-
|
|
109
|
+
const createTransport = (
|
|
97
110
|
connection: MCPConnection,
|
|
98
111
|
signal?: AbortSignal,
|
|
99
112
|
extraHeaders?: Record<string, string>,
|
package/src/core/client/mcp.ts
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import type { MCPConnection } from "../connection";
|
|
4
4
|
import { createMCPClientProxy } from "./proxy";
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export type { ServerClient } from "./mcp-client";
|
|
6
|
+
export const isStreamableToolBinder = (
|
|
7
|
+
toolBinder: ToolBinder,
|
|
8
|
+
): toolBinder is ToolBinder<string, any, any, true> => {
|
|
9
|
+
return toolBinder.streamable === true;
|
|
10
|
+
};
|
|
10
11
|
|
|
11
12
|
// Default fetcher instance with API_SERVER_URL and API_HEADERS
|
|
12
13
|
export const MCPClient = new Proxy(
|
|
13
14
|
{} as {
|
|
15
|
+
forClient: <TDefinition extends readonly ToolBinder[]>(
|
|
16
|
+
client: ServerClient,
|
|
17
|
+
) => MCPClientFetchStub<TDefinition>;
|
|
14
18
|
forConnection: <TDefinition extends readonly ToolBinder[]>(
|
|
15
19
|
connection: MCPConnection,
|
|
16
20
|
) => MCPClientFetchStub<TDefinition>;
|
|
@@ -34,14 +38,16 @@ export const MCPClient = new Proxy(
|
|
|
34
38
|
},
|
|
35
39
|
);
|
|
36
40
|
|
|
41
|
+
export interface FetchOptions extends RequestInit {
|
|
42
|
+
path?: string;
|
|
43
|
+
segments?: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Default fetcher instance with API_SERVER_URL and API_HEADERS
|
|
37
47
|
import type { ToolBinder } from "../binder";
|
|
48
|
+
import { ServerClient } from "./mcp-client";
|
|
38
49
|
export type { ToolBinder };
|
|
39
50
|
|
|
40
|
-
export const isStreamableToolBinder = (
|
|
41
|
-
toolBinder: ToolBinder,
|
|
42
|
-
): toolBinder is ToolBinder<string, any, any, true> => {
|
|
43
|
-
return toolBinder.streamable === true;
|
|
44
|
-
};
|
|
45
51
|
export type MCPClientStub<TDefinition extends readonly ToolBinder[]> = {
|
|
46
52
|
[K in TDefinition[number] as K["name"]]: K extends ToolBinder<
|
|
47
53
|
string,
|
|
@@ -53,6 +59,16 @@ export type MCPClientStub<TDefinition extends readonly ToolBinder[]> = {
|
|
|
53
59
|
};
|
|
54
60
|
|
|
55
61
|
export type MCPClientFetchStub<TDefinition extends readonly ToolBinder[]> = {
|
|
62
|
+
listTools: () => Promise<
|
|
63
|
+
{
|
|
64
|
+
id: string;
|
|
65
|
+
inputSchema: z.ZodType;
|
|
66
|
+
outputSchema: z.ZodType;
|
|
67
|
+
description: string;
|
|
68
|
+
execute: (params: any) => Promise<any>;
|
|
69
|
+
}[]
|
|
70
|
+
>;
|
|
71
|
+
} & {
|
|
56
72
|
[K in TDefinition[number] as K["name"]]: K["streamable"] extends true
|
|
57
73
|
? K extends ToolBinder<string, infer TInput, any, true>
|
|
58
74
|
? (params: TInput, init?: RequestInit) => Promise<Response>
|
|
@@ -74,10 +90,28 @@ export interface MCPClientRaw {
|
|
|
74
90
|
>;
|
|
75
91
|
}
|
|
76
92
|
export type JSONSchemaToZodConverter = (jsonSchema: any) => z.ZodTypeAny;
|
|
77
|
-
|
|
93
|
+
|
|
94
|
+
export interface CreateStubForClientAPIOptions {
|
|
95
|
+
client: ServerClient;
|
|
96
|
+
streamable?: Record<string, boolean>;
|
|
97
|
+
debugId?: () => string;
|
|
98
|
+
getErrorByStatusCode?: (
|
|
99
|
+
statusCode: number,
|
|
100
|
+
message?: string,
|
|
101
|
+
traceId?: string,
|
|
102
|
+
errorObject?: unknown,
|
|
103
|
+
) => Error;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface CreateStubForConnectionAPIOptions {
|
|
78
107
|
connection: MCPConnection;
|
|
79
108
|
streamable?: Record<string, boolean>;
|
|
80
109
|
debugId?: () => string;
|
|
110
|
+
createServerClient?: (
|
|
111
|
+
mcpServer: { connection: MCPConnection; name?: string },
|
|
112
|
+
signal?: AbortSignal,
|
|
113
|
+
extraHeaders?: Record<string, string>,
|
|
114
|
+
) => ServerClient;
|
|
81
115
|
getErrorByStatusCode?: (
|
|
82
116
|
statusCode: number,
|
|
83
117
|
message?: string,
|
|
@@ -85,6 +119,9 @@ export interface CreateStubAPIOptions {
|
|
|
85
119
|
errorObject?: unknown,
|
|
86
120
|
) => Error;
|
|
87
121
|
}
|
|
122
|
+
export type CreateStubAPIOptions =
|
|
123
|
+
| CreateStubForClientAPIOptions
|
|
124
|
+
| CreateStubForConnectionAPIOptions;
|
|
88
125
|
|
|
89
126
|
export function createMCPFetchStub<TDefinition extends readonly ToolBinder[]>(
|
|
90
127
|
options: CreateStubAPIOptions,
|
package/src/core/client/proxy.ts
CHANGED
|
@@ -11,24 +11,49 @@ const safeParse = (content: string) => {
|
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
string
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
outputSchema?: any;
|
|
21
|
-
description: string;
|
|
22
|
-
}>
|
|
23
|
-
>
|
|
24
|
-
>();
|
|
14
|
+
type Tool = {
|
|
15
|
+
name: string;
|
|
16
|
+
inputSchema: any;
|
|
17
|
+
outputSchema?: any;
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
25
20
|
|
|
21
|
+
const toolsMap = new Map<string, Promise<Array<Tool>>>();
|
|
22
|
+
|
|
23
|
+
const mapTool = (
|
|
24
|
+
tool: Tool,
|
|
25
|
+
callToolFn: (input: any, toolName?: string) => Promise<any>,
|
|
26
|
+
) => {
|
|
27
|
+
return {
|
|
28
|
+
...tool,
|
|
29
|
+
id: tool.name,
|
|
30
|
+
inputSchema: tool.inputSchema
|
|
31
|
+
? convertJsonSchemaToZod(tool.inputSchema)
|
|
32
|
+
: undefined,
|
|
33
|
+
outputSchema: tool.outputSchema
|
|
34
|
+
? convertJsonSchemaToZod(tool.outputSchema)
|
|
35
|
+
: undefined,
|
|
36
|
+
execute: (input: any) => {
|
|
37
|
+
return callToolFn(input.context, tool.name);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
26
41
|
/**
|
|
27
42
|
* The base fetcher used to fetch the MCP from API.
|
|
28
43
|
*/
|
|
29
44
|
export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
30
45
|
options: CreateStubAPIOptions,
|
|
31
46
|
): T {
|
|
47
|
+
const createClient = (extraHeaders?: Record<string, string>) => {
|
|
48
|
+
if ("connection" in options) {
|
|
49
|
+
return createServerClient(
|
|
50
|
+
{ connection: options.connection },
|
|
51
|
+
undefined,
|
|
52
|
+
extraHeaders,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return options.client;
|
|
56
|
+
};
|
|
32
57
|
return new Proxy<T>({} as T, {
|
|
33
58
|
get(_, name) {
|
|
34
59
|
if (name === "toJSON") {
|
|
@@ -37,32 +62,28 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
|
37
62
|
if (typeof name !== "string") {
|
|
38
63
|
throw new Error("Name must be a string");
|
|
39
64
|
}
|
|
40
|
-
|
|
65
|
+
if (name === "listTools") {
|
|
66
|
+
return asCallableTools;
|
|
67
|
+
}
|
|
68
|
+
async function callToolFn(
|
|
69
|
+
args: Record<string, unknown>,
|
|
70
|
+
toolName = name,
|
|
71
|
+
) {
|
|
41
72
|
const debugId = options?.debugId?.();
|
|
42
73
|
const extraHeaders = debugId
|
|
43
74
|
? { "x-trace-debug-id": debugId }
|
|
44
75
|
: undefined;
|
|
45
76
|
|
|
46
|
-
const { client, callStreamableTool } = await
|
|
47
|
-
{ connection: options.connection },
|
|
48
|
-
undefined,
|
|
49
|
-
extraHeaders,
|
|
50
|
-
);
|
|
77
|
+
const { client, callStreamableTool } = await createClient(extraHeaders);
|
|
51
78
|
|
|
52
|
-
if (options?.streamable?.[String(
|
|
53
|
-
return callStreamableTool(String(
|
|
79
|
+
if (options?.streamable?.[String(toolName)]) {
|
|
80
|
+
return callStreamableTool(String(toolName), args);
|
|
54
81
|
}
|
|
55
82
|
|
|
56
|
-
const { structuredContent, isError, content } = await client.callTool(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
undefined,
|
|
62
|
-
{
|
|
63
|
-
timeout: 3000000,
|
|
64
|
-
},
|
|
65
|
-
);
|
|
83
|
+
const { structuredContent, isError, content } = await client.callTool({
|
|
84
|
+
name: String(toolName),
|
|
85
|
+
arguments: args as Record<string, unknown>,
|
|
86
|
+
});
|
|
66
87
|
|
|
67
88
|
if (isError) {
|
|
68
89
|
const maybeErrorMessage = (content as { text: string }[])?.[0]?.text;
|
|
@@ -85,7 +106,7 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
|
85
106
|
}
|
|
86
107
|
|
|
87
108
|
throw new Error(
|
|
88
|
-
`Tool ${String(
|
|
109
|
+
`Tool ${String(toolName)} returned an error: ${JSON.stringify(
|
|
89
110
|
structuredContent ?? content,
|
|
90
111
|
)}`,
|
|
91
112
|
);
|
|
@@ -93,10 +114,8 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
|
93
114
|
return structuredContent;
|
|
94
115
|
}
|
|
95
116
|
|
|
96
|
-
|
|
97
|
-
const { client } = await
|
|
98
|
-
connection: options.connection,
|
|
99
|
-
});
|
|
117
|
+
async function listToolsFn() {
|
|
118
|
+
const { client } = await createClient();
|
|
100
119
|
const { tools } = await client.listTools();
|
|
101
120
|
|
|
102
121
|
return tools as {
|
|
@@ -105,9 +124,12 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
|
105
124
|
outputSchema?: any;
|
|
106
125
|
description: string;
|
|
107
126
|
}[];
|
|
108
|
-
}
|
|
127
|
+
}
|
|
109
128
|
|
|
110
129
|
async function listToolsOnce() {
|
|
130
|
+
if (!("connection" in options)) {
|
|
131
|
+
return listToolsFn();
|
|
132
|
+
}
|
|
111
133
|
const conn = options.connection;
|
|
112
134
|
const key = JSON.stringify(conn);
|
|
113
135
|
|
|
@@ -124,6 +146,12 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
|
124
146
|
return;
|
|
125
147
|
}
|
|
126
148
|
}
|
|
149
|
+
|
|
150
|
+
async function asCallableTools() {
|
|
151
|
+
const tools = (await listToolsOnce()) ?? [];
|
|
152
|
+
return tools.map((tool) => mapTool(tool, callToolFn));
|
|
153
|
+
}
|
|
154
|
+
|
|
127
155
|
callToolFn.asTool = async () => {
|
|
128
156
|
const tools = (await listToolsOnce()) ?? [];
|
|
129
157
|
const tool = tools.find((t) => t.name === name);
|
|
@@ -131,19 +159,7 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
|
|
|
131
159
|
throw new Error(`Tool ${name} not found`);
|
|
132
160
|
}
|
|
133
161
|
|
|
134
|
-
return
|
|
135
|
-
...tool,
|
|
136
|
-
id: tool.name,
|
|
137
|
-
inputSchema: tool.inputSchema
|
|
138
|
-
? convertJsonSchemaToZod(tool.inputSchema)
|
|
139
|
-
: undefined,
|
|
140
|
-
outputSchema: tool.outputSchema
|
|
141
|
-
? convertJsonSchemaToZod(tool.outputSchema)
|
|
142
|
-
: undefined,
|
|
143
|
-
execute: (input: any) => {
|
|
144
|
-
return callToolFn(input.context);
|
|
145
|
-
},
|
|
146
|
-
};
|
|
162
|
+
return mapTool(tool, callToolFn);
|
|
147
163
|
};
|
|
148
164
|
return callToolFn;
|
|
149
165
|
},
|
package/src/index.ts
CHANGED
|
@@ -13,3 +13,60 @@ export {
|
|
|
13
13
|
type ToolBinder,
|
|
14
14
|
type ToolWithSchemas,
|
|
15
15
|
} from "./core/binder";
|
|
16
|
+
|
|
17
|
+
// Re-export registry binding types
|
|
18
|
+
export {
|
|
19
|
+
MCPRegistryServerSchema,
|
|
20
|
+
type RegistryAppCollectionEntity,
|
|
21
|
+
REGISTRY_APP_BINDING,
|
|
22
|
+
} from "./well-known/registry";
|
|
23
|
+
|
|
24
|
+
// Re-export event subscriber binding types (for connections that receive events)
|
|
25
|
+
export {
|
|
26
|
+
CloudEventSchema,
|
|
27
|
+
type CloudEvent,
|
|
28
|
+
EventResultSchema,
|
|
29
|
+
type EventResult,
|
|
30
|
+
OnEventsInputSchema,
|
|
31
|
+
type OnEventsInput,
|
|
32
|
+
OnEventsOutputSchema,
|
|
33
|
+
type OnEventsOutput,
|
|
34
|
+
EVENT_SUBSCRIBER_BINDING,
|
|
35
|
+
EventSubscriberBinding,
|
|
36
|
+
type EventSubscriberBindingClient,
|
|
37
|
+
} from "./well-known/event-subscriber";
|
|
38
|
+
|
|
39
|
+
// Re-export event bus binding types (for interacting with an event bus)
|
|
40
|
+
export {
|
|
41
|
+
EventPublishInputSchema,
|
|
42
|
+
type EventPublishInput,
|
|
43
|
+
EventPublishOutputSchema,
|
|
44
|
+
type EventPublishOutput,
|
|
45
|
+
EventSubscribeInputSchema,
|
|
46
|
+
type EventSubscribeInput,
|
|
47
|
+
EventSubscribeOutputSchema,
|
|
48
|
+
type EventSubscribeOutput,
|
|
49
|
+
EventUnsubscribeInputSchema,
|
|
50
|
+
type EventUnsubscribeInput,
|
|
51
|
+
EventUnsubscribeOutputSchema,
|
|
52
|
+
type EventUnsubscribeOutput,
|
|
53
|
+
EventCancelInputSchema,
|
|
54
|
+
type EventCancelInput,
|
|
55
|
+
EventCancelOutputSchema,
|
|
56
|
+
type EventCancelOutput,
|
|
57
|
+
EventAckInputSchema,
|
|
58
|
+
type EventAckInput,
|
|
59
|
+
EventAckOutputSchema,
|
|
60
|
+
type EventAckOutput,
|
|
61
|
+
SubscriptionItemSchema,
|
|
62
|
+
type SubscriptionItem,
|
|
63
|
+
SubscriptionDetailSchema,
|
|
64
|
+
type SubscriptionDetail,
|
|
65
|
+
EventSyncSubscriptionsInputSchema,
|
|
66
|
+
type EventSyncSubscriptionsInput,
|
|
67
|
+
EventSyncSubscriptionsOutputSchema,
|
|
68
|
+
type EventSyncSubscriptionsOutput,
|
|
69
|
+
EVENT_BUS_BINDING,
|
|
70
|
+
EventBusBinding,
|
|
71
|
+
type EventBusBindingClient,
|
|
72
|
+
} from "./well-known/event-bus";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assistants Well-Known Binding
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface for AI assistant providers.
|
|
5
|
+
* Any MCP that implements this binding can provide configurable AI assistants
|
|
6
|
+
* with a system prompt and runtime configuration (gateway + model).
|
|
7
|
+
*
|
|
8
|
+
* This binding uses collection bindings for full CRUD operations.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import type { Binder } from "../core/binder";
|
|
13
|
+
import {
|
|
14
|
+
BaseCollectionEntitySchema,
|
|
15
|
+
createCollectionBindings,
|
|
16
|
+
} from "./collections";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Assistant entity schema for AI assistants
|
|
20
|
+
* Extends BaseCollectionEntitySchema with assistant-specific fields
|
|
21
|
+
* Base schema already includes: id, title, created_at, updated_at, created_by, updated_by
|
|
22
|
+
*/
|
|
23
|
+
export const AssistantSchema = BaseCollectionEntitySchema.extend({
|
|
24
|
+
/**
|
|
25
|
+
* Assistant avatar.
|
|
26
|
+
* Can be a regular URL or a data URI.
|
|
27
|
+
*/
|
|
28
|
+
avatar: z
|
|
29
|
+
.string()
|
|
30
|
+
.describe("URL or data URI to the assistant's avatar image"),
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* System prompt that defines the assistant's behavior.
|
|
34
|
+
*/
|
|
35
|
+
system_prompt: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("System prompt that defines the assistant's behavior"),
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Selected gateway for this assistant (single gateway).
|
|
41
|
+
* This gateway determines which MCP tools are exposed to chat.
|
|
42
|
+
*/
|
|
43
|
+
gateway_id: z.string().describe("Gateway ID to use for this assistant"),
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Selected model for this assistant (model id + the connection where it lives).
|
|
47
|
+
* This allows the UI/runtime to call the correct model provider connection.
|
|
48
|
+
*/
|
|
49
|
+
model: z
|
|
50
|
+
.object({
|
|
51
|
+
id: z.string().describe("Model ID"),
|
|
52
|
+
connectionId: z
|
|
53
|
+
.string()
|
|
54
|
+
.describe("Connection ID that provides the model"),
|
|
55
|
+
})
|
|
56
|
+
.describe("Selected model reference for this assistant"),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* ASSISTANT Collection Binding
|
|
61
|
+
*
|
|
62
|
+
* Collection bindings for assistants.
|
|
63
|
+
* Provides full CRUD operations (LIST, GET, CREATE, UPDATE, DELETE) for AI assistants.
|
|
64
|
+
*/
|
|
65
|
+
export const ASSISTANTS_COLLECTION_BINDING = createCollectionBindings(
|
|
66
|
+
"assistant",
|
|
67
|
+
AssistantSchema,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* ASSISTANTS Binding
|
|
72
|
+
*
|
|
73
|
+
* Defines the interface for AI assistant providers.
|
|
74
|
+
* Any MCP that implements this binding can provide configurable AI assistants.
|
|
75
|
+
*
|
|
76
|
+
* Required tools:
|
|
77
|
+
* - COLLECTION_ASSISTANT_LIST: List available AI assistants with their configurations
|
|
78
|
+
* - COLLECTION_ASSISTANT_GET: Get a single assistant by ID (includes system_prompt, gateway_id, model)
|
|
79
|
+
*
|
|
80
|
+
* Optional tools:
|
|
81
|
+
* - COLLECTION_ASSISTANT_CREATE: Create a new assistant
|
|
82
|
+
* - COLLECTION_ASSISTANT_UPDATE: Update an existing assistant
|
|
83
|
+
* - COLLECTION_ASSISTANT_DELETE: Delete an assistant
|
|
84
|
+
*/
|
|
85
|
+
export const ASSISTANTS_BINDING = [
|
|
86
|
+
...ASSISTANTS_COLLECTION_BINDING,
|
|
87
|
+
] as const satisfies Binder;
|