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.
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +53 -0
- package/LICENSE +201 -0
- package/dist/agent.d.ts +43 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +130 -0
- package/dist/context.d.ts +70 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +111 -0
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +31 -0
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +1 -0
- package/dist/guardrail.d.ts +178 -0
- package/dist/guardrail.d.ts.map +1 -0
- package/dist/guardrail.js +34 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/kernel.d.ts +7 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +7 -0
- package/dist/kernl.d.ts +18 -0
- package/dist/kernl.d.ts.map +1 -0
- package/dist/kernl.js +16 -0
- package/dist/lib/env.d.ts +43 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +29 -0
- package/dist/lib/error.d.ts +88 -0
- package/dist/lib/error.d.ts.map +1 -0
- package/dist/lib/error.js +117 -0
- package/dist/lib/logger.d.ts +36 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +43 -0
- package/dist/lib/serde/__tests__/codec.test.d.ts +2 -0
- package/dist/lib/serde/__tests__/codec.test.d.ts.map +1 -0
- package/dist/lib/serde/__tests__/codec.test.js +75 -0
- package/dist/lib/serde/codec.d.ts +12 -0
- package/dist/lib/serde/codec.d.ts.map +1 -0
- package/dist/lib/serde/codec.js +54 -0
- package/dist/lib/serde/json.d.ts +8 -0
- package/dist/lib/serde/json.d.ts.map +1 -0
- package/dist/lib/serde/json.js +13 -0
- package/dist/lib/serde/thread.d.ts +1 -0
- package/dist/lib/serde/thread.d.ts.map +1 -0
- package/dist/lib/serde/thread.js +172 -0
- package/dist/lib/serde/tool.d.ts +36 -0
- package/dist/lib/serde/tool.d.ts.map +1 -0
- package/dist/lib/serde/tool.js +1 -0
- package/dist/lib/utils.d.ts +19 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +41 -0
- package/dist/lifecycle.d.ts +133 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +29 -0
- package/dist/logger.d.ts +36 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +43 -0
- package/dist/mcp/__tests__/base.test.d.ts +2 -0
- package/dist/mcp/__tests__/base.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/base.test.js +268 -0
- package/dist/mcp/__tests__/fixtures/echo-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/echo-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/echo-server.js +92 -0
- package/dist/mcp/__tests__/fixtures/math-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/math-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/math-server.js +98 -0
- package/dist/mcp/__tests__/fixtures/server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/server.js +162 -0
- package/dist/mcp/__tests__/fixtures/test-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/test-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/test-server.js +163 -0
- package/dist/mcp/__tests__/fixtures/utils.d.ts +17 -0
- package/dist/mcp/__tests__/fixtures/utils.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/utils.js +42 -0
- package/dist/mcp/__tests__/integration.test.d.ts +2 -0
- package/dist/mcp/__tests__/integration.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/integration.test.js +360 -0
- package/dist/mcp/__tests__/stdio.test.d.ts +2 -0
- package/dist/mcp/__tests__/stdio.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/stdio.test.js +180 -0
- package/dist/mcp/__tests__/test-utils.d.ts +17 -0
- package/dist/mcp/__tests__/test-utils.d.ts.map +1 -0
- package/dist/mcp/__tests__/test-utils.js +42 -0
- package/dist/mcp/__tests__/utils.test.d.ts +2 -0
- package/dist/mcp/__tests__/utils.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/utils.test.js +300 -0
- package/dist/mcp/base.d.ts +88 -0
- package/dist/mcp/base.d.ts.map +1 -0
- package/dist/mcp/base.js +68 -0
- package/dist/mcp/http.d.ts +34 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +100 -0
- package/dist/mcp/node.d.ts +60 -0
- package/dist/mcp/node.d.ts.map +1 -0
- package/dist/mcp/node.js +297 -0
- package/dist/mcp/sse.d.ts +34 -0
- package/dist/mcp/sse.d.ts.map +1 -0
- package/dist/mcp/sse.js +97 -0
- package/dist/mcp/stdio.d.ts +32 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +96 -0
- package/dist/mcp/types.d.ts +172 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +16 -0
- package/dist/mcp/utils.d.ts +23 -0
- package/dist/mcp/utils.d.ts.map +1 -0
- package/dist/mcp/utils.js +44 -0
- package/dist/model.d.ts +175 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +1 -0
- package/dist/providers/ai.d.ts +1 -0
- package/dist/providers/ai.d.ts.map +1 -0
- package/dist/providers/ai.js +1 -0
- package/dist/providers/default.d.ts +16 -0
- package/dist/providers/default.d.ts.map +1 -0
- package/dist/providers/default.js +17 -0
- package/dist/providers/registry.d.ts +1 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +1 -0
- package/dist/sched/scheduler.d.ts +20 -0
- package/dist/sched/scheduler.d.ts.map +1 -0
- package/dist/sched/scheduler.js +1 -0
- package/dist/sched/task.d.ts +92 -0
- package/dist/sched/task.d.ts.map +1 -0
- package/dist/sched/task.js +102 -0
- package/dist/serde/__tests__/codec.test.d.ts +2 -0
- package/dist/serde/__tests__/codec.test.d.ts.map +1 -0
- package/dist/serde/__tests__/codec.test.js +75 -0
- package/dist/serde/codec.d.ts +12 -0
- package/dist/serde/codec.d.ts.map +1 -0
- package/dist/serde/codec.js +54 -0
- package/dist/serde/json.d.ts +8 -0
- package/dist/serde/json.d.ts.map +1 -0
- package/dist/serde/json.js +13 -0
- package/dist/serde/thread.d.ts +687 -0
- package/dist/serde/thread.d.ts.map +1 -0
- package/dist/serde/thread.js +158 -0
- package/dist/serde/tool.d.ts +36 -0
- package/dist/serde/tool.d.ts.map +1 -0
- package/dist/serde/tool.js +1 -0
- package/dist/session.d.ts +1 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +1 -0
- package/dist/task.d.ts +87 -0
- package/dist/task.d.ts.map +1 -0
- package/dist/task.js +97 -0
- package/dist/thread/__tests__/mock.d.ts +28 -0
- package/dist/thread/__tests__/mock.d.ts.map +1 -0
- package/dist/thread/__tests__/mock.js +74 -0
- package/dist/thread/__tests__/thread.test.d.ts +2 -0
- package/dist/thread/__tests__/thread.test.d.ts.map +1 -0
- package/dist/thread/__tests__/thread.test.js +1412 -0
- package/dist/thread/index.d.ts +2 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/thread/index.js +1 -0
- package/dist/thread/thread.d.ts +66 -0
- package/dist/thread/thread.d.ts.map +1 -0
- package/dist/thread/thread.js +472 -0
- package/dist/thread/utils.d.ts +19 -0
- package/dist/thread/utils.d.ts.map +1 -0
- package/dist/thread/utils.js +50 -0
- package/dist/tool/__tests__/fixtures.d.ts +45 -0
- package/dist/tool/__tests__/fixtures.d.ts.map +1 -0
- package/dist/tool/__tests__/fixtures.js +97 -0
- package/dist/tool/__tests__/tool.test.d.ts +2 -0
- package/dist/tool/__tests__/tool.test.d.ts.map +1 -0
- package/dist/tool/__tests__/tool.test.js +172 -0
- package/dist/tool/__tests__/toolkit.test.d.ts +2 -0
- package/dist/tool/__tests__/toolkit.test.d.ts.map +1 -0
- package/dist/tool/__tests__/toolkit.test.js +134 -0
- package/dist/tool/index.d.ts +4 -0
- package/dist/tool/index.d.ts.map +1 -0
- package/dist/tool/index.js +2 -0
- package/dist/tool/mcp.d.ts +75 -0
- package/dist/tool/mcp.d.ts.map +1 -0
- package/dist/tool/mcp.js +111 -0
- package/dist/tool/tool.d.ts +95 -0
- package/dist/tool/tool.d.ts.map +1 -0
- package/dist/tool/tool.js +176 -0
- package/dist/tool/toolkit.d.ts +121 -0
- package/dist/tool/toolkit.d.ts.map +1 -0
- package/dist/tool/toolkit.js +180 -0
- package/dist/tool/types.d.ts +187 -0
- package/dist/tool/types.d.ts.map +1 -0
- package/dist/tool/types.js +1 -0
- package/dist/tools.d.ts +362 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +220 -0
- package/dist/trace/processor.d.ts +1 -0
- package/dist/trace/processor.d.ts.map +1 -0
- package/dist/trace/processor.js +1 -0
- package/dist/trace/traces.d.ts +1 -0
- package/dist/trace/traces.d.ts.map +1 -0
- package/dist/trace/traces.js +73 -0
- package/dist/trace/utils.d.ts +22 -0
- package/dist/trace/utils.d.ts.map +1 -0
- package/dist/trace/utils.js +30 -0
- package/dist/types/agent.d.ts +91 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +1 -0
- package/dist/types/proto.d.ts +1551 -0
- package/dist/types/proto.d.ts.map +1 -0
- package/dist/types/proto.js +531 -0
- package/dist/types/thread.d.ts +71 -0
- package/dist/types/thread.d.ts.map +1 -0
- package/dist/types/thread.js +5 -0
- package/dist/usage.d.ts +43 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +61 -0
- package/package.json +52 -0
- package/src/agent.ts +203 -0
- package/src/context.ts +265 -0
- package/src/guardrail.ts +277 -0
- package/src/index.ts +3 -0
- package/src/kernl.ts +22 -0
- package/src/lib/env.ts +36 -0
- package/src/lib/error.ts +158 -0
- package/src/lib/logger.ts +78 -0
- package/src/lib/serde/json.ts +18 -0
- package/src/lib/serde/thread.ts +188 -0
- package/src/lifecycle.ts +181 -0
- package/src/mcp/__tests__/base.test.ts +344 -0
- package/src/mcp/__tests__/fixtures/server.ts +179 -0
- package/src/mcp/__tests__/fixtures/utils.ts +58 -0
- package/src/mcp/__tests__/integration.test.ts +447 -0
- package/src/mcp/__tests__/stdio.test.ts +236 -0
- package/src/mcp/__tests__/utils.test.ts +360 -0
- package/src/mcp/base.ts +162 -0
- package/src/mcp/http.ts +147 -0
- package/src/mcp/sse.ts +137 -0
- package/src/mcp/stdio.ts +136 -0
- package/src/mcp/types.ts +202 -0
- package/src/mcp/utils.ts +62 -0
- package/src/task.ts +119 -0
- package/src/thread/__tests__/mock.ts +95 -0
- package/src/thread/__tests__/thread.test.ts +1574 -0
- package/src/thread/index.ts +1 -0
- package/src/thread/thread.ts +611 -0
- package/src/thread/utils.ts +67 -0
- package/src/tool/__tests__/fixtures.ts +106 -0
- package/src/tool/__tests__/tool.test.ts +235 -0
- package/src/tool/__tests__/toolkit.test.ts +174 -0
- package/src/tool/index.ts +10 -0
- package/src/tool/tool.ts +264 -0
- package/src/tool/toolkit.ts +234 -0
- package/src/tool/types.ts +243 -0
- package/src/trace/processor.ts +0 -0
- package/src/trace/traces.ts +86 -0
- package/src/trace/utils.ts +38 -0
- package/src/types/agent.ts +145 -0
- package/src/types/thread.ts +86 -0
- package/tsconfig.json +13 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// import * as events from "@/types/thread";
|
|
4
|
+
|
|
5
|
+
// /**
|
|
6
|
+
// * Utilities and schemas for serializing the run-state so that it can be paused / resumed later.
|
|
7
|
+
// *
|
|
8
|
+
// * (TODO): should be able to define this as a codec for the ThreadState schema
|
|
9
|
+
// */
|
|
10
|
+
|
|
11
|
+
// /**
|
|
12
|
+
// * The schema version of the serialized run state. This is used to ensure that the serialized
|
|
13
|
+
// * run state is compatible with the current version of the SDK.
|
|
14
|
+
// * If anything in this schema changes, the version will have to be incremented.
|
|
15
|
+
// */
|
|
16
|
+
// export const CURRENT_SCHEMA_VERSION = "1.0" as const;
|
|
17
|
+
// const $schemaVersion = z.literal(CURRENT_SCHEMA_VERSION);
|
|
18
|
+
|
|
19
|
+
// const serializedAgentSchema = z.object({
|
|
20
|
+
// id: z.string(),
|
|
21
|
+
// name: z.string().optional(),
|
|
22
|
+
// });
|
|
23
|
+
|
|
24
|
+
// const serializedSpanBase = z.object({
|
|
25
|
+
// object: z.literal("trace.span"),
|
|
26
|
+
// id: z.string(),
|
|
27
|
+
// trace_id: z.string(),
|
|
28
|
+
// parent_id: z.string().nullable(),
|
|
29
|
+
// started_at: z.string().nullable(),
|
|
30
|
+
// ended_at: z.string().nullable(),
|
|
31
|
+
// error: z
|
|
32
|
+
// .object({
|
|
33
|
+
// message: z.string(),
|
|
34
|
+
// data: z.record(z.string(), z.any()).optional(),
|
|
35
|
+
// })
|
|
36
|
+
// .nullable(),
|
|
37
|
+
// span_data: z.record(z.string(), z.any()),
|
|
38
|
+
// });
|
|
39
|
+
|
|
40
|
+
// type SerializedSpanType = z.infer<typeof serializedSpanBase> & {
|
|
41
|
+
// previous_span?: SerializedSpanType;
|
|
42
|
+
// };
|
|
43
|
+
|
|
44
|
+
// const SerializedSpan: z.ZodType<SerializedSpanType> = serializedSpanBase.extend(
|
|
45
|
+
// {
|
|
46
|
+
// previous_span: z.lazy(() => SerializedSpan).optional(),
|
|
47
|
+
// },
|
|
48
|
+
// );
|
|
49
|
+
|
|
50
|
+
// const usageSchema = z.object({
|
|
51
|
+
// requests: z.number(),
|
|
52
|
+
// inputTokens: z.number(),
|
|
53
|
+
// outputTokens: z.number(),
|
|
54
|
+
// totalTokens: z.number(),
|
|
55
|
+
// });
|
|
56
|
+
|
|
57
|
+
// const modelResponseSchema = z.object({
|
|
58
|
+
// usage: usageSchema,
|
|
59
|
+
// events: z.array(events.ThreadEvent),
|
|
60
|
+
// responseId: z.string().optional(),
|
|
61
|
+
// providerData: z.record(z.string(), z.any()).optional(),
|
|
62
|
+
// });
|
|
63
|
+
|
|
64
|
+
// const itemSchema = z.discriminatedUnion("kind", [
|
|
65
|
+
// z.object({
|
|
66
|
+
// kind: z.literal("message"),
|
|
67
|
+
// rawItem: events.AssistantMessage,
|
|
68
|
+
// agent: serializedAgentSchema,
|
|
69
|
+
// }),
|
|
70
|
+
// z.object({
|
|
71
|
+
// kind: z.literal("tool-call"),
|
|
72
|
+
// // rawItem: events.ToolCall.or(events.HostedToolCall),
|
|
73
|
+
// rawItem: events.ToolCall,
|
|
74
|
+
// agent: serializedAgentSchema,
|
|
75
|
+
// }),
|
|
76
|
+
// z.object({
|
|
77
|
+
// kind: z.literal("tool-result"),
|
|
78
|
+
// rawItem: events.ToolResultEvent,
|
|
79
|
+
// agent: serializedAgentSchema,
|
|
80
|
+
// output: z.string(),
|
|
81
|
+
// }),
|
|
82
|
+
// z.object({
|
|
83
|
+
// kind: z.literal("reasoning"),
|
|
84
|
+
// rawItem: events.Reasoning,
|
|
85
|
+
// agent: serializedAgentSchema,
|
|
86
|
+
// }),
|
|
87
|
+
// ]);
|
|
88
|
+
|
|
89
|
+
// const serializedTraceSchema = z.object({
|
|
90
|
+
// object: z.literal("trace"),
|
|
91
|
+
// id: z.string(),
|
|
92
|
+
// workflow_name: z.string(),
|
|
93
|
+
// group_id: z.string().nullable(),
|
|
94
|
+
// metadata: z.record(z.string(), z.any()),
|
|
95
|
+
// });
|
|
96
|
+
|
|
97
|
+
// const serializedProcessedResponseSchema = z.object({
|
|
98
|
+
// newItems: z.array(itemSchema),
|
|
99
|
+
// toolsUsed: z.array(z.string()),
|
|
100
|
+
// functions: z.array(
|
|
101
|
+
// z.object({
|
|
102
|
+
// toolCall: z.any(),
|
|
103
|
+
// tool: z.any(),
|
|
104
|
+
// }),
|
|
105
|
+
// ),
|
|
106
|
+
// mcpApprovalRequests: z
|
|
107
|
+
// .array(
|
|
108
|
+
// z.object({
|
|
109
|
+
// requestItem: z.object({
|
|
110
|
+
// // protocol.HostedToolCallItem
|
|
111
|
+
// rawItem: z.object({
|
|
112
|
+
// type: z.literal("hosted_tool_call"),
|
|
113
|
+
// name: z.string(),
|
|
114
|
+
// arguments: z.string().optional(),
|
|
115
|
+
// status: z.string().optional(),
|
|
116
|
+
// output: z.string().optional(),
|
|
117
|
+
// // this always exists but marked as optional for early version compatibility; when releasing 1.0, we can remove the nullable and optional
|
|
118
|
+
// providerData: z.record(z.string(), z.any()).nullable().optional(),
|
|
119
|
+
// }),
|
|
120
|
+
// }),
|
|
121
|
+
// // HostedMCPTool
|
|
122
|
+
// mcpTool: z.object({
|
|
123
|
+
// type: z.literal("hosted_tool"),
|
|
124
|
+
// name: z.literal("hosted_mcp"),
|
|
125
|
+
// providerData: z.record(z.string(), z.any()),
|
|
126
|
+
// }),
|
|
127
|
+
// }),
|
|
128
|
+
// )
|
|
129
|
+
// .optional(),
|
|
130
|
+
// });
|
|
131
|
+
|
|
132
|
+
// const guardrailFunctionOutputSchema = z.object({
|
|
133
|
+
// tripwireTriggered: z.boolean(),
|
|
134
|
+
// outputInfo: z.any(),
|
|
135
|
+
// });
|
|
136
|
+
|
|
137
|
+
// const inputGuardrailResultSchema = z.object({
|
|
138
|
+
// guardrail: z.object({
|
|
139
|
+
// type: z.literal("input"),
|
|
140
|
+
// name: z.string(),
|
|
141
|
+
// }),
|
|
142
|
+
// output: guardrailFunctionOutputSchema,
|
|
143
|
+
// });
|
|
144
|
+
|
|
145
|
+
// const outputGuardrailResultSchema = z.object({
|
|
146
|
+
// guardrail: z.object({
|
|
147
|
+
// type: z.literal("output"),
|
|
148
|
+
// name: z.string(),
|
|
149
|
+
// }),
|
|
150
|
+
// agentOutput: z.any(),
|
|
151
|
+
// agent: serializedAgentSchema,
|
|
152
|
+
// output: guardrailFunctionOutputSchema,
|
|
153
|
+
// });
|
|
154
|
+
|
|
155
|
+
// // (TODO): define z.codec
|
|
156
|
+
// export const SerializedThread = z.object({
|
|
157
|
+
// $schemaVersion,
|
|
158
|
+
// currentTurn: z.number(),
|
|
159
|
+
// currentAgent: serializedAgentSchema, // (TODO): in our case we probably don't need to serialize the whole agent - an ID would suffice
|
|
160
|
+
// originalInput: z.string().or(z.array(events.ThreadEvent)),
|
|
161
|
+
// modelResponses: z.array(modelResponseSchema),
|
|
162
|
+
// context: z.object({
|
|
163
|
+
// usage: usageSchema, // (TODO): move to stats
|
|
164
|
+
// // (TODO): belongs elsewhere
|
|
165
|
+
// approvals: z.record(
|
|
166
|
+
// z.string(),
|
|
167
|
+
// z.object({
|
|
168
|
+
// approved: z.array(z.string()).or(z.boolean()),
|
|
169
|
+
// rejected: z.array(z.string()).or(z.boolean()),
|
|
170
|
+
// }),
|
|
171
|
+
// ),
|
|
172
|
+
// context: z.record(z.string(), z.any()),
|
|
173
|
+
// }),
|
|
174
|
+
// toolUseTracker: z.record(z.string(), z.array(z.string())),
|
|
175
|
+
// maxTurns: z.number(),
|
|
176
|
+
// currentAgentSpan: SerializedSpan.nullable().optional(),
|
|
177
|
+
// noActiveAgentRun: z.boolean(),
|
|
178
|
+
// inputGuardrailResults: z.array(inputGuardrailResultSchema),
|
|
179
|
+
// outputGuardrailResults: z.array(outputGuardrailResultSchema),
|
|
180
|
+
// // (TODO): currentStep: nextStepSchema.optional(),
|
|
181
|
+
// lastModelResponse: modelResponseSchema.optional(),
|
|
182
|
+
// generatedItems: z.array(itemSchema),
|
|
183
|
+
// lastProcessedResponse: serializedProcessedResponseSchema.optional(),
|
|
184
|
+
// currentTurnPersistedItemCount: z.number().int().min(0).optional(),
|
|
185
|
+
// trace: serializedTraceSchema.nullable(),
|
|
186
|
+
// });
|
|
187
|
+
|
|
188
|
+
// export type SerializedThread = z.infer<typeof SerializedThread>;
|
package/src/lifecycle.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
|
|
3
|
+
import { Agent } from "./agent";
|
|
4
|
+
import { Context, UnknownContext } from "./context";
|
|
5
|
+
import { Tool } from "./tool";
|
|
6
|
+
import type { ToolCall } from "@kernl/protocol";
|
|
7
|
+
|
|
8
|
+
import { AgentResponseType } from "./types/agent";
|
|
9
|
+
import { TextResponse } from "./types/thread";
|
|
10
|
+
|
|
11
|
+
export type EventEmitterEvents = Record<string, any[]>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generic typed event emitter that wraps Node's EventEmitter with type safety
|
|
15
|
+
*/
|
|
16
|
+
class TypedEventEmitter<
|
|
17
|
+
EventTypes extends EventEmitterEvents = Record<string, any[]>,
|
|
18
|
+
> extends EventEmitter {
|
|
19
|
+
// Overload for typed events
|
|
20
|
+
on<K extends keyof EventTypes>(
|
|
21
|
+
event: K,
|
|
22
|
+
listener: (...args: EventTypes[K]) => void,
|
|
23
|
+
): this;
|
|
24
|
+
// Fallback for compatibility with parent
|
|
25
|
+
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
26
|
+
on(event: any, listener: any): this {
|
|
27
|
+
return super.on(event, listener);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Overload for typed events
|
|
31
|
+
off<K extends keyof EventTypes>(
|
|
32
|
+
event: K,
|
|
33
|
+
listener: (...args: EventTypes[K]) => void,
|
|
34
|
+
): this;
|
|
35
|
+
// Fallback for compatibility with parent
|
|
36
|
+
off(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
37
|
+
off(event: any, listener: any): this {
|
|
38
|
+
return super.off(event, listener);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Overload for typed events
|
|
42
|
+
emit<K extends keyof EventTypes>(event: K, ...args: EventTypes[K]): boolean;
|
|
43
|
+
// Fallback for compatibility with parent
|
|
44
|
+
emit(event: string | symbol, ...args: any[]): boolean;
|
|
45
|
+
emit(event: any, ...args: any[]): boolean {
|
|
46
|
+
return super.emit(event, ...args);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Overload for typed events
|
|
50
|
+
once<K extends keyof EventTypes>(
|
|
51
|
+
event: K,
|
|
52
|
+
listener: (...args: EventTypes[K]) => void,
|
|
53
|
+
): this;
|
|
54
|
+
// Fallback for compatibility with parent
|
|
55
|
+
once(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
56
|
+
once(event: any, listener: any): this {
|
|
57
|
+
return super.once(event, listener);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type AgentHookEvents<
|
|
62
|
+
TContext = UnknownContext,
|
|
63
|
+
TOutput extends AgentResponseType = TextResponse,
|
|
64
|
+
> = {
|
|
65
|
+
/**
|
|
66
|
+
* @param context - The context of the run
|
|
67
|
+
*/
|
|
68
|
+
agent_start: [context: Context<TContext>, agent: Agent<TContext, TOutput>];
|
|
69
|
+
/**
|
|
70
|
+
* @param context - The context of the run
|
|
71
|
+
* @param output - The output of the agent
|
|
72
|
+
*/
|
|
73
|
+
agent_end: [context: Context<TContext>, output: string];
|
|
74
|
+
// /**
|
|
75
|
+
// * @param context - The context of the run
|
|
76
|
+
// * @param agent - The agent that is handing off
|
|
77
|
+
// * @param nextAgent - The next agent to run
|
|
78
|
+
// */
|
|
79
|
+
// agent_handoff: [context: Context<TContext>, nextAgent: Agent<any, any>];
|
|
80
|
+
/**
|
|
81
|
+
* @param context - The context of the run
|
|
82
|
+
* @param agent - The agent that is starting a tool
|
|
83
|
+
* @param tool - The tool that is starting
|
|
84
|
+
*/
|
|
85
|
+
agent_tool_start: [
|
|
86
|
+
context: Context<TContext>,
|
|
87
|
+
tool: Tool<any>,
|
|
88
|
+
details: { toolCall: ToolCall },
|
|
89
|
+
];
|
|
90
|
+
/**
|
|
91
|
+
* @param context - The context of the run
|
|
92
|
+
* @param agent - The agent that is ending a tool
|
|
93
|
+
* @param tool - The tool that is ending
|
|
94
|
+
* @param result - The result of the tool
|
|
95
|
+
*/
|
|
96
|
+
agent_tool_end: [
|
|
97
|
+
context: Context<TContext>,
|
|
98
|
+
tool: Tool<any>,
|
|
99
|
+
result: string,
|
|
100
|
+
details: { toolCall: ToolCall },
|
|
101
|
+
];
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Event emitter that every Agent instance inherits from and that emits events for the lifecycle
|
|
106
|
+
* of the agent.
|
|
107
|
+
*/
|
|
108
|
+
export class AgentHooks<
|
|
109
|
+
TContext = UnknownContext,
|
|
110
|
+
TOutput extends AgentResponseType = TextResponse,
|
|
111
|
+
> extends TypedEventEmitter<AgentHookEvents<TContext, TOutput>> {}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Events emitted by the kernl during execution.
|
|
115
|
+
*
|
|
116
|
+
* Unlike AgentHookEvents (which are emitted by individual agents with implicit context),
|
|
117
|
+
* KernlHookEvents explicitly include the agent reference in all events since it needs to
|
|
118
|
+
* coordinate multiple agents and listeners need to know which agent triggered each event.
|
|
119
|
+
*/
|
|
120
|
+
export type KernlHookEvents<
|
|
121
|
+
TContext = UnknownContext,
|
|
122
|
+
TOutput extends AgentResponseType = TextResponse,
|
|
123
|
+
> = {
|
|
124
|
+
/**
|
|
125
|
+
* @param context - The context of the run
|
|
126
|
+
* @param agent - The agent that is starting
|
|
127
|
+
*/
|
|
128
|
+
agent_start: [context: Context<TContext>, agent: Agent<TContext, TOutput>];
|
|
129
|
+
/**
|
|
130
|
+
* @param context - The context of the run
|
|
131
|
+
* @param agent - The agent that is ending
|
|
132
|
+
* @param output - The output of the agent
|
|
133
|
+
*/
|
|
134
|
+
agent_end: [
|
|
135
|
+
context: Context<TContext>,
|
|
136
|
+
agent: Agent<TContext, TOutput>,
|
|
137
|
+
output: string,
|
|
138
|
+
];
|
|
139
|
+
/**
|
|
140
|
+
* @param context - The context of the run
|
|
141
|
+
* @param fromAgent - The agent that is handing off
|
|
142
|
+
* @param toAgent - The next agent to run
|
|
143
|
+
*/
|
|
144
|
+
agent_handoff: [
|
|
145
|
+
context: Context<TContext>,
|
|
146
|
+
fromAgent: Agent<any, any>,
|
|
147
|
+
toAgent: Agent<any, any>,
|
|
148
|
+
];
|
|
149
|
+
/**
|
|
150
|
+
* @param context - The context of the run
|
|
151
|
+
* @param agent - The agent that is starting a tool
|
|
152
|
+
* @param tool - The tool that is starting
|
|
153
|
+
*/
|
|
154
|
+
agent_tool_start: [
|
|
155
|
+
context: Context<TContext>,
|
|
156
|
+
agent: Agent<TContext, TOutput>,
|
|
157
|
+
tool: Tool,
|
|
158
|
+
details: { toolCall: ToolCall },
|
|
159
|
+
];
|
|
160
|
+
/**
|
|
161
|
+
* @param context - The context of the run
|
|
162
|
+
* @param agent - The agent that is ending a tool
|
|
163
|
+
* @param tool - The tool that is ending
|
|
164
|
+
* @param result - The result of the tool
|
|
165
|
+
*/
|
|
166
|
+
agent_tool_end: [
|
|
167
|
+
context: Context<TContext>,
|
|
168
|
+
agent: Agent<TContext, TOutput>,
|
|
169
|
+
tool: Tool,
|
|
170
|
+
result: string,
|
|
171
|
+
details: { toolCall: ToolCall },
|
|
172
|
+
];
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Event emitter that the kernl uses to emit events for the lifecycle of every agent run.
|
|
177
|
+
*/
|
|
178
|
+
export class KernlHooks<
|
|
179
|
+
TContext = UnknownContext,
|
|
180
|
+
TOutput extends AgentResponseType = TextResponse,
|
|
181
|
+
> extends TypedEventEmitter<KernlHookEvents<TContext, TOutput>> {}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { BaseMCPServer } from "../base";
|
|
3
|
+
import type { MCPTool, CallToolResultContent } from "../types";
|
|
4
|
+
import { logger } from "@/lib/logger";
|
|
5
|
+
|
|
6
|
+
// Create a minimal concrete implementation for testing
|
|
7
|
+
class TestMCPServer extends BaseMCPServer {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
private mockTools: MCPTool[] = [];
|
|
10
|
+
private isConnected = false;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
id: string,
|
|
14
|
+
options?: {
|
|
15
|
+
cacheToolsList?: boolean;
|
|
16
|
+
toolFilter?: any;
|
|
17
|
+
},
|
|
18
|
+
) {
|
|
19
|
+
super({
|
|
20
|
+
cacheToolsList: options?.cacheToolsList,
|
|
21
|
+
toolFilter: options?.toolFilter,
|
|
22
|
+
logger: logger,
|
|
23
|
+
});
|
|
24
|
+
this.id = id;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Method to set mock tools for testing
|
|
28
|
+
setMockTools(tools: MCPTool[]): void {
|
|
29
|
+
this.mockTools = tools;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async connect(): Promise<void> {
|
|
33
|
+
this.isConnected = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async close(): Promise<void> {
|
|
37
|
+
this.isConnected = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected async _listTools(): Promise<MCPTool[]> {
|
|
41
|
+
if (!this.isConnected) {
|
|
42
|
+
throw new Error("Not connected");
|
|
43
|
+
}
|
|
44
|
+
return this.mockTools;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async callTool(
|
|
48
|
+
toolName: string,
|
|
49
|
+
args: Record<string, unknown> | null,
|
|
50
|
+
): Promise<CallToolResultContent> {
|
|
51
|
+
if (!this.isConnected) {
|
|
52
|
+
throw new Error("Not connected");
|
|
53
|
+
}
|
|
54
|
+
return [{ type: "text", text: `Called ${toolName}` }];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe("BaseMCPServer", () => {
|
|
59
|
+
describe("Caching mechanisms", () => {
|
|
60
|
+
it("should have cache disabled by default", () => {
|
|
61
|
+
const server = new TestMCPServer("test-server");
|
|
62
|
+
expect(server.cacheToolsList).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should store tools correctly when cache enabled", async () => {
|
|
66
|
+
const server = new TestMCPServer("test-server", {
|
|
67
|
+
cacheToolsList: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const mockTools: MCPTool[] = [
|
|
71
|
+
{
|
|
72
|
+
name: "tool1",
|
|
73
|
+
description: "First tool",
|
|
74
|
+
inputSchema: { type: "object", properties: {} },
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "tool2",
|
|
78
|
+
description: "Second tool",
|
|
79
|
+
inputSchema: { type: "object", properties: {} },
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
server.setMockTools(mockTools);
|
|
84
|
+
await server.connect();
|
|
85
|
+
|
|
86
|
+
const tools = await server.listTools();
|
|
87
|
+
expect(tools).toEqual(mockTools);
|
|
88
|
+
expect(tools).toHaveLength(2);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should return same reference on subsequent calls when cached", async () => {
|
|
92
|
+
const server = new TestMCPServer("test-server", {
|
|
93
|
+
cacheToolsList: true,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const mockTools: MCPTool[] = [
|
|
97
|
+
{
|
|
98
|
+
name: "tool1",
|
|
99
|
+
inputSchema: { type: "object", properties: {} },
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
server.setMockTools(mockTools);
|
|
104
|
+
await server.connect();
|
|
105
|
+
|
|
106
|
+
const tools1 = await server.listTools();
|
|
107
|
+
const tools2 = await server.listTools();
|
|
108
|
+
|
|
109
|
+
// Should return the exact same cached reference
|
|
110
|
+
expect(tools1).toBe(tools2);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should fetch fresh tools when cache disabled", async () => {
|
|
114
|
+
const server = new TestMCPServer("test-server", {
|
|
115
|
+
cacheToolsList: false,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const mockTools: MCPTool[] = [
|
|
119
|
+
{
|
|
120
|
+
name: "tool1",
|
|
121
|
+
inputSchema: { type: "object", properties: {} },
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
server.setMockTools(mockTools);
|
|
126
|
+
await server.connect();
|
|
127
|
+
|
|
128
|
+
const tools1 = await server.listTools();
|
|
129
|
+
const tools2 = await server.listTools();
|
|
130
|
+
|
|
131
|
+
// Both should succeed and have correct data
|
|
132
|
+
expect(tools1).toEqual(mockTools);
|
|
133
|
+
expect(tools2).toEqual(mockTools);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should have cache isolated per server instance", async () => {
|
|
137
|
+
const server1 = new TestMCPServer("server1", {
|
|
138
|
+
cacheToolsList: true,
|
|
139
|
+
});
|
|
140
|
+
const server2 = new TestMCPServer("server2", {
|
|
141
|
+
cacheToolsList: true,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const tools1: MCPTool[] = [
|
|
145
|
+
{
|
|
146
|
+
name: "tool1",
|
|
147
|
+
inputSchema: { type: "object", properties: {} },
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
const tools2: MCPTool[] = [
|
|
151
|
+
{
|
|
152
|
+
name: "tool2",
|
|
153
|
+
inputSchema: { type: "object", properties: {} },
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
server1.setMockTools(tools1);
|
|
158
|
+
server2.setMockTools(tools2);
|
|
159
|
+
|
|
160
|
+
await server1.connect();
|
|
161
|
+
await server2.connect();
|
|
162
|
+
|
|
163
|
+
const result1 = await server1.listTools();
|
|
164
|
+
const result2 = await server2.listTools();
|
|
165
|
+
|
|
166
|
+
expect(result1).toEqual(tools1);
|
|
167
|
+
expect(result2).toEqual(tools2);
|
|
168
|
+
expect(result1).not.toEqual(result2);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should clear cache when invalidateCache() called", async () => {
|
|
172
|
+
const server = new TestMCPServer("test-server", {
|
|
173
|
+
cacheToolsList: true,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const initialTools: MCPTool[] = [
|
|
177
|
+
{
|
|
178
|
+
name: "tool1",
|
|
179
|
+
inputSchema: { type: "object", properties: {} },
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
server.setMockTools(initialTools);
|
|
184
|
+
await server.connect();
|
|
185
|
+
|
|
186
|
+
// First fetch - populates cache
|
|
187
|
+
await server.listTools();
|
|
188
|
+
|
|
189
|
+
// Invalidate cache
|
|
190
|
+
await server.invalidateCache();
|
|
191
|
+
|
|
192
|
+
// Update mock tools
|
|
193
|
+
const newTools: MCPTool[] = [
|
|
194
|
+
{
|
|
195
|
+
name: "tool2",
|
|
196
|
+
inputSchema: { type: "object", properties: {} },
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
server.setMockTools(newTools);
|
|
200
|
+
|
|
201
|
+
// Next fetch should get new tools, not cached ones
|
|
202
|
+
const result = await server.listTools();
|
|
203
|
+
expect(result).toEqual(newTools);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("should set _cacheDirty flag when invalidateCache() called", async () => {
|
|
207
|
+
const server = new TestMCPServer("test-server", {
|
|
208
|
+
cacheToolsList: true,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
await server.connect();
|
|
212
|
+
server.setMockTools([
|
|
213
|
+
{ name: "tool1", inputSchema: { type: "object", properties: {} } },
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// Populate cache
|
|
217
|
+
await server.listTools();
|
|
218
|
+
|
|
219
|
+
// Invalidate
|
|
220
|
+
await server.invalidateCache();
|
|
221
|
+
|
|
222
|
+
// The next listTools() call should fetch fresh data
|
|
223
|
+
// We can verify this by changing the mock tools and seeing the change
|
|
224
|
+
server.setMockTools([
|
|
225
|
+
{ name: "tool2", inputSchema: { type: "object", properties: {} } },
|
|
226
|
+
]);
|
|
227
|
+
|
|
228
|
+
const tools = await server.listTools();
|
|
229
|
+
expect(tools[0].name).toBe("tool2");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should skip cache when _cacheDirty is true", async () => {
|
|
233
|
+
const server = new TestMCPServer("test-server", {
|
|
234
|
+
cacheToolsList: true,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await server.connect();
|
|
238
|
+
|
|
239
|
+
// First call
|
|
240
|
+
server.setMockTools([
|
|
241
|
+
{ name: "tool1", inputSchema: { type: "object", properties: {} } },
|
|
242
|
+
]);
|
|
243
|
+
await server.listTools();
|
|
244
|
+
|
|
245
|
+
// Invalidate cache (sets _cacheDirty = true)
|
|
246
|
+
await server.invalidateCache();
|
|
247
|
+
|
|
248
|
+
// Change mock tools
|
|
249
|
+
server.setMockTools([
|
|
250
|
+
{ name: "tool2", inputSchema: { type: "object", properties: {} } },
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
// Should fetch fresh tools, not use cache
|
|
254
|
+
const tools = await server.listTools();
|
|
255
|
+
expect(tools[0].name).toBe("tool2");
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe("Abstract class contracts", () => {
|
|
260
|
+
it("should allow subclass to implement connect()", async () => {
|
|
261
|
+
const server = new TestMCPServer("test-server");
|
|
262
|
+
|
|
263
|
+
await expect(server.connect()).resolves.not.toThrow();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should allow subclass to implement close()", async () => {
|
|
267
|
+
const server = new TestMCPServer("test-server");
|
|
268
|
+
|
|
269
|
+
await server.connect();
|
|
270
|
+
await expect(server.close()).resolves.not.toThrow();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should allow subclass to implement _listTools()", async () => {
|
|
274
|
+
const server = new TestMCPServer("test-server");
|
|
275
|
+
|
|
276
|
+
server.setMockTools([
|
|
277
|
+
{ name: "tool1", inputSchema: { type: "object", properties: {} } },
|
|
278
|
+
]);
|
|
279
|
+
await server.connect();
|
|
280
|
+
|
|
281
|
+
const tools = await server.listTools();
|
|
282
|
+
expect(tools).toHaveLength(1);
|
|
283
|
+
expect(tools[0].name).toBe("tool1");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("should allow subclass to implement callTool()", async () => {
|
|
287
|
+
const server = new TestMCPServer("test-server");
|
|
288
|
+
|
|
289
|
+
await server.connect();
|
|
290
|
+
const result = await server.callTool("test_tool", { arg: "value" });
|
|
291
|
+
|
|
292
|
+
expect(result).toHaveLength(1);
|
|
293
|
+
expect(result[0].text).toContain("test_tool");
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe("toolFilter integration", () => {
|
|
298
|
+
it("should default to allow-all filter", async () => {
|
|
299
|
+
const server = new TestMCPServer("test-server");
|
|
300
|
+
|
|
301
|
+
// The default filter should allow all tools
|
|
302
|
+
expect(server.toolFilter).toBeDefined();
|
|
303
|
+
|
|
304
|
+
// Test that it returns true for any tool
|
|
305
|
+
const mockContext: any = {
|
|
306
|
+
context: {},
|
|
307
|
+
agent: {},
|
|
308
|
+
serverId: "test",
|
|
309
|
+
};
|
|
310
|
+
const mockTool: MCPTool = {
|
|
311
|
+
name: "any_tool",
|
|
312
|
+
inputSchema: { type: "object", properties: {} },
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const result = await server.toolFilter(mockContext, mockTool);
|
|
316
|
+
expect(result).toBe(true);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should store custom filter correctly in constructor", async () => {
|
|
320
|
+
const customFilter = vi.fn().mockResolvedValue(false);
|
|
321
|
+
|
|
322
|
+
const server = new TestMCPServer("test-server", {
|
|
323
|
+
toolFilter: customFilter,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
expect(server.toolFilter).toBe(customFilter);
|
|
327
|
+
|
|
328
|
+
// Verify the filter is actually used
|
|
329
|
+
const mockContext: any = {
|
|
330
|
+
context: {},
|
|
331
|
+
agent: {},
|
|
332
|
+
serverId: "test",
|
|
333
|
+
};
|
|
334
|
+
const mockTool: MCPTool = {
|
|
335
|
+
name: "tool1",
|
|
336
|
+
inputSchema: { type: "object", properties: {} },
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const result = await server.toolFilter(mockContext, mockTool);
|
|
340
|
+
expect(result).toBe(false);
|
|
341
|
+
expect(customFilter).toHaveBeenCalledWith(mockContext, mockTool);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
});
|