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.
Files changed (257) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +53 -0
  3. package/LICENSE +201 -0
  4. package/dist/agent.d.ts +43 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/agent.js +130 -0
  7. package/dist/context.d.ts +70 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +111 -0
  10. package/dist/env.d.ts +45 -0
  11. package/dist/env.d.ts.map +1 -0
  12. package/dist/env.js +31 -0
  13. package/dist/error.d.ts +1 -0
  14. package/dist/error.d.ts.map +1 -0
  15. package/dist/error.js +1 -0
  16. package/dist/guardrail.d.ts +178 -0
  17. package/dist/guardrail.d.ts.map +1 -0
  18. package/dist/guardrail.js +34 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +2 -0
  22. package/dist/kernel.d.ts +7 -0
  23. package/dist/kernel.d.ts.map +1 -0
  24. package/dist/kernel.js +7 -0
  25. package/dist/kernl.d.ts +18 -0
  26. package/dist/kernl.d.ts.map +1 -0
  27. package/dist/kernl.js +16 -0
  28. package/dist/lib/env.d.ts +43 -0
  29. package/dist/lib/env.d.ts.map +1 -0
  30. package/dist/lib/env.js +29 -0
  31. package/dist/lib/error.d.ts +88 -0
  32. package/dist/lib/error.d.ts.map +1 -0
  33. package/dist/lib/error.js +117 -0
  34. package/dist/lib/logger.d.ts +36 -0
  35. package/dist/lib/logger.d.ts.map +1 -0
  36. package/dist/lib/logger.js +43 -0
  37. package/dist/lib/serde/__tests__/codec.test.d.ts +2 -0
  38. package/dist/lib/serde/__tests__/codec.test.d.ts.map +1 -0
  39. package/dist/lib/serde/__tests__/codec.test.js +75 -0
  40. package/dist/lib/serde/codec.d.ts +12 -0
  41. package/dist/lib/serde/codec.d.ts.map +1 -0
  42. package/dist/lib/serde/codec.js +54 -0
  43. package/dist/lib/serde/json.d.ts +8 -0
  44. package/dist/lib/serde/json.d.ts.map +1 -0
  45. package/dist/lib/serde/json.js +13 -0
  46. package/dist/lib/serde/thread.d.ts +1 -0
  47. package/dist/lib/serde/thread.d.ts.map +1 -0
  48. package/dist/lib/serde/thread.js +172 -0
  49. package/dist/lib/serde/tool.d.ts +36 -0
  50. package/dist/lib/serde/tool.d.ts.map +1 -0
  51. package/dist/lib/serde/tool.js +1 -0
  52. package/dist/lib/utils.d.ts +19 -0
  53. package/dist/lib/utils.d.ts.map +1 -0
  54. package/dist/lib/utils.js +41 -0
  55. package/dist/lifecycle.d.ts +133 -0
  56. package/dist/lifecycle.d.ts.map +1 -0
  57. package/dist/lifecycle.js +29 -0
  58. package/dist/logger.d.ts +36 -0
  59. package/dist/logger.d.ts.map +1 -0
  60. package/dist/logger.js +43 -0
  61. package/dist/mcp/__tests__/base.test.d.ts +2 -0
  62. package/dist/mcp/__tests__/base.test.d.ts.map +1 -0
  63. package/dist/mcp/__tests__/base.test.js +268 -0
  64. package/dist/mcp/__tests__/fixtures/echo-server.d.ts +3 -0
  65. package/dist/mcp/__tests__/fixtures/echo-server.d.ts.map +1 -0
  66. package/dist/mcp/__tests__/fixtures/echo-server.js +92 -0
  67. package/dist/mcp/__tests__/fixtures/math-server.d.ts +3 -0
  68. package/dist/mcp/__tests__/fixtures/math-server.d.ts.map +1 -0
  69. package/dist/mcp/__tests__/fixtures/math-server.js +98 -0
  70. package/dist/mcp/__tests__/fixtures/server.d.ts +3 -0
  71. package/dist/mcp/__tests__/fixtures/server.d.ts.map +1 -0
  72. package/dist/mcp/__tests__/fixtures/server.js +162 -0
  73. package/dist/mcp/__tests__/fixtures/test-server.d.ts +3 -0
  74. package/dist/mcp/__tests__/fixtures/test-server.d.ts.map +1 -0
  75. package/dist/mcp/__tests__/fixtures/test-server.js +163 -0
  76. package/dist/mcp/__tests__/fixtures/utils.d.ts +17 -0
  77. package/dist/mcp/__tests__/fixtures/utils.d.ts.map +1 -0
  78. package/dist/mcp/__tests__/fixtures/utils.js +42 -0
  79. package/dist/mcp/__tests__/integration.test.d.ts +2 -0
  80. package/dist/mcp/__tests__/integration.test.d.ts.map +1 -0
  81. package/dist/mcp/__tests__/integration.test.js +360 -0
  82. package/dist/mcp/__tests__/stdio.test.d.ts +2 -0
  83. package/dist/mcp/__tests__/stdio.test.d.ts.map +1 -0
  84. package/dist/mcp/__tests__/stdio.test.js +180 -0
  85. package/dist/mcp/__tests__/test-utils.d.ts +17 -0
  86. package/dist/mcp/__tests__/test-utils.d.ts.map +1 -0
  87. package/dist/mcp/__tests__/test-utils.js +42 -0
  88. package/dist/mcp/__tests__/utils.test.d.ts +2 -0
  89. package/dist/mcp/__tests__/utils.test.d.ts.map +1 -0
  90. package/dist/mcp/__tests__/utils.test.js +300 -0
  91. package/dist/mcp/base.d.ts +88 -0
  92. package/dist/mcp/base.d.ts.map +1 -0
  93. package/dist/mcp/base.js +68 -0
  94. package/dist/mcp/http.d.ts +34 -0
  95. package/dist/mcp/http.d.ts.map +1 -0
  96. package/dist/mcp/http.js +100 -0
  97. package/dist/mcp/node.d.ts +60 -0
  98. package/dist/mcp/node.d.ts.map +1 -0
  99. package/dist/mcp/node.js +297 -0
  100. package/dist/mcp/sse.d.ts +34 -0
  101. package/dist/mcp/sse.d.ts.map +1 -0
  102. package/dist/mcp/sse.js +97 -0
  103. package/dist/mcp/stdio.d.ts +32 -0
  104. package/dist/mcp/stdio.d.ts.map +1 -0
  105. package/dist/mcp/stdio.js +96 -0
  106. package/dist/mcp/types.d.ts +172 -0
  107. package/dist/mcp/types.d.ts.map +1 -0
  108. package/dist/mcp/types.js +16 -0
  109. package/dist/mcp/utils.d.ts +23 -0
  110. package/dist/mcp/utils.d.ts.map +1 -0
  111. package/dist/mcp/utils.js +44 -0
  112. package/dist/model.d.ts +175 -0
  113. package/dist/model.d.ts.map +1 -0
  114. package/dist/model.js +1 -0
  115. package/dist/providers/ai.d.ts +1 -0
  116. package/dist/providers/ai.d.ts.map +1 -0
  117. package/dist/providers/ai.js +1 -0
  118. package/dist/providers/default.d.ts +16 -0
  119. package/dist/providers/default.d.ts.map +1 -0
  120. package/dist/providers/default.js +17 -0
  121. package/dist/providers/registry.d.ts +1 -0
  122. package/dist/providers/registry.d.ts.map +1 -0
  123. package/dist/providers/registry.js +1 -0
  124. package/dist/sched/scheduler.d.ts +20 -0
  125. package/dist/sched/scheduler.d.ts.map +1 -0
  126. package/dist/sched/scheduler.js +1 -0
  127. package/dist/sched/task.d.ts +92 -0
  128. package/dist/sched/task.d.ts.map +1 -0
  129. package/dist/sched/task.js +102 -0
  130. package/dist/serde/__tests__/codec.test.d.ts +2 -0
  131. package/dist/serde/__tests__/codec.test.d.ts.map +1 -0
  132. package/dist/serde/__tests__/codec.test.js +75 -0
  133. package/dist/serde/codec.d.ts +12 -0
  134. package/dist/serde/codec.d.ts.map +1 -0
  135. package/dist/serde/codec.js +54 -0
  136. package/dist/serde/json.d.ts +8 -0
  137. package/dist/serde/json.d.ts.map +1 -0
  138. package/dist/serde/json.js +13 -0
  139. package/dist/serde/thread.d.ts +687 -0
  140. package/dist/serde/thread.d.ts.map +1 -0
  141. package/dist/serde/thread.js +158 -0
  142. package/dist/serde/tool.d.ts +36 -0
  143. package/dist/serde/tool.d.ts.map +1 -0
  144. package/dist/serde/tool.js +1 -0
  145. package/dist/session.d.ts +1 -0
  146. package/dist/session.d.ts.map +1 -0
  147. package/dist/session.js +1 -0
  148. package/dist/task.d.ts +87 -0
  149. package/dist/task.d.ts.map +1 -0
  150. package/dist/task.js +97 -0
  151. package/dist/thread/__tests__/mock.d.ts +28 -0
  152. package/dist/thread/__tests__/mock.d.ts.map +1 -0
  153. package/dist/thread/__tests__/mock.js +74 -0
  154. package/dist/thread/__tests__/thread.test.d.ts +2 -0
  155. package/dist/thread/__tests__/thread.test.d.ts.map +1 -0
  156. package/dist/thread/__tests__/thread.test.js +1412 -0
  157. package/dist/thread/index.d.ts +2 -0
  158. package/dist/thread/index.d.ts.map +1 -0
  159. package/dist/thread/index.js +1 -0
  160. package/dist/thread/thread.d.ts +66 -0
  161. package/dist/thread/thread.d.ts.map +1 -0
  162. package/dist/thread/thread.js +472 -0
  163. package/dist/thread/utils.d.ts +19 -0
  164. package/dist/thread/utils.d.ts.map +1 -0
  165. package/dist/thread/utils.js +50 -0
  166. package/dist/tool/__tests__/fixtures.d.ts +45 -0
  167. package/dist/tool/__tests__/fixtures.d.ts.map +1 -0
  168. package/dist/tool/__tests__/fixtures.js +97 -0
  169. package/dist/tool/__tests__/tool.test.d.ts +2 -0
  170. package/dist/tool/__tests__/tool.test.d.ts.map +1 -0
  171. package/dist/tool/__tests__/tool.test.js +172 -0
  172. package/dist/tool/__tests__/toolkit.test.d.ts +2 -0
  173. package/dist/tool/__tests__/toolkit.test.d.ts.map +1 -0
  174. package/dist/tool/__tests__/toolkit.test.js +134 -0
  175. package/dist/tool/index.d.ts +4 -0
  176. package/dist/tool/index.d.ts.map +1 -0
  177. package/dist/tool/index.js +2 -0
  178. package/dist/tool/mcp.d.ts +75 -0
  179. package/dist/tool/mcp.d.ts.map +1 -0
  180. package/dist/tool/mcp.js +111 -0
  181. package/dist/tool/tool.d.ts +95 -0
  182. package/dist/tool/tool.d.ts.map +1 -0
  183. package/dist/tool/tool.js +176 -0
  184. package/dist/tool/toolkit.d.ts +121 -0
  185. package/dist/tool/toolkit.d.ts.map +1 -0
  186. package/dist/tool/toolkit.js +180 -0
  187. package/dist/tool/types.d.ts +187 -0
  188. package/dist/tool/types.d.ts.map +1 -0
  189. package/dist/tool/types.js +1 -0
  190. package/dist/tools.d.ts +362 -0
  191. package/dist/tools.d.ts.map +1 -0
  192. package/dist/tools.js +220 -0
  193. package/dist/trace/processor.d.ts +1 -0
  194. package/dist/trace/processor.d.ts.map +1 -0
  195. package/dist/trace/processor.js +1 -0
  196. package/dist/trace/traces.d.ts +1 -0
  197. package/dist/trace/traces.d.ts.map +1 -0
  198. package/dist/trace/traces.js +73 -0
  199. package/dist/trace/utils.d.ts +22 -0
  200. package/dist/trace/utils.d.ts.map +1 -0
  201. package/dist/trace/utils.js +30 -0
  202. package/dist/types/agent.d.ts +91 -0
  203. package/dist/types/agent.d.ts.map +1 -0
  204. package/dist/types/agent.js +1 -0
  205. package/dist/types/proto.d.ts +1551 -0
  206. package/dist/types/proto.d.ts.map +1 -0
  207. package/dist/types/proto.js +531 -0
  208. package/dist/types/thread.d.ts +71 -0
  209. package/dist/types/thread.d.ts.map +1 -0
  210. package/dist/types/thread.js +5 -0
  211. package/dist/usage.d.ts +43 -0
  212. package/dist/usage.d.ts.map +1 -0
  213. package/dist/usage.js +61 -0
  214. package/package.json +52 -0
  215. package/src/agent.ts +203 -0
  216. package/src/context.ts +265 -0
  217. package/src/guardrail.ts +277 -0
  218. package/src/index.ts +3 -0
  219. package/src/kernl.ts +22 -0
  220. package/src/lib/env.ts +36 -0
  221. package/src/lib/error.ts +158 -0
  222. package/src/lib/logger.ts +78 -0
  223. package/src/lib/serde/json.ts +18 -0
  224. package/src/lib/serde/thread.ts +188 -0
  225. package/src/lifecycle.ts +181 -0
  226. package/src/mcp/__tests__/base.test.ts +344 -0
  227. package/src/mcp/__tests__/fixtures/server.ts +179 -0
  228. package/src/mcp/__tests__/fixtures/utils.ts +58 -0
  229. package/src/mcp/__tests__/integration.test.ts +447 -0
  230. package/src/mcp/__tests__/stdio.test.ts +236 -0
  231. package/src/mcp/__tests__/utils.test.ts +360 -0
  232. package/src/mcp/base.ts +162 -0
  233. package/src/mcp/http.ts +147 -0
  234. package/src/mcp/sse.ts +137 -0
  235. package/src/mcp/stdio.ts +136 -0
  236. package/src/mcp/types.ts +202 -0
  237. package/src/mcp/utils.ts +62 -0
  238. package/src/task.ts +119 -0
  239. package/src/thread/__tests__/mock.ts +95 -0
  240. package/src/thread/__tests__/thread.test.ts +1574 -0
  241. package/src/thread/index.ts +1 -0
  242. package/src/thread/thread.ts +611 -0
  243. package/src/thread/utils.ts +67 -0
  244. package/src/tool/__tests__/fixtures.ts +106 -0
  245. package/src/tool/__tests__/tool.test.ts +235 -0
  246. package/src/tool/__tests__/toolkit.test.ts +174 -0
  247. package/src/tool/index.ts +10 -0
  248. package/src/tool/tool.ts +264 -0
  249. package/src/tool/toolkit.ts +234 -0
  250. package/src/tool/types.ts +243 -0
  251. package/src/trace/processor.ts +0 -0
  252. package/src/trace/traces.ts +86 -0
  253. package/src/trace/utils.ts +38 -0
  254. package/src/types/agent.ts +145 -0
  255. package/src/types/thread.ts +86 -0
  256. package/tsconfig.json +13 -0
  257. 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
+ });