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,264 @@
1
+ import { Context, UnknownContext } from "@/context";
2
+
3
+ import { ModelBehaviorError } from "@/lib/error";
4
+ import { logger } from "@/lib/logger";
5
+ import { json } from "@kernl/shared/lib";
6
+ import { FAILED, COMPLETED, INTERRUPTIBLE } from "@kernl/protocol";
7
+ import type { LanguageModelTool } from "@kernl/protocol";
8
+
9
+ import type {
10
+ ToolType,
11
+ ToolConfig,
12
+ ToolApprovalFunction,
13
+ ToolEnabledFunction,
14
+ ToolEnabledPredicate,
15
+ ToolErrorFunction,
16
+ ToolExecuteArgument,
17
+ ToolExecuteFunction,
18
+ ToolInputParameters,
19
+ ToolResult,
20
+ } from "./types";
21
+
22
+ /**
23
+ * Exposes a function to the agent as a tool to be called
24
+ *
25
+ * @param config The options for the tool
26
+ * @returns A new tool instance
27
+ */
28
+ export function tool<
29
+ TContext = UnknownContext,
30
+ TParameters extends ToolInputParameters = undefined,
31
+ TResult = string,
32
+ >(
33
+ config: ToolConfig<TContext, TParameters, TResult>,
34
+ ): FunctionTool<TContext, TParameters, TResult> {
35
+ return new FunctionTool(config);
36
+ }
37
+
38
+ /**
39
+ * Base class for all tools (function and hosted)
40
+ */
41
+ export abstract class BaseTool<TContext = UnknownContext> {
42
+ abstract readonly type: "function" | "hosted-tool";
43
+ abstract readonly id: string;
44
+ abstract readonly name?: string;
45
+
46
+ /**
47
+ * The function to invoke when an error occurs while running the tool.
48
+ */
49
+ abstract errorfn: ToolErrorFunction | null;
50
+
51
+ /**
52
+ * Whether the tool requires human approval before it can be called.
53
+ */
54
+ abstract requiresApproval: ToolApprovalFunction<any>;
55
+
56
+ /**
57
+ * Determines whether the tool should be exposed to the model for the current run.
58
+ */
59
+ abstract isEnabled(context: Context<TContext>, agent: any): Promise<boolean>;
60
+
61
+ /**
62
+ * Serialize this tool for sending to the model
63
+ */
64
+ abstract serialize(): LanguageModelTool;
65
+ }
66
+
67
+ /**
68
+ * A function tool that can be used by agents.
69
+ */
70
+ export class FunctionTool<
71
+ TContext = UnknownContext,
72
+ TParameters extends ToolInputParameters = undefined,
73
+ TResult = unknown,
74
+ > extends BaseTool<TContext> {
75
+ readonly type = "function" as const;
76
+ readonly id: string;
77
+ readonly name?: string;
78
+ readonly description: string;
79
+ readonly parameters?: TParameters;
80
+ readonly mode: "blocking" | "async";
81
+ private execute: ToolExecuteFunction<TContext, TParameters, TResult>;
82
+
83
+ errorfn: ToolErrorFunction | null;
84
+ requiresApproval: ToolApprovalFunction<TParameters>;
85
+ isEnabled: ToolEnabledFunction<TContext>;
86
+
87
+ constructor(config: ToolConfig<TContext, TParameters, TResult>) {
88
+ super();
89
+ this.id = config.id;
90
+ this.name = config.name;
91
+ this.description = config.description;
92
+ this.parameters = config.parameters;
93
+ this.mode = config.mode ?? "blocking";
94
+ this.execute = config.execute;
95
+
96
+ // setup error function
97
+ this.errorfn =
98
+ typeof config.errorfn === "undefined"
99
+ ? defaultToolErrorFunction
100
+ : config.errorfn;
101
+
102
+ // setup approval function
103
+ this.requiresApproval =
104
+ typeof config.requiresApproval === "function"
105
+ ? config.requiresApproval
106
+ : async () =>
107
+ typeof config.requiresApproval === "boolean"
108
+ ? config.requiresApproval
109
+ : false;
110
+
111
+ // setup enabled function
112
+ this.isEnabled =
113
+ typeof config.isEnabled === "function"
114
+ ? async (context, agent) => {
115
+ const predicate =
116
+ config.isEnabled as ToolEnabledPredicate<TContext>;
117
+ const result = await predicate({ context, agent });
118
+ return Boolean(result);
119
+ }
120
+ : async () =>
121
+ typeof config.isEnabled === "boolean" ? config.isEnabled : true;
122
+ }
123
+
124
+ /**
125
+ * Main invocation method -
126
+ *
127
+ * Wraps execute with parsing, approval, and error handling
128
+ */
129
+ async invoke(
130
+ context: Context<TContext>,
131
+ args: string,
132
+ callId?: string,
133
+ ): Promise<ToolResult<TResult>> {
134
+ return this._invoke(context, args, callId).catch((error) => {
135
+ const msg = this.errorfn
136
+ ? this.errorfn(context, error)
137
+ : error instanceof Error
138
+ ? error.message
139
+ : String(error);
140
+
141
+ return {
142
+ state: FAILED,
143
+ result: undefined,
144
+ error: msg,
145
+ };
146
+ });
147
+ }
148
+
149
+ /**
150
+ * Executes the tool with the provided execute() function
151
+ */
152
+ private async _invoke(
153
+ context: Context<TContext>,
154
+ args: string,
155
+ callId?: string,
156
+ ): Promise<ToolResult<TResult>> {
157
+ let parsed = args as ToolExecuteArgument<TParameters>;
158
+
159
+ if (this.parameters) {
160
+ try {
161
+ parsed = json(this.parameters).decode(
162
+ args,
163
+ ) as ToolExecuteArgument<TParameters>;
164
+ } catch (error) {
165
+ logger.debug(`Invalid JSON input for tool ${this.id}: ${args}`);
166
+ throw new ModelBehaviorError("Invalid JSON input for tool");
167
+ }
168
+ }
169
+
170
+ // Check if approval is required
171
+ const needsApproval = await this.requiresApproval(context, parsed, callId);
172
+ const approvalStatus = callId ? context.approvals.get(callId) : undefined;
173
+
174
+ if (needsApproval && approvalStatus !== "approved") {
175
+ return {
176
+ state: INTERRUPTIBLE,
177
+ result: undefined,
178
+ error: null,
179
+ };
180
+ }
181
+
182
+ const result = await this.execute(context, parsed);
183
+ return {
184
+ state: COMPLETED,
185
+ result: result,
186
+ error: null,
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Serialize this function tool for sending to the model
192
+ */
193
+ serialize(): LanguageModelTool {
194
+ return {
195
+ kind: "function",
196
+ name: this.id,
197
+ description: this.description,
198
+ parameters: this.parameters as any, // TODO: convert Zod to JSON Schema
199
+ };
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Hosted tool executed server-side by the model provider
205
+ */
206
+ export class HostedTool extends BaseTool {
207
+ readonly type = "hosted-tool" as const;
208
+ readonly id: string;
209
+ readonly name?: string;
210
+ readonly providerData?: Record<string, any>;
211
+
212
+ /**
213
+ * Hosted tools use the default error function
214
+ */
215
+ errorfn: ToolErrorFunction | null = defaultToolErrorFunction;
216
+
217
+ /**
218
+ * Hosted tools do not require approval by default
219
+ */
220
+ requiresApproval: ToolApprovalFunction<any> = async () => false;
221
+
222
+ constructor(config: {
223
+ id: string;
224
+ name?: string;
225
+ providerData?: Record<string, any>;
226
+ }) {
227
+ super();
228
+ this.id = config.id;
229
+ this.name = config.name;
230
+ this.providerData = config.providerData;
231
+ }
232
+
233
+ /**
234
+ * Hosted tools are always enabled
235
+ */
236
+ async isEnabled(): Promise<boolean> {
237
+ return true;
238
+ }
239
+
240
+ /**
241
+ * Serialize this hosted tool for sending to the model
242
+ */
243
+ serialize(): LanguageModelTool {
244
+ return {
245
+ kind: "provider-defined",
246
+ id: this.id as `${string}.${string}`,
247
+ name: this.name || this.id,
248
+ args: this.providerData || {},
249
+ };
250
+ }
251
+ }
252
+
253
+ /**
254
+ * The default function to invoke when an error occurs while running the tool.
255
+ *
256
+ * Always returns `An error occurred while running the tool. Please try again. Error: <error details>`
257
+ *
258
+ * @param context - An instance of the current Context
259
+ * @param error - The error that occurred
260
+ */
261
+ function defaultToolErrorFunction(context: Context, error: Error | unknown) {
262
+ const details = error instanceof Error ? error.toString() : String(error);
263
+ return `An error occurred while running the tool. Please try again. Error: ${details}`;
264
+ }
@@ -0,0 +1,234 @@
1
+ import type { Agent } from "@/agent";
2
+ import type { Context, UnknownContext } from "@/context";
3
+
4
+ import { MCPServer } from "@/mcp/base";
5
+ import { mcpToFunctionTool } from "@/mcp/utils";
6
+ import { filter } from "@kernl/shared/lib";
7
+
8
+ import type { Tool } from ".";
9
+ import type {
10
+ FunctionToolkitConfig,
11
+ MCPToolkitConfig,
12
+ ToolkitFilter,
13
+ } from "./types";
14
+
15
+ /**
16
+ * A toolkit is a collection of related tools that can be used by an agent.
17
+ *
18
+ * Toolkits can be static (FunctionToolkit) or dynamic (MCPToolkit), and provide
19
+ * a unified interface for tool discovery and management.
20
+ */
21
+ export abstract class Toolkit<TContext = UnknownContext> {
22
+ /**
23
+ * Unique identifier for this toolkit
24
+ */
25
+ abstract readonly id: string;
26
+
27
+ /**
28
+ * The agent this toolkit is bound to (if any)
29
+ */
30
+ protected agent?: Agent<TContext, any>;
31
+
32
+ /**
33
+ * Bind this toolkit to an agent.
34
+ * Called by Agent constructor.
35
+ */
36
+ bind(agent: Agent<TContext, any>): void {
37
+ this.agent = agent;
38
+ }
39
+
40
+ /**
41
+ * Get a specific tool by its ID.
42
+ *
43
+ * @param id The tool ID to look up
44
+ * @returns The tool if found, undefined otherwise
45
+ */
46
+ abstract get(id: string): Tool<TContext> | undefined;
47
+
48
+ /**
49
+ * List all tools available for the given context.
50
+ * If no context provided, returns all tools without filtering.
51
+ *
52
+ * @param context Optional context for filtering tools
53
+ * @returns Array of tools available in this toolkit
54
+ */
55
+ abstract list(context?: Context<TContext>): Promise<Tool<TContext>[]>;
56
+
57
+ /**
58
+ * Cleanup resources held by this toolkit.
59
+ * Override if your toolkit needs cleanup (e.g., closing connections).
60
+ * Default implementation does nothing.
61
+ */
62
+ async destroy(): Promise<void> {
63
+ // Default: no-op
64
+ }
65
+ }
66
+
67
+ /**
68
+ * A toolkit containing static function tools.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const fs = new FunctionToolkit({
73
+ * id: "fs",
74
+ * tools: [readFile, writeFile, listDir, ...]
75
+ * });
76
+ * ```
77
+ */
78
+ export class FunctionToolkit<
79
+ TContext = UnknownContext,
80
+ > extends Toolkit<TContext> {
81
+ readonly id: string;
82
+ private tools: Map<string, Tool<TContext>>;
83
+
84
+ /**
85
+ * Create a new function toolkit.
86
+ *
87
+ * @param config Toolkit configuration with id and tools array
88
+ */
89
+ constructor(config: FunctionToolkitConfig<TContext>) {
90
+ super();
91
+ this.id = config.id;
92
+ this.tools = new Map(config.tools.map((t) => [t.id, t]));
93
+ }
94
+
95
+ /**
96
+ * Get a specific tool by ID.
97
+ *
98
+ * @param id The tool ID to look up
99
+ * @returns The tool if found, undefined otherwise
100
+ */
101
+ get(id: string): Tool<TContext> | undefined {
102
+ return this.tools.get(id);
103
+ }
104
+
105
+ /**
106
+ * List all tools in this toolkit.
107
+ *
108
+ * @param context Optional context for filtering tools (currently unused)
109
+ * @returns Array of all tools in this toolkit
110
+ */
111
+ async list(context?: Context<TContext>): Promise<Tool<TContext>[]> {
112
+ return Array.from(this.tools.values());
113
+ }
114
+ }
115
+
116
+ /*
117
+ * A toolkit that wraps an MCP server and provides tools from it.
118
+ *
119
+ * Handles connection lifecycle automatically - connects lazily on first tool request
120
+ * and provides cleanup via destroy().
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * const server = new MCPServerStdio({
125
+ * id: "github",
126
+ * command: "npx",
127
+ * args: ["-y", "@modelcontextprotocol/server-github"],
128
+ * env: {
129
+ * GITHUB_TOKEN: process.env.GITHUB_TOKEN,
130
+ * },
131
+ * });
132
+ *
133
+ * const github = new MCPToolkit({
134
+ * id: "github",
135
+ * server,
136
+ * filter: async (ctx, tool) => {
137
+ * // Only allow certain tools
138
+ * return !tool.id.startsWith("dangerous_");
139
+ * },
140
+ * });
141
+ *
142
+ * const agent = new Agent({
143
+ * toolkits: [github],
144
+ * });
145
+ * ```
146
+ */
147
+ export class MCPToolkit<TContext = UnknownContext> extends Toolkit<TContext> {
148
+ readonly id: string;
149
+ private server: MCPServer;
150
+ private cache: Map<string, Tool<TContext>>;
151
+ private filter: ToolkitFilter<TContext>;
152
+
153
+ private connected = false;
154
+ private cached = false;
155
+
156
+ /**
157
+ * Create a new MCP toolkit.
158
+ *
159
+ * @param config Toolkit configuration with id and server instance
160
+ */
161
+ constructor(config: MCPToolkitConfig<TContext>) {
162
+ super();
163
+ this.id = config.id;
164
+ this.server = config.server;
165
+ this.filter = config.filter ?? (() => true);
166
+ this.cache = new Map();
167
+ }
168
+
169
+ /**
170
+ * Get a specific tool by ID.
171
+ *
172
+ * Returns the tool from the local cache. The cache is populated on the first
173
+ * call to list(). Returns undefined if list() hasn't been called yet.
174
+ *
175
+ * @param id The tool ID to look up
176
+ * @returns The tool if found in cache, undefined otherwise
177
+ */
178
+ get(id: string): Tool<TContext> | undefined {
179
+ return this.cache.get(id);
180
+ }
181
+
182
+ /**
183
+ * List all tools available from the MCP server.
184
+ *
185
+ * Connects to the server lazily on first call. Tools are cached locally after
186
+ * the first fetch. The MCP server itself also handles caching via the
187
+ * cacheToolsList option, so the network call is only made once.
188
+ *
189
+ * @param context Optional context for filtering tools
190
+ * @returns Array of tools from the MCP server
191
+ */
192
+ async list(context?: Context<TContext>): Promise<Tool<TContext>[]> {
193
+ if (!this.connected) {
194
+ await this.server.connect();
195
+ this.connected = true;
196
+ }
197
+
198
+ // lazy cache population
199
+ if (!this.cached) {
200
+ const mcpTools = await this.server.listTools();
201
+
202
+ for (const mcpTool of mcpTools) {
203
+ const tool = mcpToFunctionTool(this.server, mcpTool);
204
+ this.cache.set(tool.id, tool);
205
+ }
206
+
207
+ this.cached = true;
208
+ }
209
+
210
+ const tools = Array.from(this.cache.values());
211
+
212
+ // apply filter
213
+ if (context && this.agent) {
214
+ const ctx = { context, agent: this.agent, toolkitId: this.id };
215
+ return filter(tools, async (tool) => {
216
+ return await this.filter(ctx, tool);
217
+ });
218
+ }
219
+
220
+ return tools;
221
+ }
222
+
223
+ /**
224
+ * Cleanup resources and close the MCP server connection.
225
+ */
226
+ async destroy(): Promise<void> {
227
+ if (this.connected) {
228
+ await this.server.close();
229
+ this.connected = false;
230
+ this.cache.clear();
231
+ this.cached = false;
232
+ }
233
+ }
234
+ }