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,29 @@
1
+ import { EventEmitter } from "node:events";
2
+ /**
3
+ * Generic typed event emitter that wraps Node's EventEmitter with type safety
4
+ */
5
+ class TypedEventEmitter extends EventEmitter {
6
+ on(event, listener) {
7
+ return super.on(event, listener);
8
+ }
9
+ off(event, listener) {
10
+ return super.off(event, listener);
11
+ }
12
+ emit(event, ...args) {
13
+ return super.emit(event, ...args);
14
+ }
15
+ once(event, listener) {
16
+ return super.once(event, listener);
17
+ }
18
+ }
19
+ /**
20
+ * Event emitter that every Agent instance inherits from and that emits events for the lifecycle
21
+ * of the agent.
22
+ */
23
+ export class AgentHooks extends TypedEventEmitter {
24
+ }
25
+ /**
26
+ * Event emitter that the kernl uses to emit events for the lifecycle of every agent run.
27
+ */
28
+ export class KernlHooks extends TypedEventEmitter {
29
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A logger instance with namespace support and sensitive data flags.
3
+ */
4
+ export type Logger = {
5
+ /**
6
+ * The namespace used for the logger.
7
+ */
8
+ namespace: string;
9
+ trace: (message: any, ...args: any[]) => void;
10
+ debug: (message: any, ...args: any[]) => void;
11
+ info: (message: any, ...args: any[]) => void;
12
+ warn: (message: any, ...args: any[]) => void;
13
+ error: (message: any, ...args: any[]) => void;
14
+ fatal: (message: any, ...args: any[]) => void;
15
+ /**
16
+ * Whether to log model data.
17
+ */
18
+ dontLogModelData: boolean;
19
+ /**
20
+ * Whether to log tool data.
21
+ */
22
+ dontLogToolData: boolean;
23
+ };
24
+ /**
25
+ * Get a logger for a given namespace.
26
+ *
27
+ * @param namespace - the namespace to use for the logger (e.g., 'kernl:core', 'kernl:agent').
28
+ * @returns A logger object with all pino log levels and sensitive data flags.
29
+ */
30
+ export declare function getLogger(namespace?: string): Logger;
31
+ /**
32
+ * Default logger instance for the core library.
33
+ */
34
+ export declare const logger: Logger;
35
+ export default logger;
36
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAsBA;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC9C,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC9C,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC7C,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC9C,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAE9C;;OAEG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,SAAS,GAAE,MAAgB,GAAG,MAAM,CAc7D;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,QAA0B,CAAC;AAE9C,eAAe,MAAM,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,43 @@
1
+ import pino from "pino";
2
+ import { env } from "./env";
3
+ /**
4
+ * By default we don't log LLM inputs/outputs, to prevent exposing sensitive data.
5
+ * Set KERNL_LOG_MODEL_DATA=true to enable.
6
+ */
7
+ const dontLogModelData = !env.KERNL_LOG_MODEL_DATA;
8
+ /**
9
+ * By default we don't log tool inputs/outputs, to prevent exposing sensitive data.
10
+ * Set KERNL_LOG_TOOL_DATA=true to enable.
11
+ */
12
+ const dontLogToolData = !env.KERNL_LOG_TOOL_DATA;
13
+ /**
14
+ * Base pino logger instance
15
+ */
16
+ const base = pino({
17
+ level: env.LOG_LEVEL,
18
+ });
19
+ /**
20
+ * Get a logger for a given namespace.
21
+ *
22
+ * @param namespace - the namespace to use for the logger (e.g., 'kernl:core', 'kernl:agent').
23
+ * @returns A logger object with all pino log levels and sensitive data flags.
24
+ */
25
+ export function getLogger(namespace = "kernl") {
26
+ const child = base.child({ namespace });
27
+ return {
28
+ namespace,
29
+ trace: child.trace.bind(child),
30
+ debug: child.debug.bind(child),
31
+ info: child.info.bind(child),
32
+ warn: child.warn.bind(child),
33
+ error: child.error.bind(child),
34
+ fatal: child.fatal.bind(child),
35
+ dontLogModelData,
36
+ dontLogToolData,
37
+ };
38
+ }
39
+ /**
40
+ * Default logger instance for the core library.
41
+ */
42
+ export const logger = getLogger("kernl:core");
43
+ export default logger;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=base.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.test.d.ts","sourceRoot":"","sources":["../../../src/mcp/__tests__/base.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,268 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { BaseMCPServer } from "../base";
3
+ import { logger } from "../../lib/logger";
4
+ // Create a minimal concrete implementation for testing
5
+ class TestMCPServer extends BaseMCPServer {
6
+ id;
7
+ mockTools = [];
8
+ isConnected = false;
9
+ constructor(id, options) {
10
+ super({
11
+ cacheToolsList: options?.cacheToolsList,
12
+ toolFilter: options?.toolFilter,
13
+ logger: logger,
14
+ });
15
+ this.id = id;
16
+ }
17
+ // Method to set mock tools for testing
18
+ setMockTools(tools) {
19
+ this.mockTools = tools;
20
+ }
21
+ async connect() {
22
+ this.isConnected = true;
23
+ }
24
+ async close() {
25
+ this.isConnected = false;
26
+ }
27
+ async _listTools() {
28
+ if (!this.isConnected) {
29
+ throw new Error("Not connected");
30
+ }
31
+ return this.mockTools;
32
+ }
33
+ async callTool(toolName, args) {
34
+ if (!this.isConnected) {
35
+ throw new Error("Not connected");
36
+ }
37
+ return [{ type: "text", text: `Called ${toolName}` }];
38
+ }
39
+ }
40
+ describe("BaseMCPServer", () => {
41
+ describe("Caching mechanisms", () => {
42
+ it("should have cache disabled by default", () => {
43
+ const server = new TestMCPServer("test-server");
44
+ expect(server.cacheToolsList).toBe(false);
45
+ });
46
+ it("should store tools correctly when cache enabled", async () => {
47
+ const server = new TestMCPServer("test-server", {
48
+ cacheToolsList: true,
49
+ });
50
+ const mockTools = [
51
+ {
52
+ name: "tool1",
53
+ description: "First tool",
54
+ inputSchema: { type: "object", properties: {} },
55
+ },
56
+ {
57
+ name: "tool2",
58
+ description: "Second tool",
59
+ inputSchema: { type: "object", properties: {} },
60
+ },
61
+ ];
62
+ server.setMockTools(mockTools);
63
+ await server.connect();
64
+ const tools = await server.listTools();
65
+ expect(tools).toEqual(mockTools);
66
+ expect(tools).toHaveLength(2);
67
+ });
68
+ it("should return same reference on subsequent calls when cached", async () => {
69
+ const server = new TestMCPServer("test-server", {
70
+ cacheToolsList: true,
71
+ });
72
+ const mockTools = [
73
+ {
74
+ name: "tool1",
75
+ inputSchema: { type: "object", properties: {} },
76
+ },
77
+ ];
78
+ server.setMockTools(mockTools);
79
+ await server.connect();
80
+ const tools1 = await server.listTools();
81
+ const tools2 = await server.listTools();
82
+ // Should return the exact same cached reference
83
+ expect(tools1).toBe(tools2);
84
+ });
85
+ it("should fetch fresh tools when cache disabled", async () => {
86
+ const server = new TestMCPServer("test-server", {
87
+ cacheToolsList: false,
88
+ });
89
+ const mockTools = [
90
+ {
91
+ name: "tool1",
92
+ inputSchema: { type: "object", properties: {} },
93
+ },
94
+ ];
95
+ server.setMockTools(mockTools);
96
+ await server.connect();
97
+ const tools1 = await server.listTools();
98
+ const tools2 = await server.listTools();
99
+ // Both should succeed and have correct data
100
+ expect(tools1).toEqual(mockTools);
101
+ expect(tools2).toEqual(mockTools);
102
+ });
103
+ it("should have cache isolated per server instance", async () => {
104
+ const server1 = new TestMCPServer("server1", {
105
+ cacheToolsList: true,
106
+ });
107
+ const server2 = new TestMCPServer("server2", {
108
+ cacheToolsList: true,
109
+ });
110
+ const tools1 = [
111
+ {
112
+ name: "tool1",
113
+ inputSchema: { type: "object", properties: {} },
114
+ },
115
+ ];
116
+ const tools2 = [
117
+ {
118
+ name: "tool2",
119
+ inputSchema: { type: "object", properties: {} },
120
+ },
121
+ ];
122
+ server1.setMockTools(tools1);
123
+ server2.setMockTools(tools2);
124
+ await server1.connect();
125
+ await server2.connect();
126
+ const result1 = await server1.listTools();
127
+ const result2 = await server2.listTools();
128
+ expect(result1).toEqual(tools1);
129
+ expect(result2).toEqual(tools2);
130
+ expect(result1).not.toEqual(result2);
131
+ });
132
+ it("should clear cache when invalidateCache() called", async () => {
133
+ const server = new TestMCPServer("test-server", {
134
+ cacheToolsList: true,
135
+ });
136
+ const initialTools = [
137
+ {
138
+ name: "tool1",
139
+ inputSchema: { type: "object", properties: {} },
140
+ },
141
+ ];
142
+ server.setMockTools(initialTools);
143
+ await server.connect();
144
+ // First fetch - populates cache
145
+ await server.listTools();
146
+ // Invalidate cache
147
+ await server.invalidateCache();
148
+ // Update mock tools
149
+ const newTools = [
150
+ {
151
+ name: "tool2",
152
+ inputSchema: { type: "object", properties: {} },
153
+ },
154
+ ];
155
+ server.setMockTools(newTools);
156
+ // Next fetch should get new tools, not cached ones
157
+ const result = await server.listTools();
158
+ expect(result).toEqual(newTools);
159
+ });
160
+ it("should set _cacheDirty flag when invalidateCache() called", async () => {
161
+ const server = new TestMCPServer("test-server", {
162
+ cacheToolsList: true,
163
+ });
164
+ await server.connect();
165
+ server.setMockTools([
166
+ { name: "tool1", inputSchema: { type: "object", properties: {} } },
167
+ ]);
168
+ // Populate cache
169
+ await server.listTools();
170
+ // Invalidate
171
+ await server.invalidateCache();
172
+ // The next listTools() call should fetch fresh data
173
+ // We can verify this by changing the mock tools and seeing the change
174
+ server.setMockTools([
175
+ { name: "tool2", inputSchema: { type: "object", properties: {} } },
176
+ ]);
177
+ const tools = await server.listTools();
178
+ expect(tools[0].name).toBe("tool2");
179
+ });
180
+ it("should skip cache when _cacheDirty is true", async () => {
181
+ const server = new TestMCPServer("test-server", {
182
+ cacheToolsList: true,
183
+ });
184
+ await server.connect();
185
+ // First call
186
+ server.setMockTools([
187
+ { name: "tool1", inputSchema: { type: "object", properties: {} } },
188
+ ]);
189
+ await server.listTools();
190
+ // Invalidate cache (sets _cacheDirty = true)
191
+ await server.invalidateCache();
192
+ // Change mock tools
193
+ server.setMockTools([
194
+ { name: "tool2", inputSchema: { type: "object", properties: {} } },
195
+ ]);
196
+ // Should fetch fresh tools, not use cache
197
+ const tools = await server.listTools();
198
+ expect(tools[0].name).toBe("tool2");
199
+ });
200
+ });
201
+ describe("Abstract class contracts", () => {
202
+ it("should allow subclass to implement connect()", async () => {
203
+ const server = new TestMCPServer("test-server");
204
+ await expect(server.connect()).resolves.not.toThrow();
205
+ });
206
+ it("should allow subclass to implement close()", async () => {
207
+ const server = new TestMCPServer("test-server");
208
+ await server.connect();
209
+ await expect(server.close()).resolves.not.toThrow();
210
+ });
211
+ it("should allow subclass to implement _listTools()", async () => {
212
+ const server = new TestMCPServer("test-server");
213
+ server.setMockTools([
214
+ { name: "tool1", inputSchema: { type: "object", properties: {} } },
215
+ ]);
216
+ await server.connect();
217
+ const tools = await server.listTools();
218
+ expect(tools).toHaveLength(1);
219
+ expect(tools[0].name).toBe("tool1");
220
+ });
221
+ it("should allow subclass to implement callTool()", async () => {
222
+ const server = new TestMCPServer("test-server");
223
+ await server.connect();
224
+ const result = await server.callTool("test_tool", { arg: "value" });
225
+ expect(result).toHaveLength(1);
226
+ expect(result[0].text).toContain("test_tool");
227
+ });
228
+ });
229
+ describe("toolFilter integration", () => {
230
+ it("should default to allow-all filter", async () => {
231
+ const server = new TestMCPServer("test-server");
232
+ // The default filter should allow all tools
233
+ expect(server.toolFilter).toBeDefined();
234
+ // Test that it returns true for any tool
235
+ const mockContext = {
236
+ context: {},
237
+ agent: {},
238
+ serverId: "test",
239
+ };
240
+ const mockTool = {
241
+ name: "any_tool",
242
+ inputSchema: { type: "object", properties: {} },
243
+ };
244
+ const result = await server.toolFilter(mockContext, mockTool);
245
+ expect(result).toBe(true);
246
+ });
247
+ it("should store custom filter correctly in constructor", async () => {
248
+ const customFilter = vi.fn().mockResolvedValue(false);
249
+ const server = new TestMCPServer("test-server", {
250
+ toolFilter: customFilter,
251
+ });
252
+ expect(server.toolFilter).toBe(customFilter);
253
+ // Verify the filter is actually used
254
+ const mockContext = {
255
+ context: {},
256
+ agent: {},
257
+ serverId: "test",
258
+ };
259
+ const mockTool = {
260
+ name: "tool1",
261
+ inputSchema: { type: "object", properties: {} },
262
+ };
263
+ const result = await server.toolFilter(mockContext, mockTool);
264
+ expect(result).toBe(false);
265
+ expect(customFilter).toHaveBeenCalledWith(mockContext, mockTool);
266
+ });
267
+ });
268
+ });
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=echo-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"echo-server.d.ts","sourceRoot":"","sources":["../../../../src/mcp/__tests__/fixtures/echo-server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Simple echo/string manipulation server for testing MCP stdio transport.
4
+ *
5
+ * Provides deterministic string transformations.
6
+ */
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ const server = new Server({
10
+ name: "echo-server",
11
+ version: "1.0.0",
12
+ }, {
13
+ capabilities: {
14
+ tools: {},
15
+ },
16
+ });
17
+ // List available tools
18
+ server.setRequestHandler("tools/list", async () => ({
19
+ tools: [
20
+ {
21
+ name: "echo",
22
+ description: "Echo back the input text",
23
+ inputSchema: {
24
+ type: "object",
25
+ properties: {
26
+ text: { type: "string", description: "Text to echo" },
27
+ },
28
+ required: ["text"],
29
+ },
30
+ },
31
+ {
32
+ name: "uppercase",
33
+ description: "Convert text to uppercase",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ text: { type: "string", description: "Text to convert" },
38
+ },
39
+ required: ["text"],
40
+ },
41
+ },
42
+ {
43
+ name: "reverse",
44
+ description: "Reverse the text",
45
+ inputSchema: {
46
+ type: "object",
47
+ properties: {
48
+ text: { type: "string", description: "Text to reverse" },
49
+ },
50
+ required: ["text"],
51
+ },
52
+ },
53
+ ],
54
+ }));
55
+ // Handle tool calls
56
+ server.setRequestHandler("tools/call", async (request) => {
57
+ const { name, arguments: args } = request.params;
58
+ switch (name) {
59
+ case "echo":
60
+ return {
61
+ content: [
62
+ {
63
+ type: "text",
64
+ text: args.text,
65
+ },
66
+ ],
67
+ };
68
+ case "uppercase":
69
+ return {
70
+ content: [
71
+ {
72
+ type: "text",
73
+ text: args.text.toUpperCase(),
74
+ },
75
+ ],
76
+ };
77
+ case "reverse":
78
+ return {
79
+ content: [
80
+ {
81
+ type: "text",
82
+ text: args.text.split("").reverse().join(""),
83
+ },
84
+ ],
85
+ };
86
+ default:
87
+ throw new Error(`Unknown tool: ${name}`);
88
+ }
89
+ });
90
+ // Start the server
91
+ const transport = new StdioServerTransport();
92
+ await server.connect(transport);
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=math-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math-server.d.ts","sourceRoot":"","sources":["../../../../src/mcp/__tests__/fixtures/math-server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Simple deterministic math server for testing MCP stdio transport.
4
+ *
5
+ * Provides basic arithmetic operations with predictable results.
6
+ */
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ const server = new Server({
10
+ name: "math-server",
11
+ version: "1.0.0",
12
+ }, {
13
+ capabilities: {
14
+ tools: {},
15
+ },
16
+ });
17
+ // List available tools
18
+ server.setRequestHandler("tools/list", async () => ({
19
+ tools: [
20
+ {
21
+ name: "add",
22
+ description: "Add two numbers",
23
+ inputSchema: {
24
+ type: "object",
25
+ properties: {
26
+ a: { type: "number", description: "First number" },
27
+ b: { type: "number", description: "Second number" },
28
+ },
29
+ required: ["a", "b"],
30
+ },
31
+ },
32
+ {
33
+ name: "multiply",
34
+ description: "Multiply two numbers",
35
+ inputSchema: {
36
+ type: "object",
37
+ properties: {
38
+ a: { type: "number", description: "First number" },
39
+ b: { type: "number", description: "Second number" },
40
+ },
41
+ required: ["a", "b"],
42
+ },
43
+ },
44
+ {
45
+ name: "divide",
46
+ description: "Divide two numbers",
47
+ inputSchema: {
48
+ type: "object",
49
+ properties: {
50
+ a: { type: "number", description: "Numerator" },
51
+ b: { type: "number", description: "Denominator" },
52
+ },
53
+ required: ["a", "b"],
54
+ },
55
+ },
56
+ ],
57
+ }));
58
+ // Handle tool calls
59
+ server.setRequestHandler("tools/call", async (request) => {
60
+ const { name, arguments: args } = request.params;
61
+ switch (name) {
62
+ case "add":
63
+ return {
64
+ content: [
65
+ {
66
+ type: "text",
67
+ text: String(args.a + args.b),
68
+ },
69
+ ],
70
+ };
71
+ case "multiply":
72
+ return {
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: String(args.a * args.b),
77
+ },
78
+ ],
79
+ };
80
+ case "divide":
81
+ if (args.b === 0) {
82
+ throw new Error("Division by zero");
83
+ }
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: String(args.a / args.b),
89
+ },
90
+ ],
91
+ };
92
+ default:
93
+ throw new Error(`Unknown tool: ${name}`);
94
+ }
95
+ });
96
+ // Start the server
97
+ const transport = new StdioServerTransport();
98
+ await server.connect(transport);
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../src/mcp/__tests__/fixtures/server.ts"],"names":[],"mappings":""}