@copilotkit/runtime 1.9.2-next.22 → 1.9.2-next.24
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/CHANGELOG.md +19 -0
- package/dist/{chunk-MQNSNCJU.mjs → chunk-ECQRTXJX.mjs} +2 -2
- package/dist/{chunk-Z2HSWVJ6.mjs → chunk-FR4C3TPU.mjs} +2 -2
- package/dist/{chunk-WLLDTXG6.mjs → chunk-GAUZHH2X.mjs} +73 -34
- package/dist/chunk-GAUZHH2X.mjs.map +1 -0
- package/dist/{chunk-YSRDTT3V.mjs → chunk-S2YGYQDH.mjs} +2 -2
- package/dist/{chunk-UOSL5GYI.mjs → chunk-TBVA4IGX.mjs} +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +65 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -6
- package/dist/lib/index.d.ts +153 -3
- package/dist/lib/index.js +65 -26
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +6 -6
- package/dist/lib/integrations/index.d.ts +2 -2
- package/dist/lib/integrations/index.js +10 -10
- package/dist/lib/integrations/index.js.map +1 -1
- package/dist/lib/integrations/index.mjs +5 -5
- package/dist/lib/integrations/nest/index.d.ts +1 -1
- package/dist/lib/integrations/nest/index.js +10 -10
- package/dist/lib/integrations/nest/index.js.map +1 -1
- package/dist/lib/integrations/nest/index.mjs +3 -3
- package/dist/lib/integrations/node-express/index.d.ts +1 -1
- package/dist/lib/integrations/node-express/index.js +10 -10
- package/dist/lib/integrations/node-express/index.js.map +1 -1
- package/dist/lib/integrations/node-express/index.mjs +3 -3
- package/dist/lib/integrations/node-http/index.d.ts +1 -1
- package/dist/lib/integrations/node-http/index.js +10 -10
- package/dist/lib/integrations/node-http/index.js.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs +2 -2
- package/dist/{shared-41d4988d.d.ts → shared-6b6dbf8b.d.ts} +1 -3
- package/package.json +11 -11
- package/src/lib/runtime/__tests__/mcp-tools-utils.test.ts +464 -0
- package/src/lib/runtime/copilot-runtime.ts +1 -3
- package/src/lib/runtime/mcp-tools-utils.ts +84 -18
- package/dist/chunk-WLLDTXG6.mjs.map +0 -1
- /package/dist/{chunk-MQNSNCJU.mjs.map → chunk-ECQRTXJX.mjs.map} +0 -0
- /package/dist/{chunk-Z2HSWVJ6.mjs.map → chunk-FR4C3TPU.mjs.map} +0 -0
- /package/dist/{chunk-YSRDTT3V.mjs.map → chunk-S2YGYQDH.mjs.map} +0 -0
- /package/dist/{chunk-UOSL5GYI.mjs.map → chunk-TBVA4IGX.mjs.map} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
copilotRuntimeNodeHttpEndpoint
|
|
3
|
-
} from "../../../chunk-
|
|
4
|
-
import "../../../chunk-SHBDMA63.mjs";
|
|
3
|
+
} from "../../../chunk-GAUZHH2X.mjs";
|
|
5
4
|
import "../../../chunk-QLLV2QVK.mjs";
|
|
6
5
|
import "../../../chunk-XWBDEXDA.mjs";
|
|
7
6
|
import "../../../chunk-AMUJQ6IR.mjs";
|
|
7
|
+
import "../../../chunk-SHBDMA63.mjs";
|
|
8
8
|
import "../../../chunk-2OZAGFV3.mjs";
|
|
9
9
|
import "../../../chunk-FHD4JECV.mjs";
|
|
10
10
|
export {
|
|
@@ -142,9 +142,7 @@ interface MCPTool {
|
|
|
142
142
|
};
|
|
143
143
|
};
|
|
144
144
|
/** The function to call to execute the tool on the MCP server. */
|
|
145
|
-
execute(
|
|
146
|
-
params: any;
|
|
147
|
-
}): Promise<any>;
|
|
145
|
+
execute(params: any): Promise<any>;
|
|
148
146
|
}
|
|
149
147
|
/**
|
|
150
148
|
* Defines the contract for *any* MCP client implementation the user might provide.
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
-
"version": "1.9.2-next.
|
|
12
|
+
"version": "1.9.2-next.24",
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"main": "./dist/index.js",
|
|
15
15
|
"module": "./dist/index.mjs",
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"tsconfig": "1.4.6"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@ag-ui/client": "0.0.
|
|
41
|
-
"@ag-ui/core": "0.0.
|
|
42
|
-
"@ag-ui/encoder": "0.0.
|
|
43
|
-
"@ag-ui/langgraph": "0.0.
|
|
44
|
-
"@ag-ui/proto": "0.0.
|
|
40
|
+
"@ag-ui/client": "0.0.34",
|
|
41
|
+
"@ag-ui/core": "0.0.34",
|
|
42
|
+
"@ag-ui/encoder": "0.0.34",
|
|
43
|
+
"@ag-ui/langgraph": "0.0.6",
|
|
44
|
+
"@ag-ui/proto": "0.0.34",
|
|
45
45
|
"@anthropic-ai/sdk": "^0.27.3",
|
|
46
46
|
"@graphql-yoga/plugin-defer-stream": "^3.3.1",
|
|
47
47
|
"@langchain/aws": "^0.1.9",
|
|
@@ -66,13 +66,13 @@
|
|
|
66
66
|
"rxjs": "7.8.1",
|
|
67
67
|
"type-graphql": "2.0.0-rc.1",
|
|
68
68
|
"zod": "^3.23.3",
|
|
69
|
-
"@copilotkit/shared": "1.9.2-next.
|
|
69
|
+
"@copilotkit/shared": "1.9.2-next.24"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@ag-ui/client": ">=0.0.
|
|
73
|
-
"@ag-ui/core": ">=0.0.
|
|
74
|
-
"@ag-ui/encoder": ">=0.0.
|
|
75
|
-
"@ag-ui/proto": ">=0.0.
|
|
72
|
+
"@ag-ui/client": ">=0.0.34",
|
|
73
|
+
"@ag-ui/core": ">=0.0.34",
|
|
74
|
+
"@ag-ui/encoder": ">=0.0.34",
|
|
75
|
+
"@ag-ui/proto": ">=0.0.34"
|
|
76
76
|
},
|
|
77
77
|
"keywords": [
|
|
78
78
|
"copilotkit",
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractParametersFromSchema,
|
|
3
|
+
convertMCPToolsToActions,
|
|
4
|
+
generateMcpToolInstructions,
|
|
5
|
+
MCPTool,
|
|
6
|
+
} from "../mcp-tools-utils";
|
|
7
|
+
|
|
8
|
+
describe("MCP Tools Utils", () => {
|
|
9
|
+
describe("extractParametersFromSchema", () => {
|
|
10
|
+
it("should extract parameters from schema.parameters.properties", () => {
|
|
11
|
+
const tool: MCPTool = {
|
|
12
|
+
description: "Test tool",
|
|
13
|
+
schema: {
|
|
14
|
+
parameters: {
|
|
15
|
+
properties: {
|
|
16
|
+
name: { type: "string", description: "A name parameter" },
|
|
17
|
+
age: { type: "number", description: "An age parameter" },
|
|
18
|
+
},
|
|
19
|
+
required: ["name"],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
execute: async () => ({}),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const result = extractParametersFromSchema(tool);
|
|
26
|
+
expect(result).toHaveLength(2);
|
|
27
|
+
expect(result[0]).toEqual({
|
|
28
|
+
name: "name",
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "A name parameter",
|
|
31
|
+
required: true,
|
|
32
|
+
});
|
|
33
|
+
expect(result[1]).toEqual({
|
|
34
|
+
name: "age",
|
|
35
|
+
type: "number",
|
|
36
|
+
description: "An age parameter",
|
|
37
|
+
required: false,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should extract parameters from schema.parameters.jsonSchema", () => {
|
|
42
|
+
const tool: MCPTool = {
|
|
43
|
+
description: "Test tool with jsonSchema",
|
|
44
|
+
schema: {
|
|
45
|
+
parameters: {
|
|
46
|
+
jsonSchema: {
|
|
47
|
+
properties: {
|
|
48
|
+
title: { type: "string", description: "A title parameter" },
|
|
49
|
+
count: { type: "number", description: "A count parameter" },
|
|
50
|
+
},
|
|
51
|
+
required: ["title"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
execute: async () => ({}),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const result = extractParametersFromSchema(tool);
|
|
59
|
+
expect(result).toHaveLength(2);
|
|
60
|
+
expect(result[0]).toEqual({
|
|
61
|
+
name: "title",
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "A title parameter",
|
|
64
|
+
required: true,
|
|
65
|
+
});
|
|
66
|
+
expect(result[1]).toEqual({
|
|
67
|
+
name: "count",
|
|
68
|
+
type: "number",
|
|
69
|
+
description: "A count parameter",
|
|
70
|
+
required: false,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should handle arrays with items", () => {
|
|
75
|
+
const tool: MCPTool = {
|
|
76
|
+
description: "Test tool with array parameters",
|
|
77
|
+
schema: {
|
|
78
|
+
parameters: {
|
|
79
|
+
properties: {
|
|
80
|
+
simpleArray: {
|
|
81
|
+
type: "array",
|
|
82
|
+
items: { type: "string" },
|
|
83
|
+
description: "Array of strings",
|
|
84
|
+
},
|
|
85
|
+
objectArray: {
|
|
86
|
+
type: "array",
|
|
87
|
+
items: {
|
|
88
|
+
type: "object",
|
|
89
|
+
properties: {
|
|
90
|
+
name: { type: "string" },
|
|
91
|
+
value: { type: "number" },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
description: "Array of objects",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
required: ["simpleArray"],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
execute: async () => ({}),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const result = extractParametersFromSchema(tool);
|
|
104
|
+
expect(result).toHaveLength(2);
|
|
105
|
+
expect(result[0]).toEqual({
|
|
106
|
+
name: "simpleArray",
|
|
107
|
+
type: "array<string>",
|
|
108
|
+
description: "Array of strings",
|
|
109
|
+
required: true,
|
|
110
|
+
});
|
|
111
|
+
expect(result[1]).toEqual({
|
|
112
|
+
name: "objectArray",
|
|
113
|
+
type: "array",
|
|
114
|
+
description: "Array of objects Array of objects with properties: name, value",
|
|
115
|
+
required: false,
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should handle enums", () => {
|
|
120
|
+
const tool: MCPTool = {
|
|
121
|
+
description: "Test tool with enum parameters",
|
|
122
|
+
schema: {
|
|
123
|
+
parameters: {
|
|
124
|
+
properties: {
|
|
125
|
+
status: {
|
|
126
|
+
type: "string",
|
|
127
|
+
enum: ["active", "inactive", "pending"],
|
|
128
|
+
description: "Status value",
|
|
129
|
+
},
|
|
130
|
+
priority: {
|
|
131
|
+
type: "number",
|
|
132
|
+
enum: [1, 2, 3],
|
|
133
|
+
description: "Priority level",
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
required: ["status"],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
execute: async () => ({}),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const result = extractParametersFromSchema(tool);
|
|
143
|
+
expect(result).toHaveLength(2);
|
|
144
|
+
expect(result[0]).toEqual({
|
|
145
|
+
name: "status",
|
|
146
|
+
type: "string",
|
|
147
|
+
description: "Status value Allowed values: active | inactive | pending",
|
|
148
|
+
required: true,
|
|
149
|
+
});
|
|
150
|
+
expect(result[1]).toEqual({
|
|
151
|
+
name: "priority",
|
|
152
|
+
type: "number",
|
|
153
|
+
description: "Priority level Allowed values: 1 | 2 | 3",
|
|
154
|
+
required: false,
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should handle nested objects", () => {
|
|
159
|
+
const tool: MCPTool = {
|
|
160
|
+
description: "Test tool with nested object parameters",
|
|
161
|
+
schema: {
|
|
162
|
+
parameters: {
|
|
163
|
+
properties: {
|
|
164
|
+
user: {
|
|
165
|
+
type: "object",
|
|
166
|
+
properties: {
|
|
167
|
+
name: { type: "string" },
|
|
168
|
+
email: { type: "string" },
|
|
169
|
+
preferences: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
theme: { type: "string" },
|
|
173
|
+
notifications: { type: "boolean" },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
description: "User object",
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
required: ["user"],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
execute: async () => ({}),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const result = extractParametersFromSchema(tool);
|
|
187
|
+
expect(result).toHaveLength(1);
|
|
188
|
+
expect(result[0]).toEqual({
|
|
189
|
+
name: "user",
|
|
190
|
+
type: "object",
|
|
191
|
+
description: "User object Object with properties: name, email, preferences",
|
|
192
|
+
required: true,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should return empty array when no properties", () => {
|
|
197
|
+
const tool: MCPTool = {
|
|
198
|
+
description: "Test tool without properties",
|
|
199
|
+
schema: {
|
|
200
|
+
parameters: {},
|
|
201
|
+
},
|
|
202
|
+
execute: async () => ({}),
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const result = extractParametersFromSchema(tool);
|
|
206
|
+
expect(result).toHaveLength(0);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("generateMcpToolInstructions", () => {
|
|
211
|
+
it("should generate instructions with correct parameter schema from schema.parameters.properties", () => {
|
|
212
|
+
const toolsMap: Record<string, MCPTool> = {
|
|
213
|
+
testTool: {
|
|
214
|
+
description: "A test tool",
|
|
215
|
+
schema: {
|
|
216
|
+
parameters: {
|
|
217
|
+
properties: {
|
|
218
|
+
name: { type: "string", description: "The name parameter" },
|
|
219
|
+
age: { type: "number", description: "The age parameter" },
|
|
220
|
+
},
|
|
221
|
+
required: ["name"],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
execute: async () => ({}),
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const result = generateMcpToolInstructions(toolsMap);
|
|
229
|
+
expect(result).toContain("testTool: A test tool");
|
|
230
|
+
expect(result).toContain("- name* (string) - The name parameter");
|
|
231
|
+
expect(result).toContain("- age (number) - The age parameter");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should generate instructions with correct parameter schema from schema.parameters.jsonSchema", () => {
|
|
235
|
+
const toolsMap: Record<string, MCPTool> = {
|
|
236
|
+
testTool: {
|
|
237
|
+
description: "A test tool with jsonSchema",
|
|
238
|
+
schema: {
|
|
239
|
+
parameters: {
|
|
240
|
+
jsonSchema: {
|
|
241
|
+
properties: {
|
|
242
|
+
title: { type: "string", description: "The title parameter" },
|
|
243
|
+
count: { type: "number", description: "The count parameter" },
|
|
244
|
+
},
|
|
245
|
+
required: ["title"],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
execute: async () => ({}),
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const result = generateMcpToolInstructions(toolsMap);
|
|
254
|
+
expect(result).toContain("testTool: A test tool with jsonSchema");
|
|
255
|
+
expect(result).toContain("- title* (string) - The title parameter");
|
|
256
|
+
expect(result).toContain("- count (number) - The count parameter");
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("should handle complex schemas with arrays and enums", () => {
|
|
260
|
+
const toolsMap: Record<string, MCPTool> = {
|
|
261
|
+
complexTool: {
|
|
262
|
+
description: "A complex tool",
|
|
263
|
+
schema: {
|
|
264
|
+
parameters: {
|
|
265
|
+
properties: {
|
|
266
|
+
items: {
|
|
267
|
+
type: "array",
|
|
268
|
+
items: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
name: { type: "string" },
|
|
272
|
+
value: { type: "number" },
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
description: "Array of items",
|
|
276
|
+
},
|
|
277
|
+
status: {
|
|
278
|
+
type: "string",
|
|
279
|
+
enum: ["active", "inactive"],
|
|
280
|
+
description: "Status",
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
required: ["items"],
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
execute: async () => ({}),
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const result = generateMcpToolInstructions(toolsMap);
|
|
291
|
+
expect(result).toContain("complexTool: A complex tool");
|
|
292
|
+
expect(result).toContain(
|
|
293
|
+
"- items* (array<object>) - Array of items Array of objects with properties: name, value",
|
|
294
|
+
);
|
|
295
|
+
expect(result).toContain("- status (string) - Status Allowed values: active | inactive");
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should fallback to schema.properties for backward compatibility", () => {
|
|
299
|
+
const toolsMap: Record<string, MCPTool> = {
|
|
300
|
+
backwardCompatTool: {
|
|
301
|
+
description: "A backward compatible tool",
|
|
302
|
+
schema: {
|
|
303
|
+
// Direct properties without nested parameters
|
|
304
|
+
properties: {
|
|
305
|
+
name: { type: "string", description: "The name parameter" },
|
|
306
|
+
},
|
|
307
|
+
required: ["name"],
|
|
308
|
+
} as any,
|
|
309
|
+
execute: async () => ({}),
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const result = generateMcpToolInstructions(toolsMap);
|
|
314
|
+
expect(result).toContain("backwardCompatTool: A backward compatible tool");
|
|
315
|
+
expect(result).toContain("- name* (string) - The name parameter");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("should show 'No parameters required' when no schema properties", () => {
|
|
319
|
+
const toolsMap: Record<string, MCPTool> = {
|
|
320
|
+
noParamsTool: {
|
|
321
|
+
description: "A tool with no parameters",
|
|
322
|
+
schema: {
|
|
323
|
+
parameters: {},
|
|
324
|
+
},
|
|
325
|
+
execute: async () => ({}),
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const result = generateMcpToolInstructions(toolsMap);
|
|
330
|
+
expect(result).toContain("noParamsTool: A tool with no parameters");
|
|
331
|
+
expect(result).toContain("No parameters required");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("should handle tools with no schema", () => {
|
|
335
|
+
const toolsMap: Record<string, MCPTool> = {
|
|
336
|
+
noSchemaTool: {
|
|
337
|
+
description: "A tool with no schema",
|
|
338
|
+
execute: async () => ({}),
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const result = generateMcpToolInstructions(toolsMap);
|
|
343
|
+
expect(result).toContain("noSchemaTool: A tool with no schema");
|
|
344
|
+
expect(result).toContain("No parameters required");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("should return empty string for empty tools map", () => {
|
|
348
|
+
const result = generateMcpToolInstructions({});
|
|
349
|
+
expect(result).toBe("");
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe("convertMCPToolsToActions", () => {
|
|
354
|
+
it("should convert MCP tools to CopilotKit actions", () => {
|
|
355
|
+
const mcpTools: Record<string, MCPTool> = {
|
|
356
|
+
testTool: {
|
|
357
|
+
description: "A test tool",
|
|
358
|
+
schema: {
|
|
359
|
+
parameters: {
|
|
360
|
+
properties: {
|
|
361
|
+
name: { type: "string", description: "The name parameter" },
|
|
362
|
+
age: { type: "number", description: "The age parameter" },
|
|
363
|
+
},
|
|
364
|
+
required: ["name"],
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
execute: async () => "test result",
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const result = convertMCPToolsToActions(mcpTools, "http://test-endpoint");
|
|
372
|
+
expect(result).toHaveLength(1);
|
|
373
|
+
expect(result[0].name).toBe("testTool");
|
|
374
|
+
expect(result[0].description).toBe("A test tool");
|
|
375
|
+
expect(result[0].parameters).toHaveLength(2);
|
|
376
|
+
expect(result[0].parameters[0]).toEqual({
|
|
377
|
+
name: "name",
|
|
378
|
+
type: "string",
|
|
379
|
+
description: "The name parameter",
|
|
380
|
+
required: true,
|
|
381
|
+
});
|
|
382
|
+
expect(result[0].parameters[1]).toEqual({
|
|
383
|
+
name: "age",
|
|
384
|
+
type: "number",
|
|
385
|
+
description: "The age parameter",
|
|
386
|
+
required: false,
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("should handle tool execution correctly", async () => {
|
|
391
|
+
const mockExecute = jest.fn().mockResolvedValue("mock result");
|
|
392
|
+
const mcpTools: Record<string, MCPTool> = {
|
|
393
|
+
testTool: {
|
|
394
|
+
description: "A test tool",
|
|
395
|
+
schema: {
|
|
396
|
+
parameters: {
|
|
397
|
+
properties: {
|
|
398
|
+
name: { type: "string", description: "The name parameter" },
|
|
399
|
+
},
|
|
400
|
+
required: ["name"],
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
execute: mockExecute,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const result = convertMCPToolsToActions(mcpTools, "http://test-endpoint");
|
|
408
|
+
const action = result[0];
|
|
409
|
+
|
|
410
|
+
const executeResult = await action.handler({ name: "test" });
|
|
411
|
+
expect(executeResult).toBe("mock result");
|
|
412
|
+
expect(mockExecute).toHaveBeenCalledWith({ name: "test" });
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it("should stringify non-string results", async () => {
|
|
416
|
+
const mcpTools: Record<string, MCPTool> = {
|
|
417
|
+
testTool: {
|
|
418
|
+
description: "A test tool",
|
|
419
|
+
schema: {
|
|
420
|
+
parameters: {
|
|
421
|
+
properties: {
|
|
422
|
+
name: { type: "string", description: "The name parameter" },
|
|
423
|
+
},
|
|
424
|
+
required: ["name"],
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
execute: async () => ({ result: "complex object" }),
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const result = convertMCPToolsToActions(mcpTools, "http://test-endpoint");
|
|
432
|
+
const action = result[0];
|
|
433
|
+
|
|
434
|
+
const executeResult = await action.handler({ name: "test" });
|
|
435
|
+
expect(executeResult).toBe('{"result":"complex object"}');
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should handle execution errors", async () => {
|
|
439
|
+
const mcpTools: Record<string, MCPTool> = {
|
|
440
|
+
testTool: {
|
|
441
|
+
description: "A test tool",
|
|
442
|
+
schema: {
|
|
443
|
+
parameters: {
|
|
444
|
+
properties: {
|
|
445
|
+
name: { type: "string", description: "The name parameter" },
|
|
446
|
+
},
|
|
447
|
+
required: ["name"],
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
execute: async () => {
|
|
451
|
+
throw new Error("Test error");
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const result = convertMCPToolsToActions(mcpTools, "http://test-endpoint");
|
|
457
|
+
const action = result[0];
|
|
458
|
+
|
|
459
|
+
await expect(action.handler({ name: "test" })).rejects.toThrow(
|
|
460
|
+
"Execution failed for MCP tool 'testTool': Test error",
|
|
461
|
+
);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
});
|
|
@@ -439,9 +439,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
const instructions =
|
|
442
|
-
|
|
443
|
-
mcpToolInstructions +
|
|
444
|
-
"\nUse them when appropriate to fulfill the user's request.";
|
|
442
|
+
mcpToolInstructions + "\nUse them when appropriate to fulfill the user's request.";
|
|
445
443
|
|
|
446
444
|
const systemMessageIndex = messages.findIndex((msg) => msg.textMessage?.role === "system");
|
|
447
445
|
|
|
@@ -14,7 +14,7 @@ export interface MCPTool {
|
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
16
|
/** The function to call to execute the tool on the MCP server. */
|
|
17
|
-
execute(
|
|
17
|
+
execute(params: any): Promise<any>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -51,7 +51,7 @@ export function extractParametersFromSchema(
|
|
|
51
51
|
? (toolOrSchema as MCPTool).schema
|
|
52
52
|
: (toolOrSchema as MCPTool["schema"]);
|
|
53
53
|
|
|
54
|
-
const toolParameters = schema?.parameters || schema?.parameters
|
|
54
|
+
const toolParameters = schema?.parameters?.jsonSchema || schema?.parameters;
|
|
55
55
|
const properties = toolParameters?.properties;
|
|
56
56
|
const requiredParams = new Set(toolParameters?.required || []);
|
|
57
57
|
|
|
@@ -62,15 +62,45 @@ export function extractParametersFromSchema(
|
|
|
62
62
|
for (const paramName in properties) {
|
|
63
63
|
if (Object.prototype.hasOwnProperty.call(properties, paramName)) {
|
|
64
64
|
const paramDef = properties[paramName];
|
|
65
|
+
|
|
66
|
+
// Enhanced type extraction with support for complex types
|
|
67
|
+
let type = paramDef.type || "string";
|
|
68
|
+
let description = paramDef.description || "";
|
|
69
|
+
|
|
70
|
+
// Handle arrays with items
|
|
71
|
+
if (type === "array" && paramDef.items) {
|
|
72
|
+
const itemType = paramDef.items.type || "object";
|
|
73
|
+
if (itemType === "object" && paramDef.items.properties) {
|
|
74
|
+
// For arrays of objects, describe the structure
|
|
75
|
+
const itemProperties = Object.keys(paramDef.items.properties).join(", ");
|
|
76
|
+
description =
|
|
77
|
+
description +
|
|
78
|
+
(description ? " " : "") +
|
|
79
|
+
`Array of objects with properties: ${itemProperties}`;
|
|
80
|
+
} else {
|
|
81
|
+
// For arrays of primitives
|
|
82
|
+
type = `array<${itemType}>`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Handle enums
|
|
87
|
+
if (paramDef.enum && Array.isArray(paramDef.enum)) {
|
|
88
|
+
const enumValues = paramDef.enum.join(" | ");
|
|
89
|
+
description = description + (description ? " " : "") + `Allowed values: ${enumValues}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Handle objects with properties
|
|
93
|
+
if (type === "object" && paramDef.properties) {
|
|
94
|
+
const objectProperties = Object.keys(paramDef.properties).join(", ");
|
|
95
|
+
description =
|
|
96
|
+
description + (description ? " " : "") + `Object with properties: ${objectProperties}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
65
99
|
parameters.push({
|
|
66
100
|
name: paramName,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
type: paramDef.type || "string",
|
|
70
|
-
description: paramDef.description,
|
|
101
|
+
type: type,
|
|
102
|
+
description: description,
|
|
71
103
|
required: requiredParams.has(paramName),
|
|
72
|
-
// Attributes might not directly map, handle if necessary
|
|
73
|
-
// attributes: paramDef.attributes || undefined,
|
|
74
104
|
});
|
|
75
105
|
}
|
|
76
106
|
}
|
|
@@ -95,7 +125,7 @@ export function convertMCPToolsToActions(
|
|
|
95
125
|
|
|
96
126
|
const handler = async (params: any): Promise<any> => {
|
|
97
127
|
try {
|
|
98
|
-
const result = await tool.execute(
|
|
128
|
+
const result = await tool.execute(params);
|
|
99
129
|
// Ensure the result is a string or stringify it, as required by many LLMs.
|
|
100
130
|
// This might need adjustment depending on how different LLMs handle tool results.
|
|
101
131
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
@@ -148,16 +178,49 @@ export function generateMcpToolInstructions(toolsMap: Record<string, MCPTool>):
|
|
|
148
178
|
if (tool.schema && typeof tool.schema === "object") {
|
|
149
179
|
const schema = tool.schema as any;
|
|
150
180
|
|
|
151
|
-
// Extract parameters from JSON Schema
|
|
152
|
-
|
|
153
|
-
|
|
181
|
+
// Extract parameters from JSON Schema - check both schema.parameters.properties and schema.properties
|
|
182
|
+
const toolParameters = schema.parameters?.jsonSchema || schema.parameters;
|
|
183
|
+
const properties = toolParameters?.properties || schema.properties;
|
|
184
|
+
const requiredParams = toolParameters?.required || schema.required || [];
|
|
154
185
|
|
|
155
|
-
|
|
156
|
-
|
|
186
|
+
if (properties) {
|
|
187
|
+
// Build parameter documentation from properties with enhanced type information
|
|
188
|
+
const paramsList = Object.entries(properties).map(([paramName, propSchema]) => {
|
|
157
189
|
const propDetails = propSchema as any;
|
|
158
190
|
const requiredMark = requiredParams.includes(paramName) ? "*" : "";
|
|
159
|
-
|
|
160
|
-
|
|
191
|
+
let typeInfo = propDetails.type || "any";
|
|
192
|
+
let description = propDetails.description ? ` - ${propDetails.description}` : "";
|
|
193
|
+
|
|
194
|
+
// Enhanced type display for complex schemas
|
|
195
|
+
if (typeInfo === "array" && propDetails.items) {
|
|
196
|
+
const itemType = propDetails.items.type || "object";
|
|
197
|
+
if (itemType === "object" && propDetails.items.properties) {
|
|
198
|
+
const itemProps = Object.keys(propDetails.items.properties).join(", ");
|
|
199
|
+
typeInfo = `array<object>`;
|
|
200
|
+
description =
|
|
201
|
+
description +
|
|
202
|
+
(description ? " " : " - ") +
|
|
203
|
+
`Array of objects with properties: ${itemProps}`;
|
|
204
|
+
} else {
|
|
205
|
+
typeInfo = `array<${itemType}>`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Handle enums
|
|
210
|
+
if (propDetails.enum && Array.isArray(propDetails.enum)) {
|
|
211
|
+
const enumValues = propDetails.enum.join(" | ");
|
|
212
|
+
description =
|
|
213
|
+
description + (description ? " " : " - ") + `Allowed values: ${enumValues}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Handle objects
|
|
217
|
+
if (typeInfo === "object" && propDetails.properties) {
|
|
218
|
+
const objectProps = Object.keys(propDetails.properties).join(", ");
|
|
219
|
+
description =
|
|
220
|
+
description +
|
|
221
|
+
(description ? " " : " - ") +
|
|
222
|
+
`Object with properties: ${objectProps}`;
|
|
223
|
+
}
|
|
161
224
|
|
|
162
225
|
return ` - ${paramName}${requiredMark} (${typeInfo})${description}`;
|
|
163
226
|
});
|
|
@@ -183,6 +246,9 @@ ${toolsDoc}
|
|
|
183
246
|
When using these tools:
|
|
184
247
|
1. Only provide valid parameters according to their type requirements
|
|
185
248
|
2. Required parameters are marked with *
|
|
186
|
-
3.
|
|
187
|
-
4.
|
|
249
|
+
3. For array parameters, provide data in the correct array format
|
|
250
|
+
4. For object parameters, include all required nested properties
|
|
251
|
+
5. For enum parameters, use only the allowed values listed
|
|
252
|
+
6. Format API calls correctly with the expected parameter structure
|
|
253
|
+
7. Always check tool responses to determine your next action`;
|
|
188
254
|
}
|