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,360 @@
|
|
|
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 { createMCPToolStaticFilter } from "../utils";
|
|
11
|
+
const TEST_SERVER = path.join(__dirname, "fixtures", "server.ts");
|
|
12
|
+
describe("MCP Integration Tests", () => {
|
|
13
|
+
describe("MCPToolkit Integration", () => {
|
|
14
|
+
let server;
|
|
15
|
+
let toolkit;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
server = new MCPServerStdio({
|
|
18
|
+
id: "test-server",
|
|
19
|
+
command: "npx",
|
|
20
|
+
args: ["tsx", TEST_SERVER],
|
|
21
|
+
});
|
|
22
|
+
toolkit = new MCPToolkit({
|
|
23
|
+
id: "test-toolkit",
|
|
24
|
+
server,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
afterEach(async () => {
|
|
28
|
+
await toolkit.destroy();
|
|
29
|
+
});
|
|
30
|
+
it("should wrap server correctly", () => {
|
|
31
|
+
expect(toolkit.id).toBe("test-toolkit");
|
|
32
|
+
expect(toolkit).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
it("should connect lazily on first list()", async () => {
|
|
35
|
+
// Server should not be connected yet
|
|
36
|
+
// (we can't directly test this without exposing internals)
|
|
37
|
+
const tools = await toolkit.list();
|
|
38
|
+
// Should have successfully fetched tools
|
|
39
|
+
expect(tools.length).toBeGreaterThan(0);
|
|
40
|
+
expect(tools.length).toBe(6); // Our test server has 6 tools
|
|
41
|
+
});
|
|
42
|
+
it("should convert MCPTools to Tools", async () => {
|
|
43
|
+
const tools = await toolkit.list();
|
|
44
|
+
// Verify they are FunctionTools with proper structure
|
|
45
|
+
for (const tool of tools) {
|
|
46
|
+
expect(tool.id).toBeDefined();
|
|
47
|
+
expect(tool.name).toBeDefined();
|
|
48
|
+
expect(tool.type).toBe("function");
|
|
49
|
+
// MCP tools are converted to FunctionTools
|
|
50
|
+
if (tool.type === "function") {
|
|
51
|
+
expect(tool.description).toBeDefined();
|
|
52
|
+
expect(tool.invoke).toBeDefined();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Check specific tool
|
|
56
|
+
const addTool = tools.find((t) => t.id === "add");
|
|
57
|
+
expect(addTool).toBeDefined();
|
|
58
|
+
if (addTool && addTool.type === "function") {
|
|
59
|
+
expect(addTool.description).toBe("Add two numbers");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
it("should cache converted tools", async () => {
|
|
63
|
+
const tools1 = await toolkit.list();
|
|
64
|
+
const tools2 = await toolkit.list();
|
|
65
|
+
// Should return the same tool objects (cached)
|
|
66
|
+
expect(tools1).toEqual(tools2);
|
|
67
|
+
expect(tools1[0]).toBe(tools2[0]); // Same reference
|
|
68
|
+
});
|
|
69
|
+
it("should return tools from cache via get()", async () => {
|
|
70
|
+
// First, list to populate cache
|
|
71
|
+
await toolkit.list();
|
|
72
|
+
// Then get specific tool
|
|
73
|
+
const addTool = toolkit.get("add");
|
|
74
|
+
expect(addTool).toBeDefined();
|
|
75
|
+
expect(addTool.id).toBe("add");
|
|
76
|
+
if (addTool && addTool.type === "function") {
|
|
77
|
+
expect(addTool.description).toBe("Add two numbers");
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it("should close server connection on destroy()", async () => {
|
|
81
|
+
// First connect
|
|
82
|
+
await toolkit.list();
|
|
83
|
+
// Destroy
|
|
84
|
+
await toolkit.destroy();
|
|
85
|
+
// Trying to list again should reconnect
|
|
86
|
+
const tools = await toolkit.list();
|
|
87
|
+
expect(tools.length).toBe(6);
|
|
88
|
+
await toolkit.destroy();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("Two-Layer Filtering", () => {
|
|
92
|
+
it("should apply server-level filter to block tools", async () => {
|
|
93
|
+
const server = new MCPServerStdio({
|
|
94
|
+
id: "filtered-server",
|
|
95
|
+
command: "npx",
|
|
96
|
+
args: ["tsx", TEST_SERVER],
|
|
97
|
+
toolFilter: createMCPToolStaticFilter({
|
|
98
|
+
blocked: ["divide", "multiply"],
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
const toolkit = new MCPToolkit({
|
|
102
|
+
id: "filtered-toolkit",
|
|
103
|
+
server,
|
|
104
|
+
});
|
|
105
|
+
const tools = await toolkit.list();
|
|
106
|
+
// Should have 4 tools (6 - 2 blocked)
|
|
107
|
+
expect(tools.length).toBe(4);
|
|
108
|
+
expect(tools.find((t) => t.id === "divide")).toBeUndefined();
|
|
109
|
+
expect(tools.find((t) => t.id === "multiply")).toBeUndefined();
|
|
110
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
111
|
+
await toolkit.destroy();
|
|
112
|
+
});
|
|
113
|
+
it("should apply toolkit-level filter to block tools", async () => {
|
|
114
|
+
const server = new MCPServerStdio({
|
|
115
|
+
id: "test-server",
|
|
116
|
+
command: "npx",
|
|
117
|
+
args: ["tsx", TEST_SERVER],
|
|
118
|
+
});
|
|
119
|
+
const toolkit = new MCPToolkit({
|
|
120
|
+
id: "filtered-toolkit",
|
|
121
|
+
server,
|
|
122
|
+
filter: async (ctx, tool) => {
|
|
123
|
+
// Block all string tools
|
|
124
|
+
return !["echo", "uppercase", "reverse"].includes(tool.id);
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const agent = new Agent({
|
|
128
|
+
id: "test-agent",
|
|
129
|
+
name: "Test Agent",
|
|
130
|
+
instructions: "Test",
|
|
131
|
+
toolkits: [toolkit],
|
|
132
|
+
});
|
|
133
|
+
const context = new Context({});
|
|
134
|
+
const tools = await toolkit.list(context);
|
|
135
|
+
// Should have 3 math tools only
|
|
136
|
+
expect(tools.length).toBe(3);
|
|
137
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
138
|
+
expect(tools.find((t) => t.id === "echo")).toBeUndefined();
|
|
139
|
+
await toolkit.destroy();
|
|
140
|
+
});
|
|
141
|
+
it("should combine server and toolkit filters with AND logic", async () => {
|
|
142
|
+
const server = new MCPServerStdio({
|
|
143
|
+
id: "filtered-server",
|
|
144
|
+
command: "npx",
|
|
145
|
+
args: ["tsx", TEST_SERVER],
|
|
146
|
+
toolFilter: createMCPToolStaticFilter({
|
|
147
|
+
// Only allow math tools at server level
|
|
148
|
+
allowed: ["add", "multiply", "divide"],
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
const toolkit = new MCPToolkit({
|
|
152
|
+
id: "filtered-toolkit",
|
|
153
|
+
server,
|
|
154
|
+
// At toolkit level, block multiply
|
|
155
|
+
filter: async (ctx, tool) => tool.id !== "multiply",
|
|
156
|
+
});
|
|
157
|
+
const agent = new Agent({
|
|
158
|
+
id: "test-agent",
|
|
159
|
+
name: "Test Agent",
|
|
160
|
+
instructions: "Test",
|
|
161
|
+
toolkits: [toolkit],
|
|
162
|
+
});
|
|
163
|
+
const context = new Context({});
|
|
164
|
+
const tools = await toolkit.list(context);
|
|
165
|
+
// Should have only add and divide (multiply blocked by toolkit filter)
|
|
166
|
+
expect(tools.length).toBe(2);
|
|
167
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
168
|
+
expect(tools.find((t) => t.id === "divide")).toBeDefined();
|
|
169
|
+
expect(tools.find((t) => t.id === "multiply")).toBeUndefined();
|
|
170
|
+
expect(tools.find((t) => t.id === "echo")).toBeUndefined();
|
|
171
|
+
await toolkit.destroy();
|
|
172
|
+
});
|
|
173
|
+
it("should receive correct context in toolkit filter", async () => {
|
|
174
|
+
const server = new MCPServerStdio({
|
|
175
|
+
id: "test-server",
|
|
176
|
+
command: "npx",
|
|
177
|
+
args: ["tsx", TEST_SERVER],
|
|
178
|
+
});
|
|
179
|
+
let receivedContext = null;
|
|
180
|
+
const toolkit = new MCPToolkit({
|
|
181
|
+
id: "test-toolkit",
|
|
182
|
+
server,
|
|
183
|
+
filter: async (ctx, tool) => {
|
|
184
|
+
receivedContext = ctx;
|
|
185
|
+
return true;
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
const agent = new Agent({
|
|
189
|
+
id: "test-agent",
|
|
190
|
+
name: "Test Agent",
|
|
191
|
+
instructions: "Test",
|
|
192
|
+
toolkits: [toolkit],
|
|
193
|
+
});
|
|
194
|
+
const context = new Context({ userId: "test-user" });
|
|
195
|
+
await toolkit.list(context);
|
|
196
|
+
// Verify context was passed correctly
|
|
197
|
+
expect(receivedContext).toBeDefined();
|
|
198
|
+
expect(receivedContext.context).toBe(context);
|
|
199
|
+
expect(receivedContext.agent).toBe(agent);
|
|
200
|
+
expect(receivedContext.toolkitId).toBe("test-toolkit");
|
|
201
|
+
await toolkit.destroy();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
describe("Agent Integration", () => {
|
|
205
|
+
it("should include MCP tools in agent.tools()", async () => {
|
|
206
|
+
const server = new MCPServerStdio({
|
|
207
|
+
id: "test-server",
|
|
208
|
+
command: "npx",
|
|
209
|
+
args: ["tsx", TEST_SERVER],
|
|
210
|
+
});
|
|
211
|
+
const mcpToolkit = new MCPToolkit({
|
|
212
|
+
id: "mcp-toolkit",
|
|
213
|
+
server,
|
|
214
|
+
});
|
|
215
|
+
const agent = new Agent({
|
|
216
|
+
id: "test-agent",
|
|
217
|
+
name: "Test Agent",
|
|
218
|
+
instructions: "Test",
|
|
219
|
+
toolkits: [mcpToolkit],
|
|
220
|
+
});
|
|
221
|
+
const context = new Context({});
|
|
222
|
+
const tools = await agent.tools(context);
|
|
223
|
+
expect(tools.length).toBe(6);
|
|
224
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
225
|
+
expect(tools.find((t) => t.id === "echo")).toBeDefined();
|
|
226
|
+
await mcpToolkit.destroy();
|
|
227
|
+
});
|
|
228
|
+
it("should find MCP tools via agent.tool()", async () => {
|
|
229
|
+
const server = new MCPServerStdio({
|
|
230
|
+
id: "test-server",
|
|
231
|
+
command: "npx",
|
|
232
|
+
args: ["tsx", TEST_SERVER],
|
|
233
|
+
});
|
|
234
|
+
const mcpToolkit = new MCPToolkit({
|
|
235
|
+
id: "mcp-toolkit",
|
|
236
|
+
server,
|
|
237
|
+
});
|
|
238
|
+
const agent = new Agent({
|
|
239
|
+
id: "test-agent",
|
|
240
|
+
name: "Test Agent",
|
|
241
|
+
instructions: "Test",
|
|
242
|
+
toolkits: [mcpToolkit],
|
|
243
|
+
});
|
|
244
|
+
// Populate toolkit cache
|
|
245
|
+
const context = new Context({});
|
|
246
|
+
await agent.tools(context);
|
|
247
|
+
// Now get specific tool
|
|
248
|
+
const addTool = agent.tool("add");
|
|
249
|
+
expect(addTool).toBeDefined();
|
|
250
|
+
expect(addTool.id).toBe("add");
|
|
251
|
+
await mcpToolkit.destroy();
|
|
252
|
+
});
|
|
253
|
+
it("should execute MCP tools through tool.invoke()", async () => {
|
|
254
|
+
const server = new MCPServerStdio({
|
|
255
|
+
id: "test-server",
|
|
256
|
+
command: "npx",
|
|
257
|
+
args: ["tsx", TEST_SERVER],
|
|
258
|
+
});
|
|
259
|
+
const mcpToolkit = new MCPToolkit({
|
|
260
|
+
id: "mcp-toolkit",
|
|
261
|
+
server,
|
|
262
|
+
});
|
|
263
|
+
const agent = new Agent({
|
|
264
|
+
id: "test-agent",
|
|
265
|
+
name: "Test Agent",
|
|
266
|
+
instructions: "Test",
|
|
267
|
+
toolkits: [mcpToolkit],
|
|
268
|
+
});
|
|
269
|
+
const context = new Context({});
|
|
270
|
+
await agent.tools(context);
|
|
271
|
+
const addTool = agent.tool("add");
|
|
272
|
+
expect(addTool).toBeDefined();
|
|
273
|
+
// Execute the tool
|
|
274
|
+
if (addTool && addTool.type === "function") {
|
|
275
|
+
const result = await addTool.invoke(context, JSON.stringify({ a: 5, b: 3 }));
|
|
276
|
+
expect(result.state).toBe("completed");
|
|
277
|
+
expect(result.result).toEqual({ type: "text", text: "8" });
|
|
278
|
+
}
|
|
279
|
+
await mcpToolkit.destroy();
|
|
280
|
+
});
|
|
281
|
+
it("should work with multiple toolkits together", async () => {
|
|
282
|
+
const server = new MCPServerStdio({
|
|
283
|
+
id: "test-server",
|
|
284
|
+
command: "npx",
|
|
285
|
+
args: ["tsx", TEST_SERVER],
|
|
286
|
+
});
|
|
287
|
+
const mcpToolkit = new MCPToolkit({
|
|
288
|
+
id: "mcp-toolkit",
|
|
289
|
+
server,
|
|
290
|
+
});
|
|
291
|
+
// Create a function toolkit
|
|
292
|
+
const localTool = tool({
|
|
293
|
+
id: "local_tool",
|
|
294
|
+
name: "local_tool",
|
|
295
|
+
description: "A local tool",
|
|
296
|
+
parameters: z.object({
|
|
297
|
+
input: z.string(),
|
|
298
|
+
}),
|
|
299
|
+
execute: async (context, params) => {
|
|
300
|
+
return `Local: ${params.input}`;
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
const functionToolkit = new FunctionToolkit({
|
|
304
|
+
id: "function-toolkit",
|
|
305
|
+
tools: [localTool],
|
|
306
|
+
});
|
|
307
|
+
const agent = new Agent({
|
|
308
|
+
id: "test-agent",
|
|
309
|
+
name: "Test Agent",
|
|
310
|
+
instructions: "Test",
|
|
311
|
+
toolkits: [mcpToolkit, functionToolkit],
|
|
312
|
+
});
|
|
313
|
+
const context = new Context({});
|
|
314
|
+
const tools = await agent.tools(context);
|
|
315
|
+
// Should have 7 tools (6 MCP + 1 local)
|
|
316
|
+
expect(tools.length).toBe(7);
|
|
317
|
+
expect(tools.find((t) => t.id === "add")).toBeDefined();
|
|
318
|
+
expect(tools.find((t) => t.id === "local_tool")).toBeDefined();
|
|
319
|
+
await mcpToolkit.destroy();
|
|
320
|
+
});
|
|
321
|
+
it("should detect duplicate tool IDs across toolkits", async () => {
|
|
322
|
+
const server = new MCPServerStdio({
|
|
323
|
+
id: "test-server",
|
|
324
|
+
command: "npx",
|
|
325
|
+
args: ["tsx", TEST_SERVER],
|
|
326
|
+
});
|
|
327
|
+
const mcpToolkit = new MCPToolkit({
|
|
328
|
+
id: "mcp-toolkit",
|
|
329
|
+
server,
|
|
330
|
+
});
|
|
331
|
+
// Create a function toolkit with a duplicate ID
|
|
332
|
+
const duplicateTool = tool({
|
|
333
|
+
id: "add", // Same as MCP tool
|
|
334
|
+
name: "add",
|
|
335
|
+
description: "Duplicate add tool",
|
|
336
|
+
parameters: z.object({
|
|
337
|
+
a: z.number(),
|
|
338
|
+
b: z.number(),
|
|
339
|
+
}),
|
|
340
|
+
execute: async (context, params) => {
|
|
341
|
+
return params.a + params.b;
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
const functionToolkit = new FunctionToolkit({
|
|
345
|
+
id: "function-toolkit",
|
|
346
|
+
tools: [duplicateTool],
|
|
347
|
+
});
|
|
348
|
+
const agent = new Agent({
|
|
349
|
+
id: "test-agent",
|
|
350
|
+
name: "Test Agent",
|
|
351
|
+
instructions: "Test",
|
|
352
|
+
toolkits: [mcpToolkit, functionToolkit],
|
|
353
|
+
});
|
|
354
|
+
const context = new Context({});
|
|
355
|
+
// Should throw error about duplicate tool IDs
|
|
356
|
+
await expect(agent.tools(context)).rejects.toThrow(/Duplicate tool IDs found/);
|
|
357
|
+
await mcpToolkit.destroy();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../../src/mcp/__tests__/stdio.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { MCPServerStdio } from "../stdio";
|
|
3
|
+
import { withMCPServer, createMCPServer } from "./fixtures/utils";
|
|
4
|
+
import path from "path";
|
|
5
|
+
const TEST_SERVER = path.join(__dirname, "fixtures", "server.ts");
|
|
6
|
+
describe("MCPServerStdio", () => {
|
|
7
|
+
describe("Connection Lifecycle", () => {
|
|
8
|
+
it("should connect successfully to a server", async () => {
|
|
9
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
10
|
+
expect(server).toBeDefined();
|
|
11
|
+
// If we get here, connection was successful
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
it("should handle connection failure gracefully", async () => {
|
|
15
|
+
const server = createMCPServer("/nonexistent/server.ts");
|
|
16
|
+
await expect(server.connect()).rejects.toThrow();
|
|
17
|
+
});
|
|
18
|
+
it("should close connection cleanly", async () => {
|
|
19
|
+
const server = createMCPServer(TEST_SERVER);
|
|
20
|
+
await server.connect();
|
|
21
|
+
await server.close();
|
|
22
|
+
// Attempting to call a tool after close should fail
|
|
23
|
+
await expect(server.callTool("add", { a: 1, b: 2 })).rejects.toThrow();
|
|
24
|
+
});
|
|
25
|
+
it("should allow reconnection after close", async () => {
|
|
26
|
+
const server = createMCPServer(TEST_SERVER);
|
|
27
|
+
await server.connect();
|
|
28
|
+
await server.close();
|
|
29
|
+
await server.connect();
|
|
30
|
+
const tools = await server.listTools();
|
|
31
|
+
expect(tools.length).toBeGreaterThan(0);
|
|
32
|
+
await server.close();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("Tool Discovery", () => {
|
|
36
|
+
it("should list tools after connect", async () => {
|
|
37
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
38
|
+
const tools = await server.listTools();
|
|
39
|
+
expect(tools).toHaveLength(6);
|
|
40
|
+
expect(tools.map((t) => t.name)).toEqual(expect.arrayContaining([
|
|
41
|
+
"add",
|
|
42
|
+
"multiply",
|
|
43
|
+
"divide",
|
|
44
|
+
"echo",
|
|
45
|
+
"uppercase",
|
|
46
|
+
"reverse",
|
|
47
|
+
]));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
it("should include tool metadata", async () => {
|
|
51
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
52
|
+
const tools = await server.listTools();
|
|
53
|
+
const addTool = tools.find((t) => t.name === "add");
|
|
54
|
+
expect(addTool).toBeDefined();
|
|
55
|
+
expect(addTool.description).toBe("Add two numbers");
|
|
56
|
+
expect(addTool.inputSchema).toBeDefined();
|
|
57
|
+
expect(addTool.inputSchema.properties).toHaveProperty("a");
|
|
58
|
+
expect(addTool.inputSchema.properties).toHaveProperty("b");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
it("should handle empty tool list", async () => {
|
|
62
|
+
// We'd need a server with no tools for this, skip for now
|
|
63
|
+
// or create a minimal-server fixture
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("Tool Execution", () => {
|
|
67
|
+
it("should call tool with valid params", async () => {
|
|
68
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
69
|
+
const result = await server.callTool("add", { a: 5, b: 3 });
|
|
70
|
+
expect(result).toHaveLength(1);
|
|
71
|
+
expect(result[0].type).toBe("text");
|
|
72
|
+
expect(result[0].text).toBe("8");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it("should handle multiple different tool calls", async () => {
|
|
76
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
77
|
+
const add = await server.callTool("add", { a: 10, b: 20 });
|
|
78
|
+
expect(add[0].text).toBe("30");
|
|
79
|
+
const multiply = await server.callTool("multiply", { a: 4, b: 5 });
|
|
80
|
+
expect(multiply[0].text).toBe("20");
|
|
81
|
+
const divide = await server.callTool("divide", { a: 100, b: 4 });
|
|
82
|
+
expect(divide[0].text).toBe("25");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
it("should handle tool execution error", async () => {
|
|
86
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
87
|
+
// Division by zero should throw
|
|
88
|
+
await expect(server.callTool("divide", { a: 10, b: 0 })).rejects.toThrow("Division by zero");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
it("should handle tool not found", async () => {
|
|
92
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
93
|
+
await expect(server.callTool("nonexistent", { foo: "bar" })).rejects.toThrow();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
it("should work with string manipulation tools", async () => {
|
|
97
|
+
await withMCPServer(TEST_SERVER, async (server) => {
|
|
98
|
+
const echo = await server.callTool("echo", { text: "hello" });
|
|
99
|
+
expect(echo[0].text).toBe("hello");
|
|
100
|
+
const upper = await server.callTool("uppercase", { text: "hello" });
|
|
101
|
+
expect(upper[0].text).toBe("HELLO");
|
|
102
|
+
const reverse = await server.callTool("reverse", { text: "hello" });
|
|
103
|
+
expect(reverse[0].text).toBe("olleh");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe("Caching", () => {
|
|
108
|
+
it("should not cache tools list by default", async () => {
|
|
109
|
+
const server = createMCPServer(TEST_SERVER, {
|
|
110
|
+
cacheToolsList: false,
|
|
111
|
+
});
|
|
112
|
+
await server.connect();
|
|
113
|
+
const tools1 = await server.listTools();
|
|
114
|
+
const tools2 = await server.listTools();
|
|
115
|
+
// Both should succeed (no cache = fresh fetch each time)
|
|
116
|
+
expect(tools1).toHaveLength(6);
|
|
117
|
+
expect(tools2).toHaveLength(6);
|
|
118
|
+
await server.close();
|
|
119
|
+
});
|
|
120
|
+
it("should cache tools list when enabled", async () => {
|
|
121
|
+
const server = createMCPServer(TEST_SERVER, {
|
|
122
|
+
cacheToolsList: true,
|
|
123
|
+
});
|
|
124
|
+
await server.connect();
|
|
125
|
+
const tools1 = await server.listTools();
|
|
126
|
+
const tools2 = await server.listTools();
|
|
127
|
+
// Should get same results from cache
|
|
128
|
+
expect(tools1).toHaveLength(6);
|
|
129
|
+
expect(tools2).toHaveLength(6);
|
|
130
|
+
expect(tools1).toEqual(tools2);
|
|
131
|
+
await server.close();
|
|
132
|
+
});
|
|
133
|
+
it("should invalidate cache when requested", async () => {
|
|
134
|
+
const server = createMCPServer(TEST_SERVER, {
|
|
135
|
+
cacheToolsList: true,
|
|
136
|
+
});
|
|
137
|
+
await server.connect();
|
|
138
|
+
const tools1 = await server.listTools();
|
|
139
|
+
expect(tools1).toHaveLength(6);
|
|
140
|
+
await server.invalidateCache();
|
|
141
|
+
const tools2 = await server.listTools();
|
|
142
|
+
expect(tools2).toHaveLength(6);
|
|
143
|
+
await server.close();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe("Constructor options", () => {
|
|
147
|
+
it("should accept command and args", async () => {
|
|
148
|
+
const server = new MCPServerStdio({
|
|
149
|
+
id: "test",
|
|
150
|
+
command: "npx",
|
|
151
|
+
args: ["tsx", TEST_SERVER],
|
|
152
|
+
});
|
|
153
|
+
await server.connect();
|
|
154
|
+
const tools = await server.listTools();
|
|
155
|
+
expect(tools.length).toBeGreaterThan(0);
|
|
156
|
+
await server.close();
|
|
157
|
+
});
|
|
158
|
+
it("should use custom id", async () => {
|
|
159
|
+
const server = createMCPServer(TEST_SERVER, { id: "my-test-server" });
|
|
160
|
+
expect(server.id).toBe("my-test-server");
|
|
161
|
+
});
|
|
162
|
+
it("should generate default id from command", async () => {
|
|
163
|
+
const server = new MCPServerStdio({
|
|
164
|
+
command: "npx",
|
|
165
|
+
args: ["tsx", TEST_SERVER],
|
|
166
|
+
});
|
|
167
|
+
expect(server.id).toContain("npx");
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe("Error handling", () => {
|
|
171
|
+
it("should throw error when calling tool before connect", async () => {
|
|
172
|
+
const server = createMCPServer(TEST_SERVER);
|
|
173
|
+
await expect(server.callTool("add", { a: 1, b: 2 })).rejects.toThrow();
|
|
174
|
+
});
|
|
175
|
+
it("should throw error when listing tools before connect", async () => {
|
|
176
|
+
const server = createMCPServer(TEST_SERVER);
|
|
177
|
+
await expect(server.listTools()).rejects.toThrow();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MCPServerStdio } from "../stdio";
|
|
2
|
+
/**
|
|
3
|
+
* Helper to run a test with an MCP server, ensuring cleanup.
|
|
4
|
+
*/
|
|
5
|
+
export declare function withMCPServer<T>(serverPath: string, fn: (server: MCPServerStdio) => Promise<T>): Promise<T>;
|
|
6
|
+
/**
|
|
7
|
+
* Helper to create a server without auto-connecting.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createMCPServer(serverPath: string, options?: {
|
|
10
|
+
id?: string;
|
|
11
|
+
cacheToolsList?: boolean;
|
|
12
|
+
}): MCPServerStdio;
|
|
13
|
+
/**
|
|
14
|
+
* Wait for a condition to be true or timeout.
|
|
15
|
+
*/
|
|
16
|
+
export declare function waitFor(condition: () => boolean | Promise<boolean>, timeout?: number, interval?: number): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=test-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/mcp/__tests__/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C;;GAEG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,CAAC,CAaZ;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,cAAc,CAOhB;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EAC3C,OAAO,SAAO,EACd,QAAQ,SAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MCPServerStdio } from "../stdio";
|
|
2
|
+
/**
|
|
3
|
+
* Helper to run a test with an MCP server, ensuring cleanup.
|
|
4
|
+
*/
|
|
5
|
+
export async function withMCPServer(serverPath, fn) {
|
|
6
|
+
const server = new MCPServerStdio({
|
|
7
|
+
id: "test-server",
|
|
8
|
+
command: "npx",
|
|
9
|
+
args: ["tsx", serverPath],
|
|
10
|
+
});
|
|
11
|
+
try {
|
|
12
|
+
await server.connect();
|
|
13
|
+
return await fn(server);
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
await server.close();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Helper to create a server without auto-connecting.
|
|
21
|
+
*/
|
|
22
|
+
export function createMCPServer(serverPath, options = {}) {
|
|
23
|
+
return new MCPServerStdio({
|
|
24
|
+
id: options.id ?? "test-server",
|
|
25
|
+
command: "npx",
|
|
26
|
+
args: ["tsx", serverPath],
|
|
27
|
+
cacheToolsList: options.cacheToolsList,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wait for a condition to be true or timeout.
|
|
32
|
+
*/
|
|
33
|
+
export async function waitFor(condition, timeout = 5000, interval = 100) {
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
while (Date.now() - startTime < timeout) {
|
|
36
|
+
if (await condition()) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Timeout waiting for condition after ${timeout}ms`);
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../../src/mcp/__tests__/utils.test.ts"],"names":[],"mappings":""}
|