@slashfi/agents-sdk 0.62.0 → 0.64.0
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/dist/adk-tools.d.ts +15 -0
- package/dist/adk-tools.d.ts.map +1 -1
- package/dist/adk-tools.js +9 -2
- package/dist/adk-tools.js.map +1 -1
- package/dist/cjs/adk-tools.js +9 -2
- package/dist/cjs/adk-tools.js.map +1 -1
- package/dist/cjs/index.js +1 -29
- package/dist/cjs/index.js.map +1 -1
- package/dist/index.d.ts +2 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -18
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adk-tools.ts +24 -2
- package/src/index.ts +2 -46
- package/src/types.ts +8 -0
- package/dist/cjs/client.js +0 -193
- package/dist/cjs/client.js.map +0 -1
- package/dist/cjs/codegen.js +0 -1071
- package/dist/cjs/codegen.js.map +0 -1
- package/dist/cjs/introspect.js +0 -136
- package/dist/cjs/introspect.js.map +0 -1
- package/dist/cjs/jsonc.js +0 -74
- package/dist/cjs/jsonc.js.map +0 -1
- package/dist/cjs/pack.js +0 -256
- package/dist/cjs/pack.js.map +0 -1
- package/dist/client.d.ts +0 -49
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -190
- package/dist/client.js.map +0 -1
- package/dist/codegen.d.ts +0 -163
- package/dist/codegen.d.ts.map +0 -1
- package/dist/codegen.js +0 -1059
- package/dist/codegen.js.map +0 -1
- package/dist/introspect.d.ts +0 -16
- package/dist/introspect.d.ts.map +0 -1
- package/dist/introspect.js +0 -133
- package/dist/introspect.js.map +0 -1
- package/dist/jsonc.d.ts +0 -15
- package/dist/jsonc.d.ts.map +0 -1
- package/dist/jsonc.js +0 -70
- package/dist/jsonc.js.map +0 -1
- package/dist/pack.d.ts +0 -59
- package/dist/pack.d.ts.map +0 -1
- package/dist/pack.js +0 -252
- package/dist/pack.js.map +0 -1
- package/src/client.ts +0 -273
- package/src/codegen.test.ts +0 -537
- package/src/codegen.ts +0 -1423
- package/src/introspect.ts +0 -171
- package/src/jsonc.ts +0 -83
- package/src/pack.ts +0 -394
package/src/codegen.test.ts
DELETED
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for MCP Codegen
|
|
3
|
-
*
|
|
4
|
-
* Tests the codegen pipeline using a mock MCP server that responds
|
|
5
|
-
* via HTTP JSON-RPC.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
|
|
9
|
-
import { readFileSync, existsSync, rmSync } from "node:fs";
|
|
10
|
-
import { join } from "node:path";
|
|
11
|
-
import { codegen, useAgent, listAgentTools } from "./codegen.js";
|
|
12
|
-
import type { McpToolDefinition } from "./codegen.js";
|
|
13
|
-
|
|
14
|
-
// ============================================
|
|
15
|
-
// Mock MCP Server
|
|
16
|
-
// ============================================
|
|
17
|
-
|
|
18
|
-
const MOCK_TOOLS: McpToolDefinition[] = [
|
|
19
|
-
{
|
|
20
|
-
name: "search_pages",
|
|
21
|
-
description: "Search for pages by query",
|
|
22
|
-
inputSchema: {
|
|
23
|
-
type: "object",
|
|
24
|
-
properties: {
|
|
25
|
-
query: { type: "string", description: "Search query" },
|
|
26
|
-
limit: { type: "number", description: "Max results" },
|
|
27
|
-
},
|
|
28
|
-
required: ["query"],
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: "create_page",
|
|
33
|
-
description: "Create a new page",
|
|
34
|
-
inputSchema: {
|
|
35
|
-
type: "object",
|
|
36
|
-
properties: {
|
|
37
|
-
title: { type: "string", description: "Page title" },
|
|
38
|
-
content: { type: "string", description: "Page content" },
|
|
39
|
-
parent_id: { type: "string", description: "Parent page ID" },
|
|
40
|
-
},
|
|
41
|
-
required: ["title"],
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: "get_page",
|
|
46
|
-
description: "Get a page by ID",
|
|
47
|
-
inputSchema: {
|
|
48
|
-
type: "object",
|
|
49
|
-
properties: {
|
|
50
|
-
page_id: { type: "string", description: "Page ID" },
|
|
51
|
-
},
|
|
52
|
-
required: ["page_id"],
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
let mockServer: ReturnType<typeof Bun.serve>;
|
|
58
|
-
let mockPort: number;
|
|
59
|
-
|
|
60
|
-
beforeAll(() => {
|
|
61
|
-
mockServer = Bun.serve({
|
|
62
|
-
port: 0,
|
|
63
|
-
fetch(req) {
|
|
64
|
-
return (async () => {
|
|
65
|
-
// Handle GET requests (well-known endpoints, etc.)
|
|
66
|
-
if (req.method === "GET") {
|
|
67
|
-
return new Response("Not Found", { status: 404 });
|
|
68
|
-
}
|
|
69
|
-
const body = (await req.json()) as {
|
|
70
|
-
id?: number;
|
|
71
|
-
method: string;
|
|
72
|
-
params?: Record<string, unknown>;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Handle notifications (no id) — return 202 Accepted
|
|
76
|
-
if (body.id === undefined) {
|
|
77
|
-
return new Response(null, { status: 202 });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let result: unknown;
|
|
81
|
-
|
|
82
|
-
switch (body.method) {
|
|
83
|
-
case "initialize":
|
|
84
|
-
result = {
|
|
85
|
-
protocolVersion: "2025-03-26",
|
|
86
|
-
serverInfo: {
|
|
87
|
-
name: "mock-notion",
|
|
88
|
-
version: "1.0.0",
|
|
89
|
-
},
|
|
90
|
-
capabilities: { tools: {} },
|
|
91
|
-
};
|
|
92
|
-
break;
|
|
93
|
-
|
|
94
|
-
case "notifications/initialized":
|
|
95
|
-
result = {};
|
|
96
|
-
break;
|
|
97
|
-
|
|
98
|
-
case "tools/list":
|
|
99
|
-
result = { tools: MOCK_TOOLS };
|
|
100
|
-
break;
|
|
101
|
-
|
|
102
|
-
case "tools/call": {
|
|
103
|
-
const params = body.params as {
|
|
104
|
-
name: string;
|
|
105
|
-
arguments: Record<string, unknown>;
|
|
106
|
-
};
|
|
107
|
-
result = {
|
|
108
|
-
content: [
|
|
109
|
-
{
|
|
110
|
-
type: "text",
|
|
111
|
-
text: JSON.stringify({
|
|
112
|
-
tool: params.name,
|
|
113
|
-
args: params.arguments,
|
|
114
|
-
mock: true,
|
|
115
|
-
}),
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
};
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
default:
|
|
123
|
-
return Response.json(
|
|
124
|
-
{
|
|
125
|
-
jsonrpc: "2.0",
|
|
126
|
-
id: body.id,
|
|
127
|
-
error: { code: -32601, message: `Unknown method: ${body.method}` },
|
|
128
|
-
},
|
|
129
|
-
{ status: 200 },
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return Response.json({
|
|
134
|
-
jsonrpc: "2.0",
|
|
135
|
-
id: body.id,
|
|
136
|
-
result,
|
|
137
|
-
});
|
|
138
|
-
})();
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
mockPort = mockServer.port;
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
afterAll(() => {
|
|
145
|
-
mockServer.stop();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// ============================================
|
|
149
|
-
// Tests
|
|
150
|
-
// ============================================
|
|
151
|
-
|
|
152
|
-
const TEST_OUT_DIR = "/tmp/agents-sdk-codegen-test";
|
|
153
|
-
|
|
154
|
-
describe("codegen", () => {
|
|
155
|
-
beforeAll(async () => {
|
|
156
|
-
// Clean up previous test output
|
|
157
|
-
rmSync(TEST_OUT_DIR, { recursive: true, force: true });
|
|
158
|
-
|
|
159
|
-
await codegen({
|
|
160
|
-
server: `http://localhost:${mockPort}`,
|
|
161
|
-
outDir: TEST_OUT_DIR,
|
|
162
|
-
name: "notion",
|
|
163
|
-
agentPath: "@notion",
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
afterAll(() => {
|
|
168
|
-
rmSync(TEST_OUT_DIR, { recursive: true, force: true });
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("generates tool files for each MCP tool", () => {
|
|
172
|
-
expect(existsSync(join(TEST_OUT_DIR, "search-pages.tool.md"))).toBe(true);
|
|
173
|
-
expect(existsSync(join(TEST_OUT_DIR, "create-page.tool.md"))).toBe(true);
|
|
174
|
-
expect(existsSync(join(TEST_OUT_DIR, "get-page.tool.md"))).toBe(true);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("generates agent.config.ts", () => {
|
|
178
|
-
const content = readFileSync(
|
|
179
|
-
join(TEST_OUT_DIR, "agent.config.ts"),
|
|
180
|
-
"utf-8",
|
|
181
|
-
);
|
|
182
|
-
expect(content).toContain("defineAgent");
|
|
183
|
-
expect(content).toContain("'@notion'");
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test("generates entrypoint.md", () => {
|
|
187
|
-
const content = readFileSync(
|
|
188
|
-
join(TEST_OUT_DIR, "entrypoint.md"),
|
|
189
|
-
"utf-8",
|
|
190
|
-
);
|
|
191
|
-
expect(content).toContain("# mock-notion");
|
|
192
|
-
expect(content).toContain("search_pages");
|
|
193
|
-
expect(content).toContain("create_page");
|
|
194
|
-
expect(content).toContain("get_page");
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("generates index.ts with agent export", () => {
|
|
198
|
-
const content = readFileSync(join(TEST_OUT_DIR, "index.ts"), "utf-8");
|
|
199
|
-
expect(content).toContain("agent.config");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("generates cli.ts", () => {
|
|
203
|
-
const content = readFileSync(join(TEST_OUT_DIR, "cli.ts"), "utf-8");
|
|
204
|
-
expect(content).toContain("search_pages");
|
|
205
|
-
expect(content).toContain("--list");
|
|
206
|
-
expect(content).toContain("--help");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("generates .codegen-manifest.json", () => {
|
|
210
|
-
const content = readFileSync(
|
|
211
|
-
join(TEST_OUT_DIR, ".codegen-manifest.json"),
|
|
212
|
-
"utf-8",
|
|
213
|
-
);
|
|
214
|
-
const manifest = JSON.parse(content);
|
|
215
|
-
expect(manifest.agentPath).toBe("@notion");
|
|
216
|
-
expect(manifest.serverInfo.name).toBe("mock-notion");
|
|
217
|
-
expect(manifest.tools).toHaveLength(3);
|
|
218
|
-
expect(manifest.tools[0].name).toBe("search_pages");
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
test("tool files contain markdown with parameters", () => {
|
|
222
|
-
const content = readFileSync(
|
|
223
|
-
join(TEST_OUT_DIR, "search-pages.tool.md"),
|
|
224
|
-
"utf-8",
|
|
225
|
-
);
|
|
226
|
-
expect(content).toContain("# search_pages");
|
|
227
|
-
expect(content).toContain("Search for pages by query");
|
|
228
|
-
expect(content).toContain("| query");
|
|
229
|
-
expect(content).toContain("| limit");
|
|
230
|
-
expect(content).toContain("## Parameters");
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test("tool files include type and required info", () => {
|
|
234
|
-
const content = readFileSync(
|
|
235
|
-
join(TEST_OUT_DIR, "search-pages.tool.md"),
|
|
236
|
-
"utf-8",
|
|
237
|
-
);
|
|
238
|
-
expect(content).toContain("`string`");
|
|
239
|
-
expect(content).toContain("✓"); // required marker for query
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test("agent.config.ts has defineAgent", () => {
|
|
243
|
-
const content = readFileSync(
|
|
244
|
-
join(TEST_OUT_DIR, "agent.config.ts"),
|
|
245
|
-
"utf-8",
|
|
246
|
-
);
|
|
247
|
-
expect(content).toContain("defineAgent");
|
|
248
|
-
expect(content).toContain("entrypoint");
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe("listAgentTools", () => {
|
|
253
|
-
beforeAll(async () => {
|
|
254
|
-
rmSync(TEST_OUT_DIR, { recursive: true, force: true });
|
|
255
|
-
await codegen({
|
|
256
|
-
server: `http://localhost:${mockPort}`,
|
|
257
|
-
outDir: TEST_OUT_DIR,
|
|
258
|
-
name: "notion",
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
afterAll(() => {
|
|
263
|
-
rmSync(TEST_OUT_DIR, { recursive: true, force: true });
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test("lists tools from manifest", () => {
|
|
267
|
-
const tools = listAgentTools(TEST_OUT_DIR);
|
|
268
|
-
expect(tools).toHaveLength(3);
|
|
269
|
-
expect(tools[0].name).toBe("search_pages");
|
|
270
|
-
expect(tools[1].name).toBe("create_page");
|
|
271
|
-
expect(tools[2].name).toBe("get_page");
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
describe("codegen JSON Schema support", () => {
|
|
276
|
-
const SCHEMA_OUT_DIR = "/tmp/agents-sdk-codegen-schema-test";
|
|
277
|
-
let schemaServer: ReturnType<typeof Bun.serve>;
|
|
278
|
-
let schemaPort: number;
|
|
279
|
-
|
|
280
|
-
const COMPLEX_TOOLS: McpToolDefinition[] = [
|
|
281
|
-
{
|
|
282
|
-
name: "complex_tool",
|
|
283
|
-
description: "Tool with advanced JSON Schema features",
|
|
284
|
-
inputSchema: {
|
|
285
|
-
type: "object",
|
|
286
|
-
properties: {
|
|
287
|
-
status: { enum: ["open", "closed", "pending"] },
|
|
288
|
-
value: { oneOf: [{ type: "string" }, { type: "number" }] },
|
|
289
|
-
merged: { allOf: [{ type: "object", properties: { a: { type: "string" } } }, { type: "object", properties: { b: { type: "number" } } }] },
|
|
290
|
-
flexible: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
291
|
-
literal: { const: "fixed_value" },
|
|
292
|
-
excluded: { not: { type: "string" } },
|
|
293
|
-
email: { type: "string", format: "email" },
|
|
294
|
-
age: { type: "integer" },
|
|
295
|
-
nullable: { type: ["string", "null"] },
|
|
296
|
-
tags: { type: "array", items: { type: "string" } },
|
|
297
|
-
metadata: { type: "object", additionalProperties: { type: "string" } },
|
|
298
|
-
coords: { type: "array", prefixItems: [{ type: "number" }, { type: "number" }] },
|
|
299
|
-
},
|
|
300
|
-
required: ["status", "value"],
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
];
|
|
304
|
-
|
|
305
|
-
beforeAll(async () => {
|
|
306
|
-
rmSync(SCHEMA_OUT_DIR, { recursive: true, force: true });
|
|
307
|
-
|
|
308
|
-
schemaServer = Bun.serve({
|
|
309
|
-
port: 0,
|
|
310
|
-
fetch(req) {
|
|
311
|
-
return (async () => {
|
|
312
|
-
if (req.method === "GET") {
|
|
313
|
-
return new Response("Not Found", { status: 404 });
|
|
314
|
-
}
|
|
315
|
-
const body = (await req.json()) as { id?: number; method: string; params?: Record<string, unknown> };
|
|
316
|
-
if (body.id === undefined) return new Response(null, { status: 202 });
|
|
317
|
-
|
|
318
|
-
let result: unknown;
|
|
319
|
-
switch (body.method) {
|
|
320
|
-
case "initialize":
|
|
321
|
-
result = { protocolVersion: "2025-03-26", serverInfo: { name: "schema-test", version: "1.0.0" }, capabilities: { tools: {} } };
|
|
322
|
-
break;
|
|
323
|
-
case "tools/list":
|
|
324
|
-
result = { tools: COMPLEX_TOOLS };
|
|
325
|
-
break;
|
|
326
|
-
default:
|
|
327
|
-
return Response.json({ jsonrpc: "2.0", id: body.id, error: { code: -32601, message: `Unknown: ${body.method}` } });
|
|
328
|
-
}
|
|
329
|
-
return Response.json({ jsonrpc: "2.0", id: body.id, result });
|
|
330
|
-
})();
|
|
331
|
-
},
|
|
332
|
-
});
|
|
333
|
-
schemaPort = schemaServer.port;
|
|
334
|
-
|
|
335
|
-
await codegen({
|
|
336
|
-
server: `http://localhost:${schemaPort}`,
|
|
337
|
-
outDir: SCHEMA_OUT_DIR,
|
|
338
|
-
name: "schema-test",
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
afterAll(() => {
|
|
343
|
-
schemaServer.stop();
|
|
344
|
-
rmSync(SCHEMA_OUT_DIR, { recursive: true, force: true });
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
test("renders enum as union", () => {
|
|
348
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
349
|
-
expect(content).toContain('"open" | "closed" | "pending"');
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
test("renders oneOf as union", () => {
|
|
353
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
354
|
-
expect(content).toContain("string | number");
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test("renders allOf as intersection", () => {
|
|
358
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
359
|
-
expect(content).toContain("&");
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
test("renders const as literal", () => {
|
|
363
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
364
|
-
expect(content).toContain('"fixed_value"');
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test("renders not", () => {
|
|
368
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
369
|
-
expect(content).toContain("Exclude");
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test("renders format hint", () => {
|
|
373
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
374
|
-
expect(content).toContain("email");
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
test("renders integer as number", () => {
|
|
378
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
379
|
-
expect(content).toContain("integer");
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
test("renders nullable type array", () => {
|
|
383
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
384
|
-
expect(content).toContain("string | null");
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
test("renders additionalProperties as Record", () => {
|
|
388
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
389
|
-
expect(content).toContain("Record<string, string>");
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test("renders tuple arrays", () => {
|
|
393
|
-
const content = readFileSync(join(SCHEMA_OUT_DIR, "complex-tool.tool.md"), "utf-8");
|
|
394
|
-
expect(content).toContain("[number, number]");
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
describe("codegen pagination", () => {
|
|
399
|
-
const PAGINATED_OUT_DIR = "/tmp/agents-sdk-codegen-paginated-test";
|
|
400
|
-
let paginatedServer: ReturnType<typeof Bun.serve>;
|
|
401
|
-
let paginatedPort: number;
|
|
402
|
-
|
|
403
|
-
beforeAll(async () => {
|
|
404
|
-
rmSync(PAGINATED_OUT_DIR, { recursive: true, force: true });
|
|
405
|
-
|
|
406
|
-
// Mock server that paginates tools across 2 pages
|
|
407
|
-
paginatedServer = Bun.serve({
|
|
408
|
-
port: 0,
|
|
409
|
-
fetch(req) {
|
|
410
|
-
return (async () => {
|
|
411
|
-
if (req.method === "GET") {
|
|
412
|
-
return new Response("Not Found", { status: 404 });
|
|
413
|
-
}
|
|
414
|
-
const body = (await req.json()) as {
|
|
415
|
-
id?: number;
|
|
416
|
-
method: string;
|
|
417
|
-
params?: Record<string, unknown>;
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
if (body.id === undefined) {
|
|
421
|
-
return new Response(null, { status: 202 });
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
let result: unknown;
|
|
425
|
-
|
|
426
|
-
switch (body.method) {
|
|
427
|
-
case "initialize":
|
|
428
|
-
result = {
|
|
429
|
-
protocolVersion: "2025-03-26",
|
|
430
|
-
serverInfo: { name: "paginated-server", version: "1.0.0" },
|
|
431
|
-
capabilities: { tools: {} },
|
|
432
|
-
};
|
|
433
|
-
break;
|
|
434
|
-
|
|
435
|
-
case "tools/list": {
|
|
436
|
-
const cursor = (body.params as { cursor?: string })?.cursor;
|
|
437
|
-
if (!cursor) {
|
|
438
|
-
// Page 1: return first 2 tools + nextCursor
|
|
439
|
-
result = {
|
|
440
|
-
tools: [MOCK_TOOLS[0], MOCK_TOOLS[1]],
|
|
441
|
-
nextCursor: "page2",
|
|
442
|
-
};
|
|
443
|
-
} else {
|
|
444
|
-
// Page 2: return last tool, no nextCursor
|
|
445
|
-
result = {
|
|
446
|
-
tools: [MOCK_TOOLS[2]],
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
default:
|
|
453
|
-
return Response.json(
|
|
454
|
-
{
|
|
455
|
-
jsonrpc: "2.0",
|
|
456
|
-
id: body.id,
|
|
457
|
-
error: { code: -32601, message: `Unknown: ${body.method}` },
|
|
458
|
-
},
|
|
459
|
-
{ status: 200 },
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return Response.json({
|
|
464
|
-
jsonrpc: "2.0",
|
|
465
|
-
id: body.id,
|
|
466
|
-
result,
|
|
467
|
-
});
|
|
468
|
-
})();
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
paginatedPort = paginatedServer.port;
|
|
472
|
-
|
|
473
|
-
await codegen({
|
|
474
|
-
server: `http://localhost:${paginatedPort}`,
|
|
475
|
-
outDir: PAGINATED_OUT_DIR,
|
|
476
|
-
name: "paginated",
|
|
477
|
-
});
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
afterAll(() => {
|
|
481
|
-
paginatedServer.stop();
|
|
482
|
-
rmSync(PAGINATED_OUT_DIR, { recursive: true, force: true });
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
test("collects all tools across paginated responses", () => {
|
|
486
|
-
// All 3 tools should be present despite being split across 2 pages
|
|
487
|
-
expect(existsSync(join(PAGINATED_OUT_DIR, "search-pages.tool.md"))).toBe(true);
|
|
488
|
-
expect(existsSync(join(PAGINATED_OUT_DIR, "create-page.tool.md"))).toBe(true);
|
|
489
|
-
expect(existsSync(join(PAGINATED_OUT_DIR, "get-page.tool.md"))).toBe(true);
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
test("manifest contains all paginated tools", () => {
|
|
493
|
-
const manifest = JSON.parse(
|
|
494
|
-
readFileSync(join(PAGINATED_OUT_DIR, ".codegen-manifest.json"), "utf-8"),
|
|
495
|
-
);
|
|
496
|
-
expect(manifest.tools.length).toBe(3);
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
describe("useAgent", () => {
|
|
501
|
-
beforeAll(async () => {
|
|
502
|
-
rmSync(TEST_OUT_DIR, { recursive: true, force: true });
|
|
503
|
-
await codegen({
|
|
504
|
-
server: `http://localhost:${mockPort}`,
|
|
505
|
-
outDir: TEST_OUT_DIR,
|
|
506
|
-
name: "notion",
|
|
507
|
-
});
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
afterAll(() => {
|
|
511
|
-
rmSync(TEST_OUT_DIR, { recursive: true, force: true });
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
test("executes a tool via MCP server", async () => {
|
|
515
|
-
const result = (await useAgent({
|
|
516
|
-
agentDir: TEST_OUT_DIR,
|
|
517
|
-
tool: "search_pages",
|
|
518
|
-
params: { query: "hello" },
|
|
519
|
-
})) as { content: { type: string; text: string }[] };
|
|
520
|
-
|
|
521
|
-
expect(result.content).toBeDefined();
|
|
522
|
-
expect(result.content[0].type).toBe("text");
|
|
523
|
-
const parsed = JSON.parse(result.content[0].text);
|
|
524
|
-
expect(parsed.tool).toBe("search_pages");
|
|
525
|
-
expect(parsed.args.query).toBe("hello");
|
|
526
|
-
expect(parsed.mock).toBe(true);
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
test("rejects unknown tool", async () => {
|
|
530
|
-
await expect(
|
|
531
|
-
useAgent({
|
|
532
|
-
agentDir: TEST_OUT_DIR,
|
|
533
|
-
tool: "nonexistent_tool",
|
|
534
|
-
}),
|
|
535
|
-
).rejects.toThrow("Unknown tool");
|
|
536
|
-
});
|
|
537
|
-
});
|