@toolsdk.ai/registry 1.0.113 → 1.0.115
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/README.md +66 -11
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +23 -25
- package/dist/domains/config/config-route.d.ts +2 -0
- package/dist/domains/config/config-route.js +31 -0
- package/dist/domains/config/config-schema.d.ts +57 -0
- package/dist/domains/config/config-schema.js +10 -0
- package/dist/domains/config/config-types.d.ts +3 -0
- package/dist/domains/executor/executor-factory.d.ts +9 -0
- package/dist/domains/executor/executor-factory.js +17 -0
- package/dist/domains/executor/executor-types.d.ts +15 -0
- package/dist/domains/executor/local-executor.d.ts +12 -0
- package/dist/domains/executor/local-executor.js +48 -0
- package/dist/domains/executor/sandbox-executor.d.ts +16 -0
- package/dist/domains/executor/sandbox-executor.js +83 -0
- package/dist/domains/package/package-handler.d.ts +17 -0
- package/dist/domains/package/package-handler.js +58 -0
- package/dist/domains/package/package-repository.d.ts +9 -0
- package/dist/domains/package/package-repository.js +26 -0
- package/dist/domains/package/package-route.d.ts +2 -0
- package/dist/{api → domains/package}/package-route.js +38 -52
- package/dist/domains/package/package-schema.d.ts +244 -0
- package/dist/domains/package/package-schema.js +52 -0
- package/dist/domains/package/package-so.d.ts +78 -0
- package/dist/domains/package/package-so.js +61 -0
- package/dist/domains/package/package-so.test.js +378 -0
- package/dist/domains/package/package-types.d.ts +9 -0
- package/dist/domains/package/package-types.js +1 -0
- package/dist/domains/sandbox/clients/daytona-client.d.ts +17 -0
- package/dist/domains/sandbox/clients/daytona-client.js +112 -0
- package/dist/domains/sandbox/clients/sandock-client.d.ts +19 -0
- package/dist/domains/sandbox/clients/sandock-client.js +178 -0
- package/dist/domains/sandbox/sandbox-factory.d.ts +8 -0
- package/dist/domains/sandbox/sandbox-factory.js +23 -0
- package/dist/domains/sandbox/sandbox-pool-so.d.ts +25 -0
- package/dist/domains/sandbox/sandbox-pool-so.js +123 -0
- package/dist/domains/sandbox/sandbox-types.d.ts +25 -0
- package/dist/domains/sandbox/sandbox-types.js +1 -0
- package/dist/domains/sandbox/sandbox-utils.d.ts +3 -0
- package/dist/domains/sandbox/sandbox-utils.js +109 -0
- package/dist/domains/search/search-handler.d.ts +47 -0
- package/dist/domains/search/search-handler.js +113 -0
- package/dist/domains/search/search-route.d.ts +2 -0
- package/dist/domains/search/search-route.js +101 -0
- package/dist/domains/search/search-schema.d.ts +384 -0
- package/dist/domains/search/search-schema.js +99 -0
- package/dist/domains/search/search-so.d.ts +55 -0
- package/dist/{search/search-service.js → domains/search/search-so.js} +200 -297
- package/dist/shared/config/environment.d.ts +16 -0
- package/dist/shared/config/environment.js +41 -0
- package/dist/shared/schemas/common-schema.d.ts +249 -0
- package/dist/{schema.js → shared/schemas/common-schema.js} +37 -80
- package/dist/shared/schemas/index.d.ts +1 -0
- package/dist/shared/schemas/index.js +1 -0
- package/dist/shared/scripts-helpers/index.d.ts +60 -0
- package/dist/shared/scripts-helpers/index.js +61 -0
- package/dist/shared/utils/file-util.d.ts +1 -0
- package/dist/shared/utils/file-util.js +5 -0
- package/dist/shared/utils/index.d.ts +5 -0
- package/dist/shared/utils/index.js +5 -0
- package/dist/shared/utils/mcp-client-util.d.ts +31 -0
- package/dist/shared/utils/mcp-client-util.js +79 -0
- package/dist/shared/utils/package-util.d.ts +6 -0
- package/dist/shared/utils/package-util.js +53 -0
- package/dist/shared/utils/promise-util.d.ts +1 -0
- package/dist/shared/utils/promise-util.js +14 -0
- package/dist/{utils.d.ts → shared/utils/response-util.d.ts} +6 -2
- package/dist/{utils.js → shared/utils/response-util.js} +1 -6
- package/dist/shared/utils/string-util.d.ts +1 -0
- package/dist/shared/utils/string-util.js +25 -0
- package/dist/shared/utils/validation-util.d.ts +12 -0
- package/dist/shared/utils/validation-util.js +99 -0
- package/indexes/categories-list.json +1 -0
- package/indexes/packages-list.json +6 -0
- package/package.json +9 -2
- package/packages/developer-tools/neurolink.json +23 -0
- package/packages/search-data-extraction/ref-tools-mcp.json +7 -2
- package/README.dev.md +0 -195
- package/dist/api/package-handler.d.ts +0 -18
- package/dist/api/package-handler.js +0 -72
- package/dist/api/package-route.d.ts +0 -2
- package/dist/api/package-so.d.ts +0 -19
- package/dist/api/package-so.js +0 -263
- package/dist/api/package.test.js +0 -19
- package/dist/helper.d.ts +0 -72
- package/dist/helper.js +0 -278
- package/dist/sandbox/mcp-sandbox-client.d.ts +0 -37
- package/dist/sandbox/mcp-sandbox-client.js +0 -428
- package/dist/schema.d.ts +0 -806
- package/dist/search/search-route.d.ts +0 -3
- package/dist/search/search-route.js +0 -305
- package/dist/search/search-service.d.ts +0 -120
- package/dist/search/search.test.js +0 -100
- package/dist/types.d.ts +0 -27
- /package/dist/{api/package.test.d.ts → domains/config/config-types.js} +0 -0
- /package/dist/{search/search.test.d.ts → domains/executor/executor-types.js} +0 -0
- /package/dist/{types.js → domains/package/package-so.test.d.ts} +0 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { PackageSO } from "./package-so";
|
|
3
|
+
describe("PackageSO", () => {
|
|
4
|
+
let mockRepository;
|
|
5
|
+
let mockExecutor;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Mock PackageRepository
|
|
8
|
+
mockRepository = {
|
|
9
|
+
getPackageConfig: vi.fn(),
|
|
10
|
+
getAllPackages: vi.fn(),
|
|
11
|
+
exists: vi.fn(),
|
|
12
|
+
};
|
|
13
|
+
// Mock ToolExecutor
|
|
14
|
+
mockExecutor = {
|
|
15
|
+
listTools: vi.fn(),
|
|
16
|
+
executeTool: vi.fn(),
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
describe("init", () => {
|
|
20
|
+
it("should successfully initialize PackageSO instance", async () => {
|
|
21
|
+
// Arrange
|
|
22
|
+
const packageName = "@modelcontextprotocol/server-filesystem";
|
|
23
|
+
const mockConfig = {
|
|
24
|
+
type: "mcp-server",
|
|
25
|
+
runtime: "node",
|
|
26
|
+
packageName,
|
|
27
|
+
name: "Filesystem Server",
|
|
28
|
+
description: "A server for filesystem operations",
|
|
29
|
+
};
|
|
30
|
+
const mockPackageInfo = {
|
|
31
|
+
path: "path/to/package",
|
|
32
|
+
category: "filesystem",
|
|
33
|
+
validated: true,
|
|
34
|
+
};
|
|
35
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
36
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({
|
|
37
|
+
[packageName]: mockPackageInfo,
|
|
38
|
+
});
|
|
39
|
+
// Act
|
|
40
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
41
|
+
// Assert
|
|
42
|
+
expect(packageSO.packageName).toBe(packageName);
|
|
43
|
+
expect(packageSO.name).toBe("Filesystem Server");
|
|
44
|
+
expect(packageSO.description).toBe("A server for filesystem operations");
|
|
45
|
+
expect(packageSO.category).toBe("filesystem");
|
|
46
|
+
expect(packageSO.validated).toBe(true);
|
|
47
|
+
expect(mockRepository.getPackageConfig).toHaveBeenCalledWith(packageName);
|
|
48
|
+
expect(mockRepository.getAllPackages).toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
it("should handle case when package config exists but packageInfo does not", async () => {
|
|
51
|
+
// Arrange
|
|
52
|
+
const packageName = "@test/new-package";
|
|
53
|
+
const mockConfig = {
|
|
54
|
+
type: "mcp-server",
|
|
55
|
+
runtime: "node",
|
|
56
|
+
packageName,
|
|
57
|
+
name: "New Package",
|
|
58
|
+
description: "A new package",
|
|
59
|
+
};
|
|
60
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
61
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
62
|
+
// Act
|
|
63
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
64
|
+
// Assert
|
|
65
|
+
expect(packageSO.packageName).toBe(packageName);
|
|
66
|
+
expect(packageSO.name).toBe("New Package");
|
|
67
|
+
expect(packageSO.category).toBeUndefined();
|
|
68
|
+
expect(packageSO.validated).toBeUndefined();
|
|
69
|
+
});
|
|
70
|
+
it("should handle null description", async () => {
|
|
71
|
+
// Arrange
|
|
72
|
+
const packageName = "@test/package";
|
|
73
|
+
const mockConfig = {
|
|
74
|
+
type: "mcp-server",
|
|
75
|
+
runtime: "node",
|
|
76
|
+
packageName,
|
|
77
|
+
name: "Test Package",
|
|
78
|
+
description: null,
|
|
79
|
+
};
|
|
80
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
81
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
82
|
+
// Act
|
|
83
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
84
|
+
// Assert
|
|
85
|
+
expect(packageSO.description).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("getTools", () => {
|
|
89
|
+
it("should successfully return tools list", async () => {
|
|
90
|
+
// Arrange
|
|
91
|
+
const packageName = "@test/package";
|
|
92
|
+
const mockConfig = {
|
|
93
|
+
type: "mcp-server",
|
|
94
|
+
runtime: "node",
|
|
95
|
+
packageName,
|
|
96
|
+
name: "Test Package",
|
|
97
|
+
description: "Test description",
|
|
98
|
+
};
|
|
99
|
+
const mockTools = [
|
|
100
|
+
{
|
|
101
|
+
name: "read_file",
|
|
102
|
+
description: "Read a file",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: {
|
|
106
|
+
path: { type: "string" },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "write_file",
|
|
112
|
+
description: "Write a file",
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
path: { type: "string" },
|
|
117
|
+
content: { type: "string" },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
123
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
124
|
+
vi.spyOn(mockExecutor, "listTools").mockResolvedValue(mockTools);
|
|
125
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
126
|
+
// Act
|
|
127
|
+
const tools = await packageSO.getTools();
|
|
128
|
+
// Assert
|
|
129
|
+
expect(tools).toEqual(mockTools);
|
|
130
|
+
expect(tools).toHaveLength(2);
|
|
131
|
+
expect(mockExecutor.listTools).toHaveBeenCalledWith(packageName);
|
|
132
|
+
expect(mockExecutor.listTools).toHaveBeenCalledTimes(1);
|
|
133
|
+
});
|
|
134
|
+
it("should throw error when executor fails", async () => {
|
|
135
|
+
// Arrange
|
|
136
|
+
const packageName = "@test/package";
|
|
137
|
+
const mockConfig = {
|
|
138
|
+
type: "mcp-server",
|
|
139
|
+
runtime: "node",
|
|
140
|
+
packageName,
|
|
141
|
+
name: "Test Package",
|
|
142
|
+
description: "Test description",
|
|
143
|
+
};
|
|
144
|
+
const errorMessage = "Failed to list tools";
|
|
145
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
146
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
147
|
+
vi.spyOn(mockExecutor, "listTools").mockRejectedValue(new Error(errorMessage));
|
|
148
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
149
|
+
// Act & Assert
|
|
150
|
+
await expect(packageSO.getTools()).rejects.toThrow(errorMessage);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe("executeTool", () => {
|
|
154
|
+
it("should successfully execute tool", async () => {
|
|
155
|
+
// Arrange
|
|
156
|
+
const packageName = "@test/package";
|
|
157
|
+
const toolKey = "read_file";
|
|
158
|
+
const inputData = { path: "/tmp/test.txt" };
|
|
159
|
+
const envs = { ENV_VAR: "test_value" };
|
|
160
|
+
const mockResult = { content: "file content", success: true };
|
|
161
|
+
const mockConfig = {
|
|
162
|
+
type: "mcp-server",
|
|
163
|
+
runtime: "node",
|
|
164
|
+
packageName,
|
|
165
|
+
name: "Test Package",
|
|
166
|
+
description: "Test description",
|
|
167
|
+
};
|
|
168
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
169
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
170
|
+
vi.spyOn(mockExecutor, "executeTool").mockResolvedValue(mockResult);
|
|
171
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
172
|
+
// Act
|
|
173
|
+
const result = await packageSO.executeTool(toolKey, inputData, envs);
|
|
174
|
+
// Assert
|
|
175
|
+
expect(result).toEqual(mockResult);
|
|
176
|
+
expect(mockExecutor.executeTool).toHaveBeenCalledWith({
|
|
177
|
+
packageName,
|
|
178
|
+
toolKey,
|
|
179
|
+
inputData,
|
|
180
|
+
envs,
|
|
181
|
+
});
|
|
182
|
+
expect(mockExecutor.executeTool).toHaveBeenCalledTimes(1);
|
|
183
|
+
});
|
|
184
|
+
it("should execute normally without envs parameter", async () => {
|
|
185
|
+
// Arrange
|
|
186
|
+
const packageName = "@test/package";
|
|
187
|
+
const toolKey = "read_file";
|
|
188
|
+
const inputData = { path: "/tmp/test.txt" };
|
|
189
|
+
const mockResult = { content: "file content" };
|
|
190
|
+
const mockConfig = {
|
|
191
|
+
type: "mcp-server",
|
|
192
|
+
runtime: "node",
|
|
193
|
+
packageName,
|
|
194
|
+
name: "Test Package",
|
|
195
|
+
description: "Test description",
|
|
196
|
+
};
|
|
197
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
198
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
199
|
+
vi.spyOn(mockExecutor, "executeTool").mockResolvedValue(mockResult);
|
|
200
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
201
|
+
// Act
|
|
202
|
+
const result = await packageSO.executeTool(toolKey, inputData);
|
|
203
|
+
// Assert
|
|
204
|
+
expect(result).toEqual(mockResult);
|
|
205
|
+
expect(mockExecutor.executeTool).toHaveBeenCalledWith({
|
|
206
|
+
packageName,
|
|
207
|
+
toolKey,
|
|
208
|
+
inputData,
|
|
209
|
+
envs: undefined,
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
it("should throw error when tool execution fails", async () => {
|
|
213
|
+
// Arrange
|
|
214
|
+
const packageName = "@test/package";
|
|
215
|
+
const toolKey = "unknown_tool";
|
|
216
|
+
const inputData = { param: "value" };
|
|
217
|
+
const errorMessage = "Unknown tool: unknown_tool";
|
|
218
|
+
const mockConfig = {
|
|
219
|
+
type: "mcp-server",
|
|
220
|
+
runtime: "node",
|
|
221
|
+
packageName,
|
|
222
|
+
name: "Test Package",
|
|
223
|
+
description: "Test description",
|
|
224
|
+
};
|
|
225
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
226
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
227
|
+
vi.spyOn(mockExecutor, "executeTool").mockRejectedValue(new Error(errorMessage));
|
|
228
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
229
|
+
// Act & Assert
|
|
230
|
+
await expect(packageSO.executeTool(toolKey, inputData)).rejects.toThrow(errorMessage);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe("getDetailWithTools", () => {
|
|
234
|
+
it("should return complete package details with tools list", async () => {
|
|
235
|
+
// Arrange
|
|
236
|
+
const packageName = "@test/package";
|
|
237
|
+
const mockConfig = {
|
|
238
|
+
type: "mcp-server",
|
|
239
|
+
runtime: "node",
|
|
240
|
+
packageName,
|
|
241
|
+
name: "Test Package",
|
|
242
|
+
description: "A test package for demonstration",
|
|
243
|
+
};
|
|
244
|
+
const mockPackageInfo = {
|
|
245
|
+
path: "path/to/package",
|
|
246
|
+
category: "testing",
|
|
247
|
+
validated: true,
|
|
248
|
+
};
|
|
249
|
+
const mockTools = [
|
|
250
|
+
{
|
|
251
|
+
name: "test_tool",
|
|
252
|
+
description: "A test tool",
|
|
253
|
+
inputSchema: {
|
|
254
|
+
type: "object",
|
|
255
|
+
properties: {},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
260
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({
|
|
261
|
+
[packageName]: mockPackageInfo,
|
|
262
|
+
});
|
|
263
|
+
vi.spyOn(mockExecutor, "listTools").mockResolvedValue(mockTools);
|
|
264
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
265
|
+
// Act
|
|
266
|
+
const detail = await packageSO.getDetailWithTools();
|
|
267
|
+
// Assert
|
|
268
|
+
expect(detail).toEqual({
|
|
269
|
+
name: "Test Package",
|
|
270
|
+
packageName: "@test/package",
|
|
271
|
+
description: "A test package for demonstration",
|
|
272
|
+
category: "testing",
|
|
273
|
+
validated: true,
|
|
274
|
+
tools: mockTools,
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
it("should return undefined tools when getting tools fails", async () => {
|
|
278
|
+
// Arrange
|
|
279
|
+
const packageName = "@test/package";
|
|
280
|
+
const mockConfig = {
|
|
281
|
+
type: "mcp-server",
|
|
282
|
+
runtime: "node",
|
|
283
|
+
packageName,
|
|
284
|
+
name: "Test Package",
|
|
285
|
+
description: "Test description",
|
|
286
|
+
};
|
|
287
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
288
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
289
|
+
vi.spyOn(mockExecutor, "listTools").mockRejectedValue(new Error("Failed to get tools"));
|
|
290
|
+
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
291
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
292
|
+
// Act
|
|
293
|
+
const detail = await packageSO.getDetailWithTools();
|
|
294
|
+
// Assert
|
|
295
|
+
expect(detail.tools).toBeUndefined();
|
|
296
|
+
expect(detail.name).toBe("Test Package");
|
|
297
|
+
expect(detail.packageName).toBe(packageName);
|
|
298
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining(`[PackageSO] Failed to get tools for ${packageName}`), expect.any(Error));
|
|
299
|
+
consoleWarnSpy.mockRestore();
|
|
300
|
+
});
|
|
301
|
+
it("should correctly handle packages without category and validated", async () => {
|
|
302
|
+
// Arrange
|
|
303
|
+
const packageName = "@test/minimal-package";
|
|
304
|
+
const mockConfig = {
|
|
305
|
+
type: "mcp-server",
|
|
306
|
+
runtime: "node",
|
|
307
|
+
packageName,
|
|
308
|
+
name: "Minimal Package",
|
|
309
|
+
description: "A minimal package",
|
|
310
|
+
};
|
|
311
|
+
const mockTools = [];
|
|
312
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
313
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
314
|
+
vi.spyOn(mockExecutor, "listTools").mockResolvedValue(mockTools);
|
|
315
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
316
|
+
// Act
|
|
317
|
+
const detail = await packageSO.getDetailWithTools();
|
|
318
|
+
// Assert
|
|
319
|
+
expect(detail).toEqual({
|
|
320
|
+
name: "Minimal Package",
|
|
321
|
+
packageName: "@test/minimal-package",
|
|
322
|
+
description: "A minimal package",
|
|
323
|
+
category: undefined,
|
|
324
|
+
validated: undefined,
|
|
325
|
+
tools: mockTools,
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
describe("Getters", () => {
|
|
330
|
+
it("should correctly access all properties through getters", async () => {
|
|
331
|
+
// Arrange
|
|
332
|
+
const packageName = "@test/package";
|
|
333
|
+
const mockConfig = {
|
|
334
|
+
type: "mcp-server",
|
|
335
|
+
runtime: "node",
|
|
336
|
+
packageName,
|
|
337
|
+
name: "Test Package",
|
|
338
|
+
description: "Test description",
|
|
339
|
+
};
|
|
340
|
+
const mockPackageInfo = {
|
|
341
|
+
path: "path/to/package",
|
|
342
|
+
category: "test-category",
|
|
343
|
+
validated: false,
|
|
344
|
+
};
|
|
345
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
346
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({
|
|
347
|
+
[packageName]: mockPackageInfo,
|
|
348
|
+
});
|
|
349
|
+
// Act
|
|
350
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
351
|
+
// Assert
|
|
352
|
+
expect(packageSO.packageName).toBe(packageName);
|
|
353
|
+
expect(packageSO.name).toBe("Test Package");
|
|
354
|
+
expect(packageSO.description).toBe("Test description");
|
|
355
|
+
expect(packageSO.category).toBe("test-category");
|
|
356
|
+
expect(packageSO.validated).toBe(false);
|
|
357
|
+
expect(packageSO.config).toEqual(mockConfig);
|
|
358
|
+
});
|
|
359
|
+
it("should return complete config object through config getter", async () => {
|
|
360
|
+
// Arrange
|
|
361
|
+
const packageName = "@test/package";
|
|
362
|
+
const mockConfig = {
|
|
363
|
+
type: "mcp-server",
|
|
364
|
+
runtime: "node",
|
|
365
|
+
packageName,
|
|
366
|
+
name: "Test Package",
|
|
367
|
+
description: "Test description",
|
|
368
|
+
};
|
|
369
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
370
|
+
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
371
|
+
// Act
|
|
372
|
+
const packageSO = await PackageSO.init(packageName, mockRepository, mockExecutor);
|
|
373
|
+
// Assert
|
|
374
|
+
expect(packageSO.config).toBe(mockConfig);
|
|
375
|
+
expect(packageSO.config).toEqual(mockConfig);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import type { MCPServerPackageConfigSchema, PackageConfigSchema, PackagesListSchema } from "../../shared/schemas";
|
|
4
|
+
export type MCPServerPackageConfig = z.infer<typeof MCPServerPackageConfigSchema>;
|
|
5
|
+
export type PackageConfig = z.infer<typeof PackageConfigSchema>;
|
|
6
|
+
export type PackagesList = z.infer<typeof PackagesListSchema>;
|
|
7
|
+
export interface MCPServerPackageConfigWithTools extends MCPServerPackageConfig {
|
|
8
|
+
tools?: Tool[];
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import type { SandboxClient } from "../sandbox-types";
|
|
3
|
+
/**
|
|
4
|
+
* Daytona Sandbox Client
|
|
5
|
+
* Implements SandboxClient interface for Daytona provider
|
|
6
|
+
*/
|
|
7
|
+
export declare class DaytonaSandboxClient implements SandboxClient {
|
|
8
|
+
private sandbox;
|
|
9
|
+
private initializing;
|
|
10
|
+
private readonly packageRepository;
|
|
11
|
+
constructor(_runtime?: "node" | "python" | "java" | "go");
|
|
12
|
+
initialize(): Promise<void>;
|
|
13
|
+
private executeCode;
|
|
14
|
+
listTools(packageKey: string): Promise<Tool[]>;
|
|
15
|
+
executeTool(packageKey: string, toolName: string, argumentsObj: Record<string, unknown>, envs?: Record<string, string>): Promise<unknown>;
|
|
16
|
+
destroy(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { Daytona, Image } from "@daytonaio/sdk";
|
|
3
|
+
import { getDaytonaConfig } from "../../../shared/config/environment";
|
|
4
|
+
import { getDirname } from "../../../shared/utils/file-util";
|
|
5
|
+
import { extractLastOuterJSON } from "../../../shared/utils/string-util";
|
|
6
|
+
import { PackageRepository } from "../../package/package-repository";
|
|
7
|
+
import { generateMCPTestCode } from "../sandbox-utils";
|
|
8
|
+
/**
|
|
9
|
+
* Daytona Sandbox Client
|
|
10
|
+
* Implements SandboxClient interface for Daytona provider
|
|
11
|
+
*/
|
|
12
|
+
export class DaytonaSandboxClient {
|
|
13
|
+
constructor(_runtime = "node") {
|
|
14
|
+
this.sandbox = null;
|
|
15
|
+
this.initializing = null;
|
|
16
|
+
const __dirname = getDirname(import.meta.url);
|
|
17
|
+
const packagesDir = path.join(__dirname, "../../../../packages");
|
|
18
|
+
this.packageRepository = new PackageRepository(packagesDir);
|
|
19
|
+
}
|
|
20
|
+
async initialize() {
|
|
21
|
+
if (this.sandbox) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (this.initializing) {
|
|
25
|
+
await this.initializing;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.initializing = (async () => {
|
|
29
|
+
try {
|
|
30
|
+
const config = getDaytonaConfig();
|
|
31
|
+
const daytonaConfig = {
|
|
32
|
+
apiKey: config.apiKey,
|
|
33
|
+
};
|
|
34
|
+
if (config.apiUrl) {
|
|
35
|
+
daytonaConfig.apiUrl = config.apiUrl;
|
|
36
|
+
}
|
|
37
|
+
const daytona = new Daytona(daytonaConfig);
|
|
38
|
+
const declarativeImage = Image.base("node:20")
|
|
39
|
+
.runCommands("npm install -g pnpm", "mkdir -p /workspace", "cd /workspace && npm init -y", "cd /workspace && pnpm add @modelcontextprotocol/sdk")
|
|
40
|
+
.workdir("/workspace");
|
|
41
|
+
this.sandbox = await daytona.create({
|
|
42
|
+
language: "javascript",
|
|
43
|
+
image: declarativeImage,
|
|
44
|
+
autoDeleteInterval: 0,
|
|
45
|
+
});
|
|
46
|
+
console.log("[DaytonaSandboxClient] Sandbox created successfully");
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
this.initializing = null;
|
|
50
|
+
}
|
|
51
|
+
})();
|
|
52
|
+
await this.initializing;
|
|
53
|
+
}
|
|
54
|
+
async executeCode(code) {
|
|
55
|
+
if (!this.sandbox) {
|
|
56
|
+
throw new Error("Sandbox not initialized. Call initialize() first.");
|
|
57
|
+
}
|
|
58
|
+
const response = await this.sandbox.process.codeRun(code);
|
|
59
|
+
return {
|
|
60
|
+
exitCode: response.exitCode,
|
|
61
|
+
result: response.result,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async listTools(packageKey) {
|
|
65
|
+
const mcpServerConfig = this.packageRepository.getPackageConfig(packageKey);
|
|
66
|
+
const testCode = generateMCPTestCode(mcpServerConfig, "listTools");
|
|
67
|
+
const response = await this.executeCode(testCode);
|
|
68
|
+
if (response.exitCode !== 0) {
|
|
69
|
+
throw new Error(`Failed to list tools: ${response.result}`);
|
|
70
|
+
}
|
|
71
|
+
const parsedResultStr = extractLastOuterJSON(response.result);
|
|
72
|
+
const result = JSON.parse(parsedResultStr);
|
|
73
|
+
return result.tools;
|
|
74
|
+
}
|
|
75
|
+
async executeTool(packageKey, toolName, argumentsObj, envs) {
|
|
76
|
+
const mcpServerConfig = this.packageRepository.getPackageConfig(packageKey);
|
|
77
|
+
const testCode = generateMCPTestCode(mcpServerConfig, "executeTool", toolName, argumentsObj, envs);
|
|
78
|
+
const response = await this.executeCode(testCode);
|
|
79
|
+
if (response.exitCode !== 0) {
|
|
80
|
+
throw new Error(`Failed to execute tool: ${response.result}`);
|
|
81
|
+
}
|
|
82
|
+
const parsedResultStr = extractLastOuterJSON(response.result);
|
|
83
|
+
const result = JSON.parse(parsedResultStr);
|
|
84
|
+
if (result.isError) {
|
|
85
|
+
console.error("[DaytonaSandboxClient] Tool execution error:", result.errorMessage);
|
|
86
|
+
throw new Error(result.errorMessage);
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
async destroy() {
|
|
91
|
+
if (!this.sandbox) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const sandboxToDelete = this.sandbox;
|
|
95
|
+
this.sandbox = null; // Clear immediately to avoid duplicate calls
|
|
96
|
+
// Asynchronously clean up sandbox without blocking result return
|
|
97
|
+
sandboxToDelete
|
|
98
|
+
.delete()
|
|
99
|
+
.then(() => {
|
|
100
|
+
console.log("[DaytonaSandboxClient] Sandbox destroyed successfully");
|
|
101
|
+
})
|
|
102
|
+
.catch((err) => {
|
|
103
|
+
const errorMessage = err.message;
|
|
104
|
+
if (errorMessage.includes("not found")) {
|
|
105
|
+
console.log("[DaytonaSandboxClient] Sandbox already destroyed (not found on platform)");
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.warn("[DaytonaSandboxClient] Warning: Could not destroy sandbox:", errorMessage);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import type { SandboxClient } from "../sandbox-types";
|
|
3
|
+
/**
|
|
4
|
+
* Sandock Sandbox Client
|
|
5
|
+
* Implements SandboxClient interface for Sandock provider
|
|
6
|
+
*/
|
|
7
|
+
export declare class SandockSandboxClient implements SandboxClient {
|
|
8
|
+
private sandboxId;
|
|
9
|
+
private initializing;
|
|
10
|
+
private readonly packageRepository;
|
|
11
|
+
private readonly client;
|
|
12
|
+
constructor(_runtime?: "node" | "python" | "java" | "go");
|
|
13
|
+
initialize(): Promise<void>;
|
|
14
|
+
private executeShellCommand;
|
|
15
|
+
private executeCode;
|
|
16
|
+
listTools(packageKey: string): Promise<Tool[]>;
|
|
17
|
+
executeTool(packageKey: string, toolName: string, argumentsObj: Record<string, unknown>, envs?: Record<string, string>): Promise<unknown>;
|
|
18
|
+
destroy(): Promise<void>;
|
|
19
|
+
}
|