@toolsdk.ai/registry 1.0.113 → 1.0.114
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/api/package-handler.js +7 -6
- package/dist/api/package-so.d.ts +7 -7
- package/dist/api/package-so.js +45 -85
- package/dist/helper.d.ts +3 -1
- package/dist/helper.js +34 -0
- package/dist/sandbox/mcp-sandbox-client.d.ts +4 -25
- package/dist/sandbox/mcp-sandbox-client.js +92 -291
- package/dist/sandbox/mcp-sandbox-client.test.d.ts +1 -0
- package/dist/sandbox/mcp-sandbox-client.test.js +491 -0
- package/dist/types.d.ts +1 -0
- package/package.json +2 -1
- package/packages/search-data-extraction/ref-tools-mcp.json +7 -2
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
// npx vitest run src/sandbox/mcp-sandbox-client.test.ts
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { MCPSandboxClient } from "./mcp-sandbox-client";
|
|
4
|
+
// Mock Daytona SDK
|
|
5
|
+
vi.mock("@daytonaio/sdk", () => {
|
|
6
|
+
const mockSandbox = {
|
|
7
|
+
process: {
|
|
8
|
+
codeRun: vi.fn(),
|
|
9
|
+
},
|
|
10
|
+
delete: vi.fn().mockResolvedValue(undefined),
|
|
11
|
+
};
|
|
12
|
+
const mockVolume = {
|
|
13
|
+
id: "mock-volume-id",
|
|
14
|
+
};
|
|
15
|
+
const mockDaytona = {
|
|
16
|
+
create: vi.fn().mockResolvedValue(mockSandbox),
|
|
17
|
+
volume: {
|
|
18
|
+
get: vi.fn().mockResolvedValue(mockVolume),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
Daytona: vi.fn(() => mockDaytona),
|
|
23
|
+
Image: {
|
|
24
|
+
base: vi.fn().mockReturnValue({
|
|
25
|
+
runCommands: vi.fn().mockReturnThis(),
|
|
26
|
+
workdir: vi.fn().mockReturnThis(),
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
// Mock helper functions
|
|
32
|
+
vi.mock("../helper", () => ({
|
|
33
|
+
extractLastOuterJSON: vi.fn((str) => str),
|
|
34
|
+
getPackageConfigByKey: vi.fn(async (key) => ({
|
|
35
|
+
type: "mcp-server",
|
|
36
|
+
packageName: `@test/${key}`,
|
|
37
|
+
name: "Test Package",
|
|
38
|
+
description: "Test Description",
|
|
39
|
+
runtime: "node",
|
|
40
|
+
env: {
|
|
41
|
+
TEST_API_KEY: {
|
|
42
|
+
description: "Test API Key",
|
|
43
|
+
required: true,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
})),
|
|
47
|
+
}));
|
|
48
|
+
// Helper function to get mocked Daytona instance
|
|
49
|
+
async function getMockedDaytonaInstance() {
|
|
50
|
+
const { Daytona } = await import("@daytonaio/sdk");
|
|
51
|
+
const DaytonaMock = Daytona;
|
|
52
|
+
return DaytonaMock.mock.results[0].value;
|
|
53
|
+
}
|
|
54
|
+
// Helper function to get mocked sandbox
|
|
55
|
+
async function getMockedSandbox() {
|
|
56
|
+
const daytonaInstance = await getMockedDaytonaInstance();
|
|
57
|
+
return daytonaInstance.create.mock.results[0].value;
|
|
58
|
+
}
|
|
59
|
+
// Helper function to mock extractLastOuterJSON
|
|
60
|
+
async function getMockedExtractJSON() {
|
|
61
|
+
const { extractLastOuterJSON } = await import("../helper");
|
|
62
|
+
return extractLastOuterJSON;
|
|
63
|
+
}
|
|
64
|
+
describe("MCPSandboxClient - MCP Sandbox Client Unit Tests", () => {
|
|
65
|
+
let client;
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
vi.clearAllMocks();
|
|
68
|
+
client = new MCPSandboxClient("node");
|
|
69
|
+
});
|
|
70
|
+
afterEach(async () => {
|
|
71
|
+
if (client) {
|
|
72
|
+
await client.kill();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
describe("Initialization Tests", () => {
|
|
76
|
+
it("should successfully initialize Sandbox", async () => {
|
|
77
|
+
await client.initialize();
|
|
78
|
+
// Verify Daytona is called correctly
|
|
79
|
+
const { Daytona } = await import("@daytonaio/sdk");
|
|
80
|
+
expect(Daytona).toHaveBeenCalledWith({
|
|
81
|
+
apiKey: expect.any(String),
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
it("should prevent duplicate initialization", async () => {
|
|
85
|
+
// First initialization
|
|
86
|
+
await client.initialize();
|
|
87
|
+
// Second initialization should return directly without creating new sandbox
|
|
88
|
+
await client.initialize();
|
|
89
|
+
const daytonaInstance = await getMockedDaytonaInstance();
|
|
90
|
+
// create should only be called once
|
|
91
|
+
expect(daytonaInstance.create).toHaveBeenCalledTimes(1);
|
|
92
|
+
});
|
|
93
|
+
it("should support concurrent initialization calls", async () => {
|
|
94
|
+
// Initiate multiple initialization requests simultaneously
|
|
95
|
+
const promises = [client.initialize(), client.initialize(), client.initialize()];
|
|
96
|
+
await Promise.all(promises);
|
|
97
|
+
const daytonaInstance = await getMockedDaytonaInstance();
|
|
98
|
+
// Even with concurrent calls, create should only be called once
|
|
99
|
+
expect(daytonaInstance.create).toHaveBeenCalledTimes(1);
|
|
100
|
+
});
|
|
101
|
+
it("should use correct runtime configuration", () => {
|
|
102
|
+
const nodeClient = new MCPSandboxClient("node");
|
|
103
|
+
const pythonClient = new MCPSandboxClient("python");
|
|
104
|
+
expect(nodeClient).toBeDefined();
|
|
105
|
+
expect(pythonClient).toBeDefined();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe("listTools - List Tools Tests", () => {
|
|
109
|
+
it("should successfully list tools", async () => {
|
|
110
|
+
await client.initialize();
|
|
111
|
+
const mockTools = [
|
|
112
|
+
{
|
|
113
|
+
name: "test_tool_1",
|
|
114
|
+
description: "Test Tool 1",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
param1: { type: "string" },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "test_tool_2",
|
|
124
|
+
description: "Test Tool 2",
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: "object",
|
|
127
|
+
properties: {
|
|
128
|
+
param2: { type: "number" },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
const mockResponse = {
|
|
134
|
+
exitCode: 0,
|
|
135
|
+
result: JSON.stringify({
|
|
136
|
+
toolCount: 2,
|
|
137
|
+
tools: mockTools,
|
|
138
|
+
}),
|
|
139
|
+
};
|
|
140
|
+
const mockSandbox = await getMockedSandbox();
|
|
141
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
142
|
+
const extractMock = await getMockedExtractJSON();
|
|
143
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
144
|
+
const tools = await client.listTools("test-package");
|
|
145
|
+
expect(tools).toEqual(mockTools);
|
|
146
|
+
expect(tools).toHaveLength(2);
|
|
147
|
+
expect(mockSandbox.process.codeRun).toHaveBeenCalledWith(expect.any(String));
|
|
148
|
+
});
|
|
149
|
+
it("should throw error when Sandbox is not initialized", async () => {
|
|
150
|
+
await expect(client.listTools("test-package")).rejects.toThrow("Sandbox not initialized. Call initialize() first.");
|
|
151
|
+
});
|
|
152
|
+
it("should handle execution failure", async () => {
|
|
153
|
+
await client.initialize();
|
|
154
|
+
const mockResponse = {
|
|
155
|
+
exitCode: 1,
|
|
156
|
+
result: "Error: Failed to list tools",
|
|
157
|
+
};
|
|
158
|
+
const mockSandbox = await getMockedSandbox();
|
|
159
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
160
|
+
await expect(client.listTools("test-package")).rejects.toThrow("Failed to list tools");
|
|
161
|
+
});
|
|
162
|
+
it("should handle JSON parsing errors", async () => {
|
|
163
|
+
await client.initialize();
|
|
164
|
+
const mockResponse = {
|
|
165
|
+
exitCode: 0,
|
|
166
|
+
result: "invalid json",
|
|
167
|
+
};
|
|
168
|
+
const mockSandbox = await getMockedSandbox();
|
|
169
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
170
|
+
const extractMock = await getMockedExtractJSON();
|
|
171
|
+
extractMock.mockReturnValue("invalid json");
|
|
172
|
+
await expect(client.listTools("test-package")).rejects.toThrow();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe("executeTool - Execute Tool Tests", () => {
|
|
176
|
+
it("should successfully execute tool", async () => {
|
|
177
|
+
await client.initialize();
|
|
178
|
+
const mockResult = {
|
|
179
|
+
result: { message: "Success", data: { value: 42 } },
|
|
180
|
+
isError: false,
|
|
181
|
+
};
|
|
182
|
+
const mockResponse = {
|
|
183
|
+
exitCode: 0,
|
|
184
|
+
result: JSON.stringify(mockResult),
|
|
185
|
+
};
|
|
186
|
+
const mockSandbox = await getMockedSandbox();
|
|
187
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
188
|
+
const extractMock = await getMockedExtractJSON();
|
|
189
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
190
|
+
const result = await client.executeTool("test-package", "test_tool", {
|
|
191
|
+
param1: "value1",
|
|
192
|
+
});
|
|
193
|
+
expect(result).toEqual(mockResult);
|
|
194
|
+
expect(mockSandbox.process.codeRun).toHaveBeenCalledWith(expect.any(String));
|
|
195
|
+
});
|
|
196
|
+
it("should support passing environment variables", async () => {
|
|
197
|
+
await client.initialize();
|
|
198
|
+
const mockResult = {
|
|
199
|
+
result: { message: "Success with env" },
|
|
200
|
+
isError: false,
|
|
201
|
+
};
|
|
202
|
+
const mockResponse = {
|
|
203
|
+
exitCode: 0,
|
|
204
|
+
result: JSON.stringify(mockResult),
|
|
205
|
+
};
|
|
206
|
+
const mockSandbox = await getMockedSandbox();
|
|
207
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
208
|
+
const extractMock = await getMockedExtractJSON();
|
|
209
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
210
|
+
const envs = {
|
|
211
|
+
TEST_API_KEY: "real-api-key-123",
|
|
212
|
+
TEST_SECRET: "secret-value",
|
|
213
|
+
};
|
|
214
|
+
const result = await client.executeTool("test-package", "test_tool", { param1: "value1" }, envs);
|
|
215
|
+
expect(result).toEqual(mockResult);
|
|
216
|
+
// Verify generated code contains environment variables
|
|
217
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
218
|
+
expect(generatedCode).toContain("TEST_API_KEY");
|
|
219
|
+
});
|
|
220
|
+
it("should handle tool execution errors", async () => {
|
|
221
|
+
await client.initialize();
|
|
222
|
+
const mockResult = {
|
|
223
|
+
result: null,
|
|
224
|
+
isError: true,
|
|
225
|
+
errorMessage: "Tool execution failed: Invalid parameters",
|
|
226
|
+
};
|
|
227
|
+
const mockResponse = {
|
|
228
|
+
exitCode: 0,
|
|
229
|
+
result: JSON.stringify(mockResult),
|
|
230
|
+
};
|
|
231
|
+
const mockSandbox = await getMockedSandbox();
|
|
232
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
233
|
+
const extractMock = await getMockedExtractJSON();
|
|
234
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
235
|
+
await expect(client.executeTool("test-package", "test_tool", { invalid: "param" })).rejects.toThrow("Tool execution failed: Invalid parameters");
|
|
236
|
+
});
|
|
237
|
+
it("should throw error when Sandbox is not initialized", async () => {
|
|
238
|
+
await expect(client.executeTool("test-package", "test_tool", {})).rejects.toThrow("Sandbox not initialized. Call initialize() first.");
|
|
239
|
+
});
|
|
240
|
+
it("should handle non-zero process exit code", async () => {
|
|
241
|
+
await client.initialize();
|
|
242
|
+
const mockResponse = {
|
|
243
|
+
exitCode: 1,
|
|
244
|
+
result: "Process crashed",
|
|
245
|
+
};
|
|
246
|
+
const mockSandbox = await getMockedSandbox();
|
|
247
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
248
|
+
await expect(client.executeTool("test-package", "test_tool", {})).rejects.toThrow("Failed to execute tool");
|
|
249
|
+
});
|
|
250
|
+
it("should correctly serialize complex argument objects", async () => {
|
|
251
|
+
await client.initialize();
|
|
252
|
+
const complexArgs = {
|
|
253
|
+
stringParam: "test",
|
|
254
|
+
numberParam: 42,
|
|
255
|
+
booleanParam: true,
|
|
256
|
+
arrayParam: [1, 2, 3],
|
|
257
|
+
objectParam: { nested: { value: "deep" } },
|
|
258
|
+
};
|
|
259
|
+
const mockResult = {
|
|
260
|
+
result: { success: true },
|
|
261
|
+
isError: false,
|
|
262
|
+
};
|
|
263
|
+
const mockResponse = {
|
|
264
|
+
exitCode: 0,
|
|
265
|
+
result: JSON.stringify(mockResult),
|
|
266
|
+
};
|
|
267
|
+
const mockSandbox = await getMockedSandbox();
|
|
268
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
269
|
+
const extractMock = await getMockedExtractJSON();
|
|
270
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
271
|
+
await client.executeTool("test-package", "test_tool", complexArgs);
|
|
272
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
273
|
+
expect(generatedCode).toContain(JSON.stringify(complexArgs));
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
describe("kill - Cleanup Tests", () => {
|
|
277
|
+
it("should successfully cleanup Sandbox", async () => {
|
|
278
|
+
await client.initialize();
|
|
279
|
+
const mockSandbox = await getMockedSandbox();
|
|
280
|
+
await client.kill();
|
|
281
|
+
expect(mockSandbox.delete).toHaveBeenCalledTimes(1);
|
|
282
|
+
});
|
|
283
|
+
it("should safely return when Sandbox is not initialized", async () => {
|
|
284
|
+
// Call kill directly without initialization
|
|
285
|
+
await expect(client.kill()).resolves.not.toThrow();
|
|
286
|
+
});
|
|
287
|
+
it("should handle deletion failure", async () => {
|
|
288
|
+
await client.initialize();
|
|
289
|
+
const mockSandbox = await getMockedSandbox();
|
|
290
|
+
mockSandbox.delete.mockRejectedValue(new Error("Delete failed"));
|
|
291
|
+
// Should not throw error even if deletion fails
|
|
292
|
+
await expect(client.kill()).resolves.not.toThrow();
|
|
293
|
+
});
|
|
294
|
+
it("should reset Sandbox state after kill", async () => {
|
|
295
|
+
await client.initialize();
|
|
296
|
+
await client.kill();
|
|
297
|
+
// Calling methods that require Sandbox should throw error
|
|
298
|
+
await expect(client.listTools("test-package")).rejects.toThrow("Sandbox not initialized");
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
describe("Code Generation Tests", () => {
|
|
302
|
+
it("generated listTools code should contain necessary imports", async () => {
|
|
303
|
+
await client.initialize();
|
|
304
|
+
const mockResponse = {
|
|
305
|
+
exitCode: 0,
|
|
306
|
+
result: JSON.stringify({ toolCount: 0, tools: [] }),
|
|
307
|
+
};
|
|
308
|
+
const mockSandbox = await getMockedSandbox();
|
|
309
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
310
|
+
const extractMock = await getMockedExtractJSON();
|
|
311
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
312
|
+
await client.listTools("test-package");
|
|
313
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
314
|
+
// Verify necessary imports
|
|
315
|
+
expect(generatedCode).toContain("import { Client }");
|
|
316
|
+
expect(generatedCode).toContain("import { StdioClientTransport }");
|
|
317
|
+
expect(generatedCode).toContain("@modelcontextprotocol/sdk");
|
|
318
|
+
});
|
|
319
|
+
it("generated executeTool code should contain tool name and arguments", async () => {
|
|
320
|
+
await client.initialize();
|
|
321
|
+
const mockResponse = {
|
|
322
|
+
exitCode: 0,
|
|
323
|
+
result: JSON.stringify({ result: {}, isError: false }),
|
|
324
|
+
};
|
|
325
|
+
const mockSandbox = await getMockedSandbox();
|
|
326
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
327
|
+
const extractMock = await getMockedExtractJSON();
|
|
328
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
329
|
+
await client.executeTool("test-package", "my_tool", { key: "value" });
|
|
330
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
331
|
+
expect(generatedCode).toContain("my_tool");
|
|
332
|
+
expect(generatedCode).toContain("client.callTool");
|
|
333
|
+
expect(generatedCode).toContain(JSON.stringify({ key: "value" }));
|
|
334
|
+
});
|
|
335
|
+
it("generated code should use pnpx to execute package", async () => {
|
|
336
|
+
await client.initialize();
|
|
337
|
+
const mockResponse = {
|
|
338
|
+
exitCode: 0,
|
|
339
|
+
result: JSON.stringify({ toolCount: 0, tools: [] }),
|
|
340
|
+
};
|
|
341
|
+
const mockSandbox = await getMockedSandbox();
|
|
342
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
343
|
+
const extractMock = await getMockedExtractJSON();
|
|
344
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
345
|
+
await client.listTools("test-package");
|
|
346
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
347
|
+
expect(generatedCode).toContain('command: "pnpx"');
|
|
348
|
+
expect(generatedCode).toContain('"--silent"');
|
|
349
|
+
});
|
|
350
|
+
it("generated code should contain environment variable configuration", async () => {
|
|
351
|
+
await client.initialize();
|
|
352
|
+
const mockResponse = {
|
|
353
|
+
exitCode: 0,
|
|
354
|
+
result: JSON.stringify({ result: {}, isError: false }),
|
|
355
|
+
};
|
|
356
|
+
const mockSandbox = await getMockedSandbox();
|
|
357
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
358
|
+
const extractMock = await getMockedExtractJSON();
|
|
359
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
360
|
+
await client.executeTool("test-package", "test_tool", {}, { TEST_API_KEY: "real-value" });
|
|
361
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
362
|
+
expect(generatedCode).toContain("PNPM_HOME");
|
|
363
|
+
expect(generatedCode).toContain("PNPM_STORE_PATH");
|
|
364
|
+
expect(generatedCode).toContain("TEST_API_KEY");
|
|
365
|
+
});
|
|
366
|
+
it("generated code should correctly handle missing environment variables", async () => {
|
|
367
|
+
await client.initialize();
|
|
368
|
+
const mockResponse = {
|
|
369
|
+
exitCode: 0,
|
|
370
|
+
result: JSON.stringify({ toolCount: 0, tools: [] }),
|
|
371
|
+
};
|
|
372
|
+
const mockSandbox = await getMockedSandbox();
|
|
373
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
374
|
+
const extractMock = await getMockedExtractJSON();
|
|
375
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
376
|
+
// Do not pass real environment variables
|
|
377
|
+
await client.listTools("test-package");
|
|
378
|
+
const generatedCode = mockSandbox.process.codeRun.mock.calls[0][0];
|
|
379
|
+
// Should use mock_value as default value
|
|
380
|
+
expect(generatedCode).toContain("mock_value");
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
describe("Edge Cases and Exception Tests", () => {
|
|
384
|
+
it("should handle empty tool list", async () => {
|
|
385
|
+
await client.initialize();
|
|
386
|
+
const mockResponse = {
|
|
387
|
+
exitCode: 0,
|
|
388
|
+
result: JSON.stringify({ toolCount: 0, tools: [] }),
|
|
389
|
+
};
|
|
390
|
+
const mockSandbox = await getMockedSandbox();
|
|
391
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
392
|
+
const extractMock = await getMockedExtractJSON();
|
|
393
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
394
|
+
const tools = await client.listTools("empty-package");
|
|
395
|
+
expect(tools).toEqual([]);
|
|
396
|
+
expect(tools).toHaveLength(0);
|
|
397
|
+
});
|
|
398
|
+
it("should handle empty argument object", async () => {
|
|
399
|
+
await client.initialize();
|
|
400
|
+
const mockResult = {
|
|
401
|
+
result: { success: true },
|
|
402
|
+
isError: false,
|
|
403
|
+
};
|
|
404
|
+
const mockResponse = {
|
|
405
|
+
exitCode: 0,
|
|
406
|
+
result: JSON.stringify(mockResult),
|
|
407
|
+
};
|
|
408
|
+
const mockSandbox = await getMockedSandbox();
|
|
409
|
+
mockSandbox.process.codeRun.mockResolvedValue(mockResponse);
|
|
410
|
+
const extractMock = await getMockedExtractJSON();
|
|
411
|
+
extractMock.mockReturnValue(mockResponse.result);
|
|
412
|
+
await expect(client.executeTool("test-package", "test_tool", {})).resolves.toEqual(mockResult);
|
|
413
|
+
});
|
|
414
|
+
it("should handle non-existent package", async () => {
|
|
415
|
+
const { getPackageConfigByKey } = await import("../helper");
|
|
416
|
+
const getPackageMock = getPackageConfigByKey;
|
|
417
|
+
getPackageMock.mockRejectedValueOnce(new Error("Package 'non-existent' not found in packages list."));
|
|
418
|
+
await client.initialize();
|
|
419
|
+
await expect(client.listTools("non-existent")).rejects.toThrow("Package 'non-existent' not found");
|
|
420
|
+
});
|
|
421
|
+
it("should handle network timeout", async () => {
|
|
422
|
+
await client.initialize();
|
|
423
|
+
const mockSandbox = await getMockedSandbox();
|
|
424
|
+
mockSandbox.process.codeRun.mockRejectedValueOnce(new Error("Network timeout"));
|
|
425
|
+
await expect(client.listTools("test-package")).rejects.toThrow("Network timeout");
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
describe("Complete Workflow Tests", () => {
|
|
429
|
+
it("should support complete workflow: initialize -> list tools -> execute tool -> cleanup", async () => {
|
|
430
|
+
// 1. Initialize
|
|
431
|
+
await client.initialize();
|
|
432
|
+
const mockSandbox = await getMockedSandbox();
|
|
433
|
+
// 2. List tools
|
|
434
|
+
const mockTools = [
|
|
435
|
+
{
|
|
436
|
+
name: "hello_tool",
|
|
437
|
+
description: "Say hello",
|
|
438
|
+
inputSchema: {
|
|
439
|
+
type: "object",
|
|
440
|
+
properties: { name: { type: "string" } },
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
];
|
|
444
|
+
mockSandbox.process.codeRun.mockResolvedValueOnce({
|
|
445
|
+
exitCode: 0,
|
|
446
|
+
result: JSON.stringify({ toolCount: 1, tools: mockTools }),
|
|
447
|
+
});
|
|
448
|
+
const extractMock = await getMockedExtractJSON();
|
|
449
|
+
extractMock.mockImplementation((str) => str);
|
|
450
|
+
const tools = await client.listTools("test-package");
|
|
451
|
+
expect(tools).toHaveLength(1);
|
|
452
|
+
expect(tools[0].name).toBe("hello_tool");
|
|
453
|
+
// 3. Execute tool
|
|
454
|
+
mockSandbox.process.codeRun.mockResolvedValueOnce({
|
|
455
|
+
exitCode: 0,
|
|
456
|
+
result: JSON.stringify({
|
|
457
|
+
result: { message: "Hello, World!" },
|
|
458
|
+
isError: false,
|
|
459
|
+
}),
|
|
460
|
+
});
|
|
461
|
+
const result = await client.executeTool("test-package", "hello_tool", {
|
|
462
|
+
name: "World",
|
|
463
|
+
});
|
|
464
|
+
expect(result).toHaveProperty("result");
|
|
465
|
+
// 4. Cleanup
|
|
466
|
+
await client.kill();
|
|
467
|
+
expect(mockSandbox.delete).toHaveBeenCalled();
|
|
468
|
+
});
|
|
469
|
+
it("should support multiple tool executions", async () => {
|
|
470
|
+
await client.initialize();
|
|
471
|
+
const mockSandbox = await getMockedSandbox();
|
|
472
|
+
const extractMock = await getMockedExtractJSON();
|
|
473
|
+
extractMock.mockImplementation((str) => str);
|
|
474
|
+
// Execute multiple times
|
|
475
|
+
for (let i = 0; i < 3; i++) {
|
|
476
|
+
mockSandbox.process.codeRun.mockResolvedValueOnce({
|
|
477
|
+
exitCode: 0,
|
|
478
|
+
result: JSON.stringify({
|
|
479
|
+
result: { iteration: i },
|
|
480
|
+
isError: false,
|
|
481
|
+
}),
|
|
482
|
+
});
|
|
483
|
+
const result = await client.executeTool("test-package", "test_tool", {
|
|
484
|
+
index: i,
|
|
485
|
+
});
|
|
486
|
+
expect(result).toHaveProperty("result");
|
|
487
|
+
}
|
|
488
|
+
expect(mockSandbox.process.codeRun).toHaveBeenCalledTimes(3);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type PackageConfig = z.infer<typeof PackageConfigSchema>;
|
|
|
6
6
|
export type CategoryConfig = z.infer<typeof CategoryConfigSchema>;
|
|
7
7
|
export type PackagesList = z.infer<typeof PackagesListSchema>;
|
|
8
8
|
export type ToolExecute = z.infer<typeof ToolExecuteSchema>;
|
|
9
|
+
export type MCPSandboxProvider = "LOCAL" | "DAYTONA" | "SANDOCK";
|
|
9
10
|
export interface Response<T> {
|
|
10
11
|
success: boolean;
|
|
11
12
|
code: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolsdk.ai/registry",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.114",
|
|
4
4
|
"description": "An Open, Structured, and Standard Registry for MCP Servers and Packages.",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"@cyanheads/pubmed-mcp-server": "1.2.3",
|
|
88
88
|
"@cyanheads/toolkit-mcp-server": "1.0.1",
|
|
89
89
|
"@dasheck0/face-generator": "1.0.1",
|
|
90
|
+
"@daytonaio/sdk": "0.109.0",
|
|
90
91
|
"@debugg-ai/debugg-ai-mcp": "1.0.15",
|
|
91
92
|
"@delorenj/mcp-server-ticketmaster": "0.2.5",
|
|
92
93
|
"@deventerprisesoftware/scrapi-mcp": "0.0.3",
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
"description": "Integrates with Ref.tools documentation search service to provide curated technical documentation access, web search fallback, and URL-to-markdown conversion for efficient developer reference during coding workflows.",
|
|
5
5
|
"url": "https://github.com/ref-tools/ref-tools-mcp",
|
|
6
6
|
"runtime": "node",
|
|
7
|
-
"license": "
|
|
8
|
-
"env": {
|
|
7
|
+
"license": "mit",
|
|
8
|
+
"env": {
|
|
9
|
+
"REF_API_KEY": {
|
|
10
|
+
"description": "sign up to get an API key",
|
|
11
|
+
"required": true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
9
14
|
"name": "Ref Tools MCP"
|
|
10
15
|
}
|