kernl 0.1.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.
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +53 -0
- package/LICENSE +201 -0
- package/dist/agent.d.ts +43 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +130 -0
- package/dist/context.d.ts +70 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +111 -0
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +31 -0
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +1 -0
- package/dist/guardrail.d.ts +178 -0
- package/dist/guardrail.d.ts.map +1 -0
- package/dist/guardrail.js +34 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/kernel.d.ts +7 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +7 -0
- package/dist/kernl.d.ts +18 -0
- package/dist/kernl.d.ts.map +1 -0
- package/dist/kernl.js +16 -0
- package/dist/lib/env.d.ts +43 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +29 -0
- package/dist/lib/error.d.ts +88 -0
- package/dist/lib/error.d.ts.map +1 -0
- package/dist/lib/error.js +117 -0
- package/dist/lib/logger.d.ts +36 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +43 -0
- package/dist/lib/serde/__tests__/codec.test.d.ts +2 -0
- package/dist/lib/serde/__tests__/codec.test.d.ts.map +1 -0
- package/dist/lib/serde/__tests__/codec.test.js +75 -0
- package/dist/lib/serde/codec.d.ts +12 -0
- package/dist/lib/serde/codec.d.ts.map +1 -0
- package/dist/lib/serde/codec.js +54 -0
- package/dist/lib/serde/json.d.ts +8 -0
- package/dist/lib/serde/json.d.ts.map +1 -0
- package/dist/lib/serde/json.js +13 -0
- package/dist/lib/serde/thread.d.ts +1 -0
- package/dist/lib/serde/thread.d.ts.map +1 -0
- package/dist/lib/serde/thread.js +172 -0
- package/dist/lib/serde/tool.d.ts +36 -0
- package/dist/lib/serde/tool.d.ts.map +1 -0
- package/dist/lib/serde/tool.js +1 -0
- package/dist/lib/utils.d.ts +19 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +41 -0
- package/dist/lifecycle.d.ts +133 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +29 -0
- package/dist/logger.d.ts +36 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +43 -0
- package/dist/mcp/__tests__/base.test.d.ts +2 -0
- package/dist/mcp/__tests__/base.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/base.test.js +268 -0
- package/dist/mcp/__tests__/fixtures/echo-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/echo-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/echo-server.js +92 -0
- package/dist/mcp/__tests__/fixtures/math-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/math-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/math-server.js +98 -0
- package/dist/mcp/__tests__/fixtures/server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/server.js +162 -0
- package/dist/mcp/__tests__/fixtures/test-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/test-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/test-server.js +163 -0
- package/dist/mcp/__tests__/fixtures/utils.d.ts +17 -0
- package/dist/mcp/__tests__/fixtures/utils.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/utils.js +42 -0
- package/dist/mcp/__tests__/integration.test.d.ts +2 -0
- package/dist/mcp/__tests__/integration.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/integration.test.js +360 -0
- package/dist/mcp/__tests__/stdio.test.d.ts +2 -0
- package/dist/mcp/__tests__/stdio.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/stdio.test.js +180 -0
- package/dist/mcp/__tests__/test-utils.d.ts +17 -0
- package/dist/mcp/__tests__/test-utils.d.ts.map +1 -0
- package/dist/mcp/__tests__/test-utils.js +42 -0
- package/dist/mcp/__tests__/utils.test.d.ts +2 -0
- package/dist/mcp/__tests__/utils.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/utils.test.js +300 -0
- package/dist/mcp/base.d.ts +88 -0
- package/dist/mcp/base.d.ts.map +1 -0
- package/dist/mcp/base.js +68 -0
- package/dist/mcp/http.d.ts +34 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +100 -0
- package/dist/mcp/node.d.ts +60 -0
- package/dist/mcp/node.d.ts.map +1 -0
- package/dist/mcp/node.js +297 -0
- package/dist/mcp/sse.d.ts +34 -0
- package/dist/mcp/sse.d.ts.map +1 -0
- package/dist/mcp/sse.js +97 -0
- package/dist/mcp/stdio.d.ts +32 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +96 -0
- package/dist/mcp/types.d.ts +172 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +16 -0
- package/dist/mcp/utils.d.ts +23 -0
- package/dist/mcp/utils.d.ts.map +1 -0
- package/dist/mcp/utils.js +44 -0
- package/dist/model.d.ts +175 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +1 -0
- package/dist/providers/ai.d.ts +1 -0
- package/dist/providers/ai.d.ts.map +1 -0
- package/dist/providers/ai.js +1 -0
- package/dist/providers/default.d.ts +16 -0
- package/dist/providers/default.d.ts.map +1 -0
- package/dist/providers/default.js +17 -0
- package/dist/providers/registry.d.ts +1 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +1 -0
- package/dist/sched/scheduler.d.ts +20 -0
- package/dist/sched/scheduler.d.ts.map +1 -0
- package/dist/sched/scheduler.js +1 -0
- package/dist/sched/task.d.ts +92 -0
- package/dist/sched/task.d.ts.map +1 -0
- package/dist/sched/task.js +102 -0
- package/dist/serde/__tests__/codec.test.d.ts +2 -0
- package/dist/serde/__tests__/codec.test.d.ts.map +1 -0
- package/dist/serde/__tests__/codec.test.js +75 -0
- package/dist/serde/codec.d.ts +12 -0
- package/dist/serde/codec.d.ts.map +1 -0
- package/dist/serde/codec.js +54 -0
- package/dist/serde/json.d.ts +8 -0
- package/dist/serde/json.d.ts.map +1 -0
- package/dist/serde/json.js +13 -0
- package/dist/serde/thread.d.ts +687 -0
- package/dist/serde/thread.d.ts.map +1 -0
- package/dist/serde/thread.js +158 -0
- package/dist/serde/tool.d.ts +36 -0
- package/dist/serde/tool.d.ts.map +1 -0
- package/dist/serde/tool.js +1 -0
- package/dist/session.d.ts +1 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +1 -0
- package/dist/task.d.ts +87 -0
- package/dist/task.d.ts.map +1 -0
- package/dist/task.js +97 -0
- package/dist/thread/__tests__/mock.d.ts +28 -0
- package/dist/thread/__tests__/mock.d.ts.map +1 -0
- package/dist/thread/__tests__/mock.js +74 -0
- package/dist/thread/__tests__/thread.test.d.ts +2 -0
- package/dist/thread/__tests__/thread.test.d.ts.map +1 -0
- package/dist/thread/__tests__/thread.test.js +1412 -0
- package/dist/thread/index.d.ts +2 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/thread/index.js +1 -0
- package/dist/thread/thread.d.ts +66 -0
- package/dist/thread/thread.d.ts.map +1 -0
- package/dist/thread/thread.js +472 -0
- package/dist/thread/utils.d.ts +19 -0
- package/dist/thread/utils.d.ts.map +1 -0
- package/dist/thread/utils.js +50 -0
- package/dist/tool/__tests__/fixtures.d.ts +45 -0
- package/dist/tool/__tests__/fixtures.d.ts.map +1 -0
- package/dist/tool/__tests__/fixtures.js +97 -0
- package/dist/tool/__tests__/tool.test.d.ts +2 -0
- package/dist/tool/__tests__/tool.test.d.ts.map +1 -0
- package/dist/tool/__tests__/tool.test.js +172 -0
- package/dist/tool/__tests__/toolkit.test.d.ts +2 -0
- package/dist/tool/__tests__/toolkit.test.d.ts.map +1 -0
- package/dist/tool/__tests__/toolkit.test.js +134 -0
- package/dist/tool/index.d.ts +4 -0
- package/dist/tool/index.d.ts.map +1 -0
- package/dist/tool/index.js +2 -0
- package/dist/tool/mcp.d.ts +75 -0
- package/dist/tool/mcp.d.ts.map +1 -0
- package/dist/tool/mcp.js +111 -0
- package/dist/tool/tool.d.ts +95 -0
- package/dist/tool/tool.d.ts.map +1 -0
- package/dist/tool/tool.js +176 -0
- package/dist/tool/toolkit.d.ts +121 -0
- package/dist/tool/toolkit.d.ts.map +1 -0
- package/dist/tool/toolkit.js +180 -0
- package/dist/tool/types.d.ts +187 -0
- package/dist/tool/types.d.ts.map +1 -0
- package/dist/tool/types.js +1 -0
- package/dist/tools.d.ts +362 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +220 -0
- package/dist/trace/processor.d.ts +1 -0
- package/dist/trace/processor.d.ts.map +1 -0
- package/dist/trace/processor.js +1 -0
- package/dist/trace/traces.d.ts +1 -0
- package/dist/trace/traces.d.ts.map +1 -0
- package/dist/trace/traces.js +73 -0
- package/dist/trace/utils.d.ts +22 -0
- package/dist/trace/utils.d.ts.map +1 -0
- package/dist/trace/utils.js +30 -0
- package/dist/types/agent.d.ts +91 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +1 -0
- package/dist/types/proto.d.ts +1551 -0
- package/dist/types/proto.d.ts.map +1 -0
- package/dist/types/proto.js +531 -0
- package/dist/types/thread.d.ts +71 -0
- package/dist/types/thread.d.ts.map +1 -0
- package/dist/types/thread.js +5 -0
- package/dist/usage.d.ts +43 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +61 -0
- package/package.json +52 -0
- package/src/agent.ts +203 -0
- package/src/context.ts +265 -0
- package/src/guardrail.ts +277 -0
- package/src/index.ts +3 -0
- package/src/kernl.ts +22 -0
- package/src/lib/env.ts +36 -0
- package/src/lib/error.ts +158 -0
- package/src/lib/logger.ts +78 -0
- package/src/lib/serde/json.ts +18 -0
- package/src/lib/serde/thread.ts +188 -0
- package/src/lifecycle.ts +181 -0
- package/src/mcp/__tests__/base.test.ts +344 -0
- package/src/mcp/__tests__/fixtures/server.ts +179 -0
- package/src/mcp/__tests__/fixtures/utils.ts +58 -0
- package/src/mcp/__tests__/integration.test.ts +447 -0
- package/src/mcp/__tests__/stdio.test.ts +236 -0
- package/src/mcp/__tests__/utils.test.ts +360 -0
- package/src/mcp/base.ts +162 -0
- package/src/mcp/http.ts +147 -0
- package/src/mcp/sse.ts +137 -0
- package/src/mcp/stdio.ts +136 -0
- package/src/mcp/types.ts +202 -0
- package/src/mcp/utils.ts +62 -0
- package/src/task.ts +119 -0
- package/src/thread/__tests__/mock.ts +95 -0
- package/src/thread/__tests__/thread.test.ts +1574 -0
- package/src/thread/index.ts +1 -0
- package/src/thread/thread.ts +611 -0
- package/src/thread/utils.ts +67 -0
- package/src/tool/__tests__/fixtures.ts +106 -0
- package/src/tool/__tests__/tool.test.ts +235 -0
- package/src/tool/__tests__/toolkit.test.ts +174 -0
- package/src/tool/index.ts +10 -0
- package/src/tool/tool.ts +264 -0
- package/src/tool/toolkit.ts +234 -0
- package/src/tool/types.ts +243 -0
- package/src/trace/processor.ts +0 -0
- package/src/trace/traces.ts +86 -0
- package/src/trace/utils.ts +38 -0
- package/src/types/agent.ts +145 -0
- package/src/types/thread.ts +86 -0
- package/tsconfig.json +13 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Simple test server combining math and string operations for MCP testing.
|
|
4
|
+
*
|
|
5
|
+
* Provides deterministic operations for comprehensive test coverage.
|
|
6
|
+
*/
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import {
|
|
10
|
+
CallToolRequestSchema,
|
|
11
|
+
ListToolsRequestSchema,
|
|
12
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
|
|
14
|
+
const server = new Server(
|
|
15
|
+
{
|
|
16
|
+
name: "test-server",
|
|
17
|
+
version: "1.0.0",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
capabilities: {
|
|
21
|
+
tools: {},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
27
|
+
tools: [
|
|
28
|
+
// Math tools
|
|
29
|
+
{
|
|
30
|
+
name: "add",
|
|
31
|
+
description: "Add two numbers",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
a: { type: "number", description: "First number" },
|
|
36
|
+
b: { type: "number", description: "Second number" },
|
|
37
|
+
},
|
|
38
|
+
required: ["a", "b"],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "multiply",
|
|
43
|
+
description: "Multiply two numbers",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: {
|
|
47
|
+
a: { type: "number", description: "First number" },
|
|
48
|
+
b: { type: "number", description: "Second number" },
|
|
49
|
+
},
|
|
50
|
+
required: ["a", "b"],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "divide",
|
|
55
|
+
description: "Divide two numbers",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
a: { type: "number", description: "Numerator" },
|
|
60
|
+
b: { type: "number", description: "Denominator" },
|
|
61
|
+
},
|
|
62
|
+
required: ["a", "b"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// String tools
|
|
66
|
+
{
|
|
67
|
+
name: "echo",
|
|
68
|
+
description: "Echo back the input text",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
text: { type: "string", description: "Text to echo" },
|
|
73
|
+
},
|
|
74
|
+
required: ["text"],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "uppercase",
|
|
79
|
+
description: "Convert text to uppercase",
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: {
|
|
83
|
+
text: { type: "string", description: "Text to convert" },
|
|
84
|
+
},
|
|
85
|
+
required: ["text"],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "reverse",
|
|
90
|
+
description: "Reverse the text",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
text: { type: "string", description: "Text to reverse" },
|
|
95
|
+
},
|
|
96
|
+
required: ["text"],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
// Handle tool calls
|
|
103
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
104
|
+
const { name, arguments: args } = request.params;
|
|
105
|
+
|
|
106
|
+
switch (name) {
|
|
107
|
+
// Math operations
|
|
108
|
+
case "add":
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: String((args as any).a + (args as any).b),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
case "multiply":
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: String((args as any).a * (args as any).b),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
case "divide":
|
|
129
|
+
if ((args as any).b === 0) {
|
|
130
|
+
throw new Error("Division by zero");
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
content: [
|
|
134
|
+
{
|
|
135
|
+
type: "text",
|
|
136
|
+
text: String((args as any).a / (args as any).b),
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// String operations
|
|
142
|
+
case "echo":
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: (args as any).text,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
case "uppercase":
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: (args as any).text.toUpperCase(),
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
case "reverse":
|
|
163
|
+
return {
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
type: "text",
|
|
167
|
+
text: (args as any).text.split("").reverse().join(""),
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
default:
|
|
173
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Start the server
|
|
178
|
+
const transport = new StdioServerTransport();
|
|
179
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { MCPServerStdio } from "../../stdio";
|
|
2
|
+
import type { MCPServer } from "../../base";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Helper to run a test with an MCP server, ensuring cleanup.
|
|
6
|
+
*/
|
|
7
|
+
export async function withMCPServer<T>(
|
|
8
|
+
serverPath: string,
|
|
9
|
+
fn: (server: MCPServerStdio) => Promise<T>
|
|
10
|
+
): Promise<T> {
|
|
11
|
+
const server = new MCPServerStdio({
|
|
12
|
+
id: "test-server",
|
|
13
|
+
command: "npx",
|
|
14
|
+
args: ["tsx", serverPath],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
await server.connect();
|
|
19
|
+
return await fn(server);
|
|
20
|
+
} finally {
|
|
21
|
+
await server.close();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Helper to create a server without auto-connecting.
|
|
27
|
+
*/
|
|
28
|
+
export function createMCPServer(
|
|
29
|
+
serverPath: string,
|
|
30
|
+
options: { id?: string; cacheToolsList?: boolean } = {}
|
|
31
|
+
): MCPServerStdio {
|
|
32
|
+
return new MCPServerStdio({
|
|
33
|
+
id: options.id ?? "test-server",
|
|
34
|
+
command: "npx",
|
|
35
|
+
args: ["tsx", serverPath],
|
|
36
|
+
cacheToolsList: options.cacheToolsList,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Wait for a condition to be true or timeout.
|
|
42
|
+
*/
|
|
43
|
+
export async function waitFor(
|
|
44
|
+
condition: () => boolean | Promise<boolean>,
|
|
45
|
+
timeout = 5000,
|
|
46
|
+
interval = 100
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
|
|
50
|
+
while (Date.now() - startTime < timeout) {
|
|
51
|
+
if (await condition()) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
throw new Error(`Timeout waiting for condition after ${timeout}ms`);
|
|
58
|
+
}
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { MCPServerStdio } from "../stdio";
|
|
4
|
+
import { MCPToolkit } from "@/tool/toolkit";
|
|
5
|
+
import { FunctionToolkit } from "@/tool/toolkit";
|
|
6
|
+
import { Agent } from "@/agent";
|
|
7
|
+
import { Context } from "@/context";
|
|
8
|
+
import { tool } from "@/tool";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { Thread } from "@/thread";
|
|
11
|
+
import { createMCPToolStaticFilter } from "../utils";
|
|
12
|
+
|
|
13
|
+
const TEST_SERVER = path.join(__dirname, "fixtures", "server.ts");
|
|
14
|
+
|
|
15
|
+
describe("MCP Integration Tests", () => {
|
|
16
|
+
describe("MCPToolkit Integration", () => {
|
|
17
|
+
let server: MCPServerStdio;
|
|
18
|
+
let toolkit: MCPToolkit;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
server = new MCPServerStdio({
|
|
22
|
+
id: "test-server",
|
|
23
|
+
command: "npx",
|
|
24
|
+
args: ["tsx", TEST_SERVER],
|
|
25
|
+
});
|
|
26
|
+
toolkit = new MCPToolkit({
|
|
27
|
+
id: "test-toolkit",
|
|
28
|
+
server,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
await toolkit.destroy();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should wrap server correctly", () => {
|
|
37
|
+
expect(toolkit.id).toBe("test-toolkit");
|
|
38
|
+
expect(toolkit).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should connect lazily on first list()", async () => {
|
|
42
|
+
// Server should not be connected yet
|
|
43
|
+
// (we can't directly test this without exposing internals)
|
|
44
|
+
|
|
45
|
+
const tools = await toolkit.list();
|
|
46
|
+
|
|
47
|
+
// Should have successfully fetched tools
|
|
48
|
+
expect(tools.length).toBeGreaterThan(0);
|
|
49
|
+
expect(tools.length).toBe(6); // Our test server has 6 tools
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should convert MCPTools to Tools", async () => {
|
|
53
|
+
const tools = await toolkit.list();
|
|
54
|
+
|
|
55
|
+
// Verify they are FunctionTools with proper structure
|
|
56
|
+
for (const tool of tools) {
|
|
57
|
+
expect(tool.id).toBeDefined();
|
|
58
|
+
expect(tool.name).toBeDefined();
|
|
59
|
+
expect(tool.type).toBe("function");
|
|
60
|
+
|
|
61
|
+
// MCP tools are converted to FunctionTools
|
|
62
|
+
if (tool.type === "function") {
|
|
63
|
+
expect(tool.description).toBeDefined();
|
|
64
|
+
expect(tool.invoke).toBeDefined();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check specific tool
|
|
69
|
+
const addTool = tools.find((t) => t.id === "add");
|
|
70
|
+
expect(addTool).toBeDefined();
|
|
71
|
+
if (addTool && addTool.type === "function") {
|
|
72
|
+
expect(addTool.description).toBe("Add two numbers");
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should cache converted tools", async () => {
|
|
77
|
+
const tools1 = await toolkit.list();
|
|
78
|
+
const tools2 = await toolkit.list();
|
|
79
|
+
|
|
80
|
+
// Should return the same tool objects (cached)
|
|
81
|
+
expect(tools1).toEqual(tools2);
|
|
82
|
+
expect(tools1[0]).toBe(tools2[0]); // Same reference
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should return tools from cache via get()", async () => {
|
|
86
|
+
// First, list to populate cache
|
|
87
|
+
await toolkit.list();
|
|
88
|
+
|
|
89
|
+
// Then get specific tool
|
|
90
|
+
const addTool = toolkit.get("add");
|
|
91
|
+
expect(addTool).toBeDefined();
|
|
92
|
+
expect(addTool!.id).toBe("add");
|
|
93
|
+
|
|
94
|
+
if (addTool && addTool.type === "function") {
|
|
95
|
+
expect(addTool.description).toBe("Add two numbers");
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should close server connection on destroy()", async () => {
|
|
100
|
+
// First connect
|
|
101
|
+
await toolkit.list();
|
|
102
|
+
|
|
103
|
+
// Destroy
|
|
104
|
+
await toolkit.destroy();
|
|
105
|
+
|
|
106
|
+
// Trying to list again should reconnect
|
|
107
|
+
const tools = await toolkit.list();
|
|
108
|
+
expect(tools.length).toBe(6);
|
|
109
|
+
|
|
110
|
+
await toolkit.destroy();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("Two-Layer Filtering", () => {
|
|
115
|
+
it("should apply server-level filter to block tools", async () => {
|
|
116
|
+
const server = new MCPServerStdio({
|
|
117
|
+
id: "filtered-server",
|
|
118
|
+
command: "npx",
|
|
119
|
+
args: ["tsx", TEST_SERVER],
|
|
120
|
+
toolFilter: createMCPToolStaticFilter({
|
|
121
|
+
blocked: ["divide", "multiply"],
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const toolkit = new MCPToolkit({
|
|
126
|
+
id: "filtered-toolkit",
|
|
127
|
+
server,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const tools = await toolkit.list();
|
|
131
|
+
|
|
132
|
+
// Should have 4 tools (6 - 2 blocked)
|
|
133
|
+
expect(tools.length).toBe(4);
|
|
134
|
+
expect(tools.find((t) => t.id === "divide")).toBeUndefined();
|
|
135
|
+
expect(tools.find((t) => t.id === "multiply")).toBeUndefined();
|
|
136
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
137
|
+
|
|
138
|
+
await toolkit.destroy();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should apply toolkit-level filter to block tools", async () => {
|
|
142
|
+
const server = new MCPServerStdio({
|
|
143
|
+
id: "test-server",
|
|
144
|
+
command: "npx",
|
|
145
|
+
args: ["tsx", TEST_SERVER],
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const toolkit = new MCPToolkit({
|
|
149
|
+
id: "filtered-toolkit",
|
|
150
|
+
server,
|
|
151
|
+
filter: async (ctx, tool) => {
|
|
152
|
+
// Block all string tools
|
|
153
|
+
return !["echo", "uppercase", "reverse"].includes(tool.id);
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const agent = new Agent({
|
|
158
|
+
id: "test-agent",
|
|
159
|
+
name: "Test Agent",
|
|
160
|
+
instructions: "Test",
|
|
161
|
+
toolkits: [toolkit],
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const context = new Context({});
|
|
165
|
+
const tools = await toolkit.list(context);
|
|
166
|
+
|
|
167
|
+
// Should have 3 math tools only
|
|
168
|
+
expect(tools.length).toBe(3);
|
|
169
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
170
|
+
expect(tools.find((t) => t.id === "echo")).toBeUndefined();
|
|
171
|
+
|
|
172
|
+
await toolkit.destroy();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should combine server and toolkit filters with AND logic", async () => {
|
|
176
|
+
const server = new MCPServerStdio({
|
|
177
|
+
id: "filtered-server",
|
|
178
|
+
command: "npx",
|
|
179
|
+
args: ["tsx", TEST_SERVER],
|
|
180
|
+
toolFilter: createMCPToolStaticFilter({
|
|
181
|
+
// Only allow math tools at server level
|
|
182
|
+
allowed: ["add", "multiply", "divide"],
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const toolkit = new MCPToolkit({
|
|
187
|
+
id: "filtered-toolkit",
|
|
188
|
+
server,
|
|
189
|
+
// At toolkit level, block multiply
|
|
190
|
+
filter: async (ctx, tool) => tool.id !== "multiply",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const agent = new Agent({
|
|
194
|
+
id: "test-agent",
|
|
195
|
+
name: "Test Agent",
|
|
196
|
+
instructions: "Test",
|
|
197
|
+
toolkits: [toolkit],
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const context = new Context({});
|
|
201
|
+
const tools = await toolkit.list(context);
|
|
202
|
+
|
|
203
|
+
// Should have only add and divide (multiply blocked by toolkit filter)
|
|
204
|
+
expect(tools.length).toBe(2);
|
|
205
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
206
|
+
expect(tools.find((t) => t.id === "divide")).toBeDefined();
|
|
207
|
+
expect(tools.find((t) => t.id === "multiply")).toBeUndefined();
|
|
208
|
+
expect(tools.find((t) => t.id === "echo")).toBeUndefined();
|
|
209
|
+
|
|
210
|
+
await toolkit.destroy();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should receive correct context in toolkit filter", async () => {
|
|
214
|
+
const server = new MCPServerStdio({
|
|
215
|
+
id: "test-server",
|
|
216
|
+
command: "npx",
|
|
217
|
+
args: ["tsx", TEST_SERVER],
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
let receivedContext: any = null;
|
|
221
|
+
|
|
222
|
+
const toolkit = new MCPToolkit({
|
|
223
|
+
id: "test-toolkit",
|
|
224
|
+
server,
|
|
225
|
+
filter: async (ctx, tool) => {
|
|
226
|
+
receivedContext = ctx;
|
|
227
|
+
return true;
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const agent = new Agent({
|
|
232
|
+
id: "test-agent",
|
|
233
|
+
name: "Test Agent",
|
|
234
|
+
instructions: "Test",
|
|
235
|
+
toolkits: [toolkit],
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const context = new Context({ userId: "test-user" });
|
|
239
|
+
await toolkit.list(context);
|
|
240
|
+
|
|
241
|
+
// Verify context was passed correctly
|
|
242
|
+
expect(receivedContext).toBeDefined();
|
|
243
|
+
expect(receivedContext.context).toBe(context);
|
|
244
|
+
expect(receivedContext.agent).toBe(agent);
|
|
245
|
+
expect(receivedContext.toolkitId).toBe("test-toolkit");
|
|
246
|
+
|
|
247
|
+
await toolkit.destroy();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("Agent Integration", () => {
|
|
252
|
+
it("should include MCP tools in agent.tools()", async () => {
|
|
253
|
+
const server = new MCPServerStdio({
|
|
254
|
+
id: "test-server",
|
|
255
|
+
command: "npx",
|
|
256
|
+
args: ["tsx", TEST_SERVER],
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const mcpToolkit = new MCPToolkit({
|
|
260
|
+
id: "mcp-toolkit",
|
|
261
|
+
server,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const agent = new Agent({
|
|
265
|
+
id: "test-agent",
|
|
266
|
+
name: "Test Agent",
|
|
267
|
+
instructions: "Test",
|
|
268
|
+
toolkits: [mcpToolkit],
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const context = new Context({});
|
|
272
|
+
const tools = await agent.tools(context);
|
|
273
|
+
|
|
274
|
+
expect(tools.length).toBe(6);
|
|
275
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
276
|
+
expect(tools.find((t) => t.id === "echo")).toBeDefined();
|
|
277
|
+
|
|
278
|
+
await mcpToolkit.destroy();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("should find MCP tools via agent.tool()", async () => {
|
|
282
|
+
const server = new MCPServerStdio({
|
|
283
|
+
id: "test-server",
|
|
284
|
+
command: "npx",
|
|
285
|
+
args: ["tsx", TEST_SERVER],
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const mcpToolkit = new MCPToolkit({
|
|
289
|
+
id: "mcp-toolkit",
|
|
290
|
+
server,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const agent = new Agent({
|
|
294
|
+
id: "test-agent",
|
|
295
|
+
name: "Test Agent",
|
|
296
|
+
instructions: "Test",
|
|
297
|
+
toolkits: [mcpToolkit],
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Populate toolkit cache
|
|
301
|
+
const context = new Context({});
|
|
302
|
+
await agent.tools(context);
|
|
303
|
+
|
|
304
|
+
// Now get specific tool
|
|
305
|
+
const addTool = agent.tool("add");
|
|
306
|
+
expect(addTool).toBeDefined();
|
|
307
|
+
expect(addTool!.id).toBe("add");
|
|
308
|
+
|
|
309
|
+
await mcpToolkit.destroy();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("should execute MCP tools through tool.invoke()", async () => {
|
|
313
|
+
const server = new MCPServerStdio({
|
|
314
|
+
id: "test-server",
|
|
315
|
+
command: "npx",
|
|
316
|
+
args: ["tsx", TEST_SERVER],
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const mcpToolkit = new MCPToolkit({
|
|
320
|
+
id: "mcp-toolkit",
|
|
321
|
+
server,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const agent = new Agent({
|
|
325
|
+
id: "test-agent",
|
|
326
|
+
name: "Test Agent",
|
|
327
|
+
instructions: "Test",
|
|
328
|
+
toolkits: [mcpToolkit],
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const context = new Context({});
|
|
332
|
+
await agent.tools(context);
|
|
333
|
+
|
|
334
|
+
const addTool = agent.tool("add");
|
|
335
|
+
expect(addTool).toBeDefined();
|
|
336
|
+
|
|
337
|
+
// Execute the tool
|
|
338
|
+
if (addTool && addTool.type === "function") {
|
|
339
|
+
const result = await addTool.invoke(
|
|
340
|
+
context,
|
|
341
|
+
JSON.stringify({ a: 5, b: 3 }),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
expect(result.state).toBe("completed");
|
|
345
|
+
expect(result.result).toEqual({ type: "text", text: "8" });
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
await mcpToolkit.destroy();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("should work with multiple toolkits together", async () => {
|
|
352
|
+
const server = new MCPServerStdio({
|
|
353
|
+
id: "test-server",
|
|
354
|
+
command: "npx",
|
|
355
|
+
args: ["tsx", TEST_SERVER],
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
const mcpToolkit = new MCPToolkit({
|
|
359
|
+
id: "mcp-toolkit",
|
|
360
|
+
server,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Create a function toolkit
|
|
364
|
+
const localTool = tool({
|
|
365
|
+
id: "local_tool",
|
|
366
|
+
name: "local_tool",
|
|
367
|
+
description: "A local tool",
|
|
368
|
+
parameters: z.object({
|
|
369
|
+
input: z.string(),
|
|
370
|
+
}),
|
|
371
|
+
execute: async (context, params) => {
|
|
372
|
+
return `Local: ${params.input}`;
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const functionToolkit = new FunctionToolkit({
|
|
377
|
+
id: "function-toolkit",
|
|
378
|
+
tools: [localTool],
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const agent = new Agent({
|
|
382
|
+
id: "test-agent",
|
|
383
|
+
name: "Test Agent",
|
|
384
|
+
instructions: "Test",
|
|
385
|
+
toolkits: [mcpToolkit, functionToolkit],
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const context = new Context({});
|
|
389
|
+
const tools = await agent.tools(context);
|
|
390
|
+
|
|
391
|
+
// Should have 7 tools (6 MCP + 1 local)
|
|
392
|
+
expect(tools.length).toBe(7);
|
|
393
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
394
|
+
expect(tools.find((t) => t.id === "local_tool")).toBeDefined();
|
|
395
|
+
|
|
396
|
+
await mcpToolkit.destroy();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("should detect duplicate tool IDs across toolkits", async () => {
|
|
400
|
+
const server = new MCPServerStdio({
|
|
401
|
+
id: "test-server",
|
|
402
|
+
command: "npx",
|
|
403
|
+
args: ["tsx", TEST_SERVER],
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const mcpToolkit = new MCPToolkit({
|
|
407
|
+
id: "mcp-toolkit",
|
|
408
|
+
server,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Create a function toolkit with a duplicate ID
|
|
412
|
+
const duplicateTool = tool({
|
|
413
|
+
id: "add", // Same as MCP tool
|
|
414
|
+
name: "add",
|
|
415
|
+
description: "Duplicate add tool",
|
|
416
|
+
parameters: z.object({
|
|
417
|
+
a: z.number(),
|
|
418
|
+
b: z.number(),
|
|
419
|
+
}),
|
|
420
|
+
execute: async (context, params) => {
|
|
421
|
+
return params.a + params.b;
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const functionToolkit = new FunctionToolkit({
|
|
426
|
+
id: "function-toolkit",
|
|
427
|
+
tools: [duplicateTool],
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const agent = new Agent({
|
|
431
|
+
id: "test-agent",
|
|
432
|
+
name: "Test Agent",
|
|
433
|
+
instructions: "Test",
|
|
434
|
+
toolkits: [mcpToolkit, functionToolkit],
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const context = new Context({});
|
|
438
|
+
|
|
439
|
+
// Should throw error about duplicate tool IDs
|
|
440
|
+
await expect(agent.tools(context)).rejects.toThrow(
|
|
441
|
+
/Duplicate tool IDs found/,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
await mcpToolkit.destroy();
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
});
|