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,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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stdio.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../../src/mcp/__tests__/utils.test.ts"],"names":[],"mappings":""}