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,97 @@
1
+ import { z } from "zod";
2
+ import { Context } from "../../context";
3
+ import { tool, HostedTool } from "../tool";
4
+ /**
5
+ * Create a minimal mock context for testing
6
+ */
7
+ export const mockContext = (data) => {
8
+ return new Context(data ?? {});
9
+ };
10
+ /**
11
+ * Simple string tool with no parameters
12
+ */
13
+ export const simpleStringTool = tool({
14
+ id: "simple",
15
+ description: "A simple tool that echoes input",
16
+ parameters: undefined,
17
+ execute: async (ctx, input) => `Echo: ${input}`,
18
+ });
19
+ /**
20
+ * Tool with Zod schema validation
21
+ */
22
+ export const zodTool = tool({
23
+ id: "zod-tool",
24
+ description: "Tool with Zod schema",
25
+ parameters: z.object({
26
+ name: z.string(),
27
+ age: z.number(),
28
+ }),
29
+ execute: async (ctx, { name, age }) => `${name} is ${age} years old`,
30
+ });
31
+ /**
32
+ * Tool that always throws an error
33
+ */
34
+ export const errorTool = tool({
35
+ id: "error-tool",
36
+ description: "Always throws an error",
37
+ parameters: undefined,
38
+ execute: async () => {
39
+ throw new Error("Tool execution failed");
40
+ },
41
+ });
42
+ /**
43
+ * Tool with custom error handler
44
+ */
45
+ export const customErrorTool = tool({
46
+ id: "custom-error-tool",
47
+ description: "Tool with custom error handler",
48
+ parameters: undefined,
49
+ execute: async () => {
50
+ throw new Error("Original error");
51
+ },
52
+ errorfn: (ctx, error) => {
53
+ return "Custom error message";
54
+ },
55
+ });
56
+ /**
57
+ * Tool requiring approval (boolean)
58
+ */
59
+ export const approvalRequiredTool = tool({
60
+ id: "approval-tool",
61
+ description: "Requires approval",
62
+ parameters: undefined,
63
+ requiresApproval: true,
64
+ execute: async () => "executed",
65
+ });
66
+ /**
67
+ * Tool with conditional approval (function)
68
+ */
69
+ export const conditionalApprovalTool = tool({
70
+ id: "conditional-approval",
71
+ description: "Conditionally requires approval",
72
+ parameters: z.object({
73
+ dangerous: z.boolean(),
74
+ }),
75
+ requiresApproval: async (ctx, input) => {
76
+ return input.dangerous === true;
77
+ },
78
+ execute: async (ctx, { dangerous }) => `Executed with dangerous=${dangerous}`,
79
+ });
80
+ /**
81
+ * Mock hosted tool (e.g., web_search)
82
+ */
83
+ export const mockHostedTool = new HostedTool({
84
+ id: "web-search",
85
+ name: "Web Search",
86
+ providerData: {
87
+ provider: "anthropic",
88
+ capabilities: ["search", "fetch"],
89
+ },
90
+ });
91
+ /**
92
+ * Another mock hosted tool
93
+ */
94
+ export const anotherHostedTool = new HostedTool({
95
+ id: "file-search",
96
+ name: "File Search",
97
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.test.d.ts","sourceRoot":"","sources":["../../../src/tool/__tests__/tool.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,172 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { COMPLETED, FAILED } from "@kernl/protocol";
3
+ import { tool } from "../tool";
4
+ import { mockContext, simpleStringTool, zodTool, errorTool, customErrorTool, approvalRequiredTool, conditionalApprovalTool, } from "./fixtures";
5
+ describe("FunctionTool", () => {
6
+ describe("creation", () => {
7
+ it("should create tool with required config", () => {
8
+ const t = tool({
9
+ id: "test",
10
+ description: "Test tool",
11
+ parameters: undefined,
12
+ execute: async () => "result",
13
+ });
14
+ expect(t.id).toBe("test");
15
+ expect(t.description).toBe("Test tool");
16
+ expect(t.type).toBe("function");
17
+ });
18
+ it("should default mode to blocking", () => {
19
+ const t = tool({
20
+ id: "test",
21
+ description: "Test tool",
22
+ parameters: undefined,
23
+ execute: async () => "result",
24
+ });
25
+ expect(t.mode).toBe("blocking");
26
+ });
27
+ it("should accept custom mode", () => {
28
+ const t = tool({
29
+ id: "test",
30
+ description: "Test tool",
31
+ parameters: undefined,
32
+ mode: "async",
33
+ execute: async () => "result",
34
+ });
35
+ expect(t.mode).toBe("async");
36
+ });
37
+ });
38
+ describe("parameter parsing", () => {
39
+ it("should handle string input when no parameters schema", async () => {
40
+ const ctx = mockContext();
41
+ const result = await simpleStringTool.invoke(ctx, "hello");
42
+ expect(result.state).toBe(COMPLETED);
43
+ expect(result.result).toBe("Echo: hello");
44
+ });
45
+ it("should parse and validate with Zod schema", async () => {
46
+ const ctx = mockContext();
47
+ const input = JSON.stringify({ name: "Alice", age: 30 });
48
+ const result = await zodTool.invoke(ctx, input);
49
+ expect(result.state).toBe(COMPLETED);
50
+ expect(result.result).toBe("Alice is 30 years old");
51
+ });
52
+ it("should throw ModelBehaviorError on invalid JSON", async () => {
53
+ const ctx = mockContext();
54
+ const result = await zodTool.invoke(ctx, "not valid json");
55
+ expect(result.state).toBe(FAILED);
56
+ expect(result.error).toContain("Invalid JSON input for tool");
57
+ });
58
+ it("should throw ModelBehaviorError on schema validation failure", async () => {
59
+ const ctx = mockContext();
60
+ const input = JSON.stringify({ name: "Alice", age: "not a number" });
61
+ const result = await zodTool.invoke(ctx, input);
62
+ expect(result.state).toBe(FAILED);
63
+ expect(result.error).toContain("Invalid JSON input for tool");
64
+ });
65
+ });
66
+ describe("execution", () => {
67
+ it("should execute and return ToolResult with completed status", async () => {
68
+ const ctx = mockContext();
69
+ const result = await simpleStringTool.invoke(ctx, "test");
70
+ expect(result.state).toBe(COMPLETED);
71
+ expect(result.result).toBe("Echo: test");
72
+ expect(result.error).toBe(null);
73
+ });
74
+ it("should handle sync execute functions", async () => {
75
+ const syncTool = tool({
76
+ id: "sync",
77
+ description: "Sync tool",
78
+ parameters: undefined,
79
+ execute: (ctx, input) => `Sync: ${input}`,
80
+ });
81
+ const ctx = mockContext();
82
+ const result = await syncTool.invoke(ctx, "test");
83
+ expect(result.state).toBe(COMPLETED);
84
+ expect(result.result).toBe("Sync: test");
85
+ });
86
+ it("should handle async execute functions", async () => {
87
+ const asyncTool = tool({
88
+ id: "async",
89
+ description: "Async tool",
90
+ parameters: undefined,
91
+ execute: async (ctx, input) => Promise.resolve(`Async: ${input}`),
92
+ });
93
+ const ctx = mockContext();
94
+ const result = await asyncTool.invoke(ctx, "test");
95
+ expect(result.state).toBe(COMPLETED);
96
+ expect(result.result).toBe("Async: test");
97
+ });
98
+ });
99
+ describe("error handling", () => {
100
+ it("should return error ToolResult when execute throws", async () => {
101
+ const ctx = mockContext();
102
+ const result = await errorTool.invoke(ctx, "test");
103
+ expect(result.state).toBe(FAILED);
104
+ expect(result.result).toBe(undefined);
105
+ expect(result.error).toContain("Tool execution failed");
106
+ });
107
+ it("should use custom error function if provided", async () => {
108
+ const ctx = mockContext();
109
+ const result = await customErrorTool.invoke(ctx, "test");
110
+ expect(result.state).toBe(FAILED);
111
+ expect(result.error).toBe("Custom error message");
112
+ });
113
+ it("should use default error function if none provided", async () => {
114
+ const ctx = mockContext();
115
+ const result = await errorTool.invoke(ctx, "test");
116
+ expect(result.state).toBe(FAILED);
117
+ expect(result.error).toContain("An error occurred while running the tool");
118
+ });
119
+ });
120
+ describe("approval", () => {
121
+ it("should evaluate boolean requiresApproval", async () => {
122
+ const ctx = mockContext();
123
+ const requiresApproval = await approvalRequiredTool.requiresApproval(ctx, "test");
124
+ expect(requiresApproval).toBe(true);
125
+ });
126
+ it("should evaluate function requiresApproval", async () => {
127
+ const ctx = mockContext();
128
+ const dangerous = await conditionalApprovalTool.requiresApproval(ctx, {
129
+ dangerous: true,
130
+ });
131
+ const safe = await conditionalApprovalTool.requiresApproval(ctx, {
132
+ dangerous: false,
133
+ });
134
+ expect(dangerous).toBe(true);
135
+ expect(safe).toBe(false);
136
+ });
137
+ });
138
+ describe("isEnabled", () => {
139
+ it("should default to enabled", async () => {
140
+ const ctx = mockContext();
141
+ const enabled = await simpleStringTool.isEnabled(ctx, null);
142
+ expect(enabled).toBe(true);
143
+ });
144
+ it("should evaluate boolean isEnabled", async () => {
145
+ const disabledTool = tool({
146
+ id: "disabled",
147
+ description: "Disabled tool",
148
+ parameters: undefined,
149
+ isEnabled: false,
150
+ execute: async () => "result",
151
+ });
152
+ const ctx = mockContext();
153
+ const enabled = await disabledTool.isEnabled(ctx, null);
154
+ expect(enabled).toBe(false);
155
+ });
156
+ it("should evaluate function isEnabled with typed context", async () => {
157
+ const conditionalTool = tool({
158
+ id: "conditional",
159
+ description: "Conditional tool",
160
+ parameters: undefined,
161
+ isEnabled: ({ context }) => {
162
+ return context.context.enabled === true;
163
+ },
164
+ execute: async () => "result",
165
+ });
166
+ const enabledCtx = mockContext({ enabled: true });
167
+ const disabledCtx = mockContext({ enabled: false });
168
+ expect(await conditionalTool.isEnabled(enabledCtx, null)).toBe(true);
169
+ expect(await conditionalTool.isEnabled(disabledCtx, null)).toBe(false);
170
+ });
171
+ });
172
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=toolkit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolkit.test.d.ts","sourceRoot":"","sources":["../../../src/tool/__tests__/toolkit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,134 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { FunctionToolkit } from "../toolkit";
3
+ import { simpleStringTool, zodTool, errorTool, mockHostedTool, anotherHostedTool, } from "./fixtures";
4
+ describe("FunctionToolkit", () => {
5
+ describe("constructor", () => {
6
+ it("should create empty toolkit with no tools", async () => {
7
+ const toolkit = new FunctionToolkit({ id: "empty", tools: [] });
8
+ expect(await toolkit.list()).toEqual([]);
9
+ });
10
+ it("should initialize with systools", async () => {
11
+ const toolkit = new FunctionToolkit({ id: "test", tools: [mockHostedTool] });
12
+ expect(await toolkit.list()).toHaveLength(1);
13
+ expect(toolkit.get("web-search")).toBe(mockHostedTool);
14
+ });
15
+ it("should initialize with function tools", async () => {
16
+ const toolkit = new FunctionToolkit({ id: "test", tools: [simpleStringTool, zodTool] });
17
+ expect(await toolkit.list()).toHaveLength(2);
18
+ expect(toolkit.get("simple")).toBe(simpleStringTool);
19
+ expect(toolkit.get("zod-tool")).toBe(zodTool);
20
+ });
21
+ it("should initialize with both systools and functions", async () => {
22
+ const toolkit = new FunctionToolkit({ id: "test", tools: [mockHostedTool, simpleStringTool] });
23
+ expect(await toolkit.list()).toHaveLength(2);
24
+ expect(toolkit.get("web-search")).toBe(mockHostedTool);
25
+ expect(toolkit.get("simple")).toBe(simpleStringTool);
26
+ });
27
+ });
28
+ describe("get", () => {
29
+ it("should return undefined for non-existent tool", () => {
30
+ const toolkit = new FunctionToolkit({ id: "test", tools: [] });
31
+ expect(toolkit.get("non-existent")).toBeUndefined();
32
+ });
33
+ it("should get systool by id", () => {
34
+ const toolkit = new FunctionToolkit({ id: "test", tools: [mockHostedTool] });
35
+ expect(toolkit.get("web-search")).toBe(mockHostedTool);
36
+ });
37
+ it("should get function tool by id", () => {
38
+ const toolkit = new FunctionToolkit({ id: "test", tools: [simpleStringTool] });
39
+ expect(toolkit.get("simple")).toBe(simpleStringTool);
40
+ });
41
+ });
42
+ describe("list", () => {
43
+ it("should return empty array for empty toolkit", async () => {
44
+ const toolkit = new FunctionToolkit({ id: "test", tools: [] });
45
+ expect(await toolkit.list()).toEqual([]);
46
+ });
47
+ it("should list all systools", async () => {
48
+ const toolkit = new FunctionToolkit({ id: "test", tools: [mockHostedTool, anotherHostedTool] });
49
+ const tools = await toolkit.list();
50
+ expect(tools).toHaveLength(2);
51
+ expect(tools).toContain(mockHostedTool);
52
+ expect(tools).toContain(anotherHostedTool);
53
+ });
54
+ it("should list all function tools", async () => {
55
+ const toolkit = new FunctionToolkit({
56
+ id: "test",
57
+ tools: [simpleStringTool, zodTool, errorTool],
58
+ });
59
+ const tools = await toolkit.list();
60
+ expect(tools).toHaveLength(3);
61
+ expect(tools).toContain(simpleStringTool);
62
+ expect(tools).toContain(zodTool);
63
+ expect(tools).toContain(errorTool);
64
+ });
65
+ it("should list both systools and function tools", async () => {
66
+ const toolkit = new FunctionToolkit({
67
+ id: "test",
68
+ tools: [mockHostedTool, simpleStringTool, zodTool],
69
+ });
70
+ const tools = await toolkit.list();
71
+ expect(tools).toHaveLength(3);
72
+ expect(tools).toContain(mockHostedTool);
73
+ expect(tools).toContain(simpleStringTool);
74
+ expect(tools).toContain(zodTool);
75
+ });
76
+ });
77
+ describe("serialize", () => {
78
+ it("should return empty array for empty toolkit", async () => {
79
+ const toolkit = new FunctionToolkit({ id: "test", tools: [] });
80
+ expect((await toolkit.list()).map((tool) => tool.serialize())).toEqual([]);
81
+ });
82
+ it("should serialize function tools correctly", async () => {
83
+ const toolkit = new FunctionToolkit({ id: "test", tools: [simpleStringTool] });
84
+ const serialized = (await toolkit.list()).map((tool) => tool.serialize());
85
+ expect(serialized).toHaveLength(1);
86
+ expect(serialized[0]).toEqual({
87
+ type: "function",
88
+ name: simpleStringTool.name,
89
+ description: simpleStringTool.description,
90
+ parameters: simpleStringTool.parameters,
91
+ });
92
+ });
93
+ it("should serialize hosted tools correctly", async () => {
94
+ const toolkit = new FunctionToolkit({ id: "test", tools: [mockHostedTool] });
95
+ const serialized = (await toolkit.list()).map((tool) => tool.serialize());
96
+ expect(serialized).toHaveLength(1);
97
+ expect(serialized[0]).toEqual({
98
+ type: "hosted-tool",
99
+ id: mockHostedTool.id,
100
+ name: mockHostedTool.name,
101
+ providerData: mockHostedTool.providerData,
102
+ });
103
+ });
104
+ it("should serialize both function and hosted tools", async () => {
105
+ const toolkit = new FunctionToolkit({ id: "test", tools: [mockHostedTool, simpleStringTool] });
106
+ const serialized = (await toolkit.list()).map((tool) => tool.serialize());
107
+ expect(serialized).toHaveLength(2);
108
+ // Check both tools are present (order not guaranteed with Map)
109
+ expect(serialized).toContainEqual({
110
+ type: "hosted-tool",
111
+ id: mockHostedTool.id,
112
+ name: mockHostedTool.name,
113
+ providerData: mockHostedTool.providerData,
114
+ });
115
+ expect(serialized).toContainEqual({
116
+ type: "function",
117
+ name: simpleStringTool.name,
118
+ description: simpleStringTool.description,
119
+ parameters: simpleStringTool.parameters,
120
+ });
121
+ });
122
+ it("should handle hosted tools without providerData", async () => {
123
+ const toolkit = new FunctionToolkit({ id: "test", tools: [anotherHostedTool] });
124
+ const serialized = (await toolkit.list()).map((tool) => tool.serialize());
125
+ expect(serialized).toHaveLength(1);
126
+ expect(serialized[0]).toEqual({
127
+ type: "hosted-tool",
128
+ id: anotherHostedTool.id,
129
+ name: anotherHostedTool.name,
130
+ providerData: undefined,
131
+ });
132
+ });
133
+ });
134
+ });
@@ -0,0 +1,4 @@
1
+ export { BaseTool, FunctionTool, HostedTool, tool } from "./tool";
2
+ export { Toolkit, FunctionToolkit, MCPToolkit } from "./toolkit";
3
+ export type { Tool, ToolResult, FunctionToolkitConfig, MCPToolkitConfig, ToolkitFilter, ToolkitFilterContext, } from "./types";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjE,YAAY,EACV,IAAI,EACJ,UAAU,EACV,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,oBAAoB,GACrB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { BaseTool, FunctionTool, HostedTool, tool } from "./tool";
2
+ export { Toolkit, FunctionToolkit, MCPToolkit } from "./toolkit";
@@ -0,0 +1,75 @@
1
+ import type { Context, UnknownContext } from "../context";
2
+ import { Toolkit } from "./toolkit";
3
+ import type { Tool } from ".";
4
+ import type { MCPToolkitConfig } from "./types";
5
+ /**
6
+ * A toolkit that wraps an MCP server and provides tools from it.
7
+ *
8
+ * Handles connection lifecycle automatically - connects lazily on first tool request
9
+ * and provides cleanup via destroy().
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const server = new MCPServerStdio({
14
+ * id: "github",
15
+ * command: "npx",
16
+ * args: ["-y", "@modelcontextprotocol/server-github"],
17
+ * env: {
18
+ * GITHUB_TOKEN: process.env.GITHUB_TOKEN,
19
+ * },
20
+ * });
21
+ *
22
+ * const github = new MCPToolkit({
23
+ * id: "github",
24
+ * server,
25
+ * filter: async (ctx, tool) => {
26
+ * // Only allow certain tools
27
+ * return !tool.id.startsWith("dangerous_");
28
+ * },
29
+ * });
30
+ *
31
+ * const agent = new Agent({
32
+ * toolkits: [github],
33
+ * });
34
+ * ```
35
+ */
36
+ export declare class MCPToolkit<TContext = UnknownContext> extends Toolkit<TContext> {
37
+ readonly id: string;
38
+ private server;
39
+ private cache;
40
+ private filter;
41
+ private connected;
42
+ private cached;
43
+ /**
44
+ * Create a new MCP toolkit.
45
+ *
46
+ * @param config Toolkit configuration with id and server instance
47
+ */
48
+ constructor(config: MCPToolkitConfig<TContext>);
49
+ /**
50
+ * Get a specific tool by ID.
51
+ *
52
+ * Returns the tool from the local cache. The cache is populated on the first
53
+ * call to list(). Returns undefined if list() hasn't been called yet.
54
+ *
55
+ * @param id The tool ID to look up
56
+ * @returns The tool if found in cache, undefined otherwise
57
+ */
58
+ get(id: string): Tool<TContext> | undefined;
59
+ /**
60
+ * List all tools available from the MCP server.
61
+ *
62
+ * Connects to the server lazily on first call. Tools are cached locally after
63
+ * the first fetch. The MCP server itself also handles caching via the
64
+ * cacheToolsList option, so the network call is only made once.
65
+ *
66
+ * @param context Optional context for filtering tools
67
+ * @returns Array of tools from the MCP server
68
+ */
69
+ list(context?: Context<TContext>): Promise<Tool<TContext>[]>;
70
+ /**
71
+ * Cleanup resources and close the MCP server connection.
72
+ */
73
+ destroy(): Promise<void>;
74
+ }
75
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/tool/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAOzD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;AAC9B,OAAO,KAAK,EAAE,gBAAgB,EAAiB,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,UAAU,CAAC,QAAQ,GAAG,cAAc,CAAE,SAAQ,OAAO,CAAC,QAAQ,CAAC;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,MAAM,CAA0B;IAExC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IAEvB;;;;OAIG;gBACS,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC;IAQ9C;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,SAAS;IAI3C;;;;;;;;;OASG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;IA+BlE;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ/B"}
@@ -0,0 +1,111 @@
1
+ import { mcpToFunctionTool } from "../mcp/utils";
2
+ import { filter } from "../lib/utils";
3
+ import { Toolkit } from "./toolkit";
4
+ /**
5
+ * A toolkit that wraps an MCP server and provides tools from it.
6
+ *
7
+ * Handles connection lifecycle automatically - connects lazily on first tool request
8
+ * and provides cleanup via destroy().
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const server = new MCPServerStdio({
13
+ * id: "github",
14
+ * command: "npx",
15
+ * args: ["-y", "@modelcontextprotocol/server-github"],
16
+ * env: {
17
+ * GITHUB_TOKEN: process.env.GITHUB_TOKEN,
18
+ * },
19
+ * });
20
+ *
21
+ * const github = new MCPToolkit({
22
+ * id: "github",
23
+ * server,
24
+ * filter: async (ctx, tool) => {
25
+ * // Only allow certain tools
26
+ * return !tool.id.startsWith("dangerous_");
27
+ * },
28
+ * });
29
+ *
30
+ * const agent = new Agent({
31
+ * toolkits: [github],
32
+ * });
33
+ * ```
34
+ */
35
+ export class MCPToolkit extends Toolkit {
36
+ id;
37
+ server;
38
+ cache;
39
+ filter;
40
+ connected = false;
41
+ cached = false;
42
+ /**
43
+ * Create a new MCP toolkit.
44
+ *
45
+ * @param config Toolkit configuration with id and server instance
46
+ */
47
+ constructor(config) {
48
+ super();
49
+ this.id = config.id;
50
+ this.server = config.server;
51
+ this.filter = config.filter ?? (() => true);
52
+ this.cache = new Map();
53
+ }
54
+ /**
55
+ * Get a specific tool by ID.
56
+ *
57
+ * Returns the tool from the local cache. The cache is populated on the first
58
+ * call to list(). Returns undefined if list() hasn't been called yet.
59
+ *
60
+ * @param id The tool ID to look up
61
+ * @returns The tool if found in cache, undefined otherwise
62
+ */
63
+ get(id) {
64
+ return this.cache.get(id);
65
+ }
66
+ /**
67
+ * List all tools available from the MCP server.
68
+ *
69
+ * Connects to the server lazily on first call. Tools are cached locally after
70
+ * the first fetch. The MCP server itself also handles caching via the
71
+ * cacheToolsList option, so the network call is only made once.
72
+ *
73
+ * @param context Optional context for filtering tools
74
+ * @returns Array of tools from the MCP server
75
+ */
76
+ async list(context) {
77
+ if (!this.connected) {
78
+ await this.server.connect();
79
+ this.connected = true;
80
+ }
81
+ // lazy cache population
82
+ if (!this.cached) {
83
+ const mcpTools = await this.server.listTools();
84
+ for (const mcpTool of mcpTools) {
85
+ const tool = mcpToFunctionTool(this.server, mcpTool);
86
+ this.cache.set(tool.id, tool);
87
+ }
88
+ this.cached = true;
89
+ }
90
+ const tools = Array.from(this.cache.values());
91
+ // apply filter
92
+ if (context && this.agent) {
93
+ const ctx = { context, agent: this.agent, toolkitId: this.id };
94
+ return filter(tools, async (tool) => {
95
+ return await this.filter(ctx, tool);
96
+ });
97
+ }
98
+ return tools;
99
+ }
100
+ /**
101
+ * Cleanup resources and close the MCP server connection.
102
+ */
103
+ async destroy() {
104
+ if (this.connected) {
105
+ await this.server.close();
106
+ this.connected = false;
107
+ this.cache.clear();
108
+ this.cached = false;
109
+ }
110
+ }
111
+ }