@toolsdk.ai/registry 1.0.124 → 1.0.126
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/index.js +4 -0
- package/dist/domains/package/package-handler.d.ts +2 -0
- package/dist/domains/package/package-handler.js +1 -1
- package/dist/domains/package/package-so.js +10 -3
- package/dist/domains/package/package-so.test.js +24 -5
- package/dist/domains/registry/__tests__/federated-registry-provider.test.d.ts +1 -0
- package/dist/domains/registry/__tests__/federated-registry-provider.test.js +125 -0
- package/dist/domains/registry/__tests__/local-registry-provider.test.d.ts +1 -0
- package/dist/domains/registry/__tests__/local-registry-provider.test.js +80 -0
- package/dist/domains/registry/__tests__/official-registry-provider.integration.test.d.ts +1 -0
- package/dist/domains/registry/__tests__/official-registry-provider.integration.test.js +70 -0
- package/dist/domains/registry/__tests__/registry-utils.test.d.ts +1 -0
- package/dist/domains/registry/__tests__/registry-utils.test.js +307 -0
- package/dist/domains/registry/index.d.ts +8 -0
- package/dist/domains/registry/index.js +9 -0
- package/dist/domains/registry/providers/federated-registry-provider.d.ts +25 -0
- package/dist/domains/registry/providers/federated-registry-provider.js +49 -0
- package/dist/domains/registry/providers/local-registry-provider.d.ts +23 -0
- package/dist/domains/registry/providers/local-registry-provider.js +35 -0
- package/dist/domains/registry/providers/official-registry-provider.d.ts +34 -0
- package/dist/domains/registry/providers/official-registry-provider.js +83 -0
- package/dist/domains/registry/registry-factory.d.ts +21 -0
- package/dist/domains/registry/registry-factory.js +52 -0
- package/dist/domains/registry/registry-schema.d.ts +586 -0
- package/dist/domains/registry/registry-schema.js +39 -0
- package/dist/domains/registry/registry-types.d.ts +23 -0
- package/dist/domains/registry/registry-types.js +1 -0
- package/dist/domains/registry/registry-utils.d.ts +14 -0
- package/dist/domains/registry/registry-utils.js +50 -0
- package/indexes/packages-list.json +1 -1
- package/package.json +2 -2
package/dist/api/index.js
CHANGED
|
@@ -4,11 +4,15 @@ import { serve } from "@hono/node-server";
|
|
|
4
4
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
5
5
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
6
6
|
import { configRoutes } from "../domains/config/config-route";
|
|
7
|
+
import { repository } from "../domains/package/package-handler";
|
|
7
8
|
import { packageRoutes } from "../domains/package/package-route";
|
|
9
|
+
import { initRegistryFactory } from "../domains/registry/registry-factory";
|
|
8
10
|
import { searchRoutes } from "../domains/search/search-route";
|
|
9
11
|
import { SearchSO } from "../domains/search/search-so";
|
|
10
12
|
import { getServerPort, isSearchEnabled } from "../shared/config/environment";
|
|
11
13
|
import { getDirname } from "../shared/utils";
|
|
14
|
+
// Initialize Registry Factory with the local repository
|
|
15
|
+
initRegistryFactory(repository);
|
|
12
16
|
const initializeSearchService = async () => {
|
|
13
17
|
try {
|
|
14
18
|
await SearchSO.getInstance();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { MCPSandboxProvider } from "../sandbox/sandbox-types";
|
|
2
|
+
import { PackageRepository } from "./package-repository";
|
|
3
|
+
export declare const repository: PackageRepository;
|
|
2
4
|
export declare const packageHandler: {
|
|
3
5
|
getPackageDetail: (packageName: string, sandboxProvider?: MCPSandboxProvider) => Promise<{
|
|
4
6
|
success: boolean;
|
|
@@ -6,7 +6,7 @@ import { PackageRepository } from "./package-repository";
|
|
|
6
6
|
import { PackageSO } from "./package-so";
|
|
7
7
|
const __dirname = getDirname(import.meta.url);
|
|
8
8
|
const packagesDir = path.join(__dirname, "../../../packages");
|
|
9
|
-
const repository = new PackageRepository(packagesDir);
|
|
9
|
+
export const repository = new PackageRepository(packagesDir);
|
|
10
10
|
export const packageHandler = {
|
|
11
11
|
getPackageDetail: async (packageName, sandboxProvider) => {
|
|
12
12
|
try {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { getRegistryProvider } from "../registry/registry-factory";
|
|
1
2
|
export class PackageSO {
|
|
2
|
-
constructor(_packageName, _config, _packageInfo,
|
|
3
|
+
constructor(_packageName, _config, _packageInfo, _executor) {
|
|
3
4
|
this._packageName = _packageName;
|
|
4
5
|
this._config = _config;
|
|
5
6
|
this._packageInfo = _packageInfo;
|
|
@@ -24,10 +25,16 @@ export class PackageSO {
|
|
|
24
25
|
return this._config;
|
|
25
26
|
}
|
|
26
27
|
static async init(packageName, repository, executor) {
|
|
27
|
-
|
|
28
|
+
// Use FederatedRegistryProvider, which checks for local packages first and falls back to the official registry if not found locally.
|
|
29
|
+
const provider = getRegistryProvider("FEDERATED");
|
|
30
|
+
const config = await provider.getPackageConfig(packageName);
|
|
31
|
+
if (!config) {
|
|
32
|
+
throw new Error(`Package '${packageName}' not found`);
|
|
33
|
+
}
|
|
34
|
+
// Get package metadata (category, validated) from local repository index if available.
|
|
28
35
|
const allPackages = repository.getAllPackages();
|
|
29
36
|
const packageInfo = allPackages[packageName] || {};
|
|
30
|
-
return new PackageSO(packageName, config, packageInfo,
|
|
37
|
+
return new PackageSO(packageName, config, packageInfo, executor);
|
|
31
38
|
}
|
|
32
39
|
async getTools() {
|
|
33
40
|
return await this._executor.listTools(this.packageName);
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { initRegistryFactory, resetRegistryFactory } from "../registry/registry-factory";
|
|
2
3
|
import { PackageSO } from "./package-so";
|
|
3
4
|
describe("PackageSO", () => {
|
|
4
5
|
let mockRepository;
|
|
5
6
|
let mockExecutor;
|
|
6
7
|
beforeEach(() => {
|
|
8
|
+
// Reset factory before each test
|
|
9
|
+
resetRegistryFactory();
|
|
7
10
|
// Mock PackageRepository
|
|
8
11
|
mockRepository = {
|
|
9
12
|
getPackageConfig: vi.fn(),
|
|
10
13
|
getAllPackages: vi.fn(),
|
|
11
14
|
exists: vi.fn(),
|
|
12
15
|
};
|
|
16
|
+
// Initialize Registry Factory with mock repository
|
|
17
|
+
initRegistryFactory(mockRepository);
|
|
13
18
|
// Mock ToolExecutor
|
|
14
19
|
mockExecutor = {
|
|
15
20
|
listTools: vi.fn(),
|
|
@@ -32,6 +37,7 @@ describe("PackageSO", () => {
|
|
|
32
37
|
category: "filesystem",
|
|
33
38
|
validated: true,
|
|
34
39
|
};
|
|
40
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
35
41
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
36
42
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({
|
|
37
43
|
[packageName]: mockPackageInfo,
|
|
@@ -44,6 +50,7 @@ describe("PackageSO", () => {
|
|
|
44
50
|
expect(packageSO.description).toBe("A server for filesystem operations");
|
|
45
51
|
expect(packageSO.category).toBe("filesystem");
|
|
46
52
|
expect(packageSO.validated).toBe(true);
|
|
53
|
+
expect(mockRepository.exists).toHaveBeenCalledWith(packageName);
|
|
47
54
|
expect(mockRepository.getPackageConfig).toHaveBeenCalledWith(packageName);
|
|
48
55
|
expect(mockRepository.getAllPackages).toHaveBeenCalled();
|
|
49
56
|
});
|
|
@@ -57,6 +64,7 @@ describe("PackageSO", () => {
|
|
|
57
64
|
name: "New Package",
|
|
58
65
|
description: "A new package",
|
|
59
66
|
};
|
|
67
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
60
68
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
61
69
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
62
70
|
// Act
|
|
@@ -77,6 +85,7 @@ describe("PackageSO", () => {
|
|
|
77
85
|
name: "Test Package",
|
|
78
86
|
description: null,
|
|
79
87
|
};
|
|
88
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
80
89
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
81
90
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
82
91
|
// Act
|
|
@@ -119,6 +128,7 @@ describe("PackageSO", () => {
|
|
|
119
128
|
},
|
|
120
129
|
},
|
|
121
130
|
];
|
|
131
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
122
132
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
123
133
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
124
134
|
vi.spyOn(mockExecutor, "listTools").mockResolvedValue(mockTools);
|
|
@@ -142,6 +152,7 @@ describe("PackageSO", () => {
|
|
|
142
152
|
description: "Test description",
|
|
143
153
|
};
|
|
144
154
|
const errorMessage = "Failed to list tools";
|
|
155
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
145
156
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
146
157
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
147
158
|
vi.spyOn(mockExecutor, "listTools").mockRejectedValue(new Error(errorMessage));
|
|
@@ -165,6 +176,7 @@ describe("PackageSO", () => {
|
|
|
165
176
|
name: "Test Package",
|
|
166
177
|
description: "Test description",
|
|
167
178
|
};
|
|
179
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
168
180
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
169
181
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
170
182
|
vi.spyOn(mockExecutor, "executeTool").mockResolvedValue(mockResult);
|
|
@@ -194,6 +206,7 @@ describe("PackageSO", () => {
|
|
|
194
206
|
name: "Test Package",
|
|
195
207
|
description: "Test description",
|
|
196
208
|
};
|
|
209
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
197
210
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
198
211
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
199
212
|
vi.spyOn(mockExecutor, "executeTool").mockResolvedValue(mockResult);
|
|
@@ -222,6 +235,7 @@ describe("PackageSO", () => {
|
|
|
222
235
|
name: "Test Package",
|
|
223
236
|
description: "Test description",
|
|
224
237
|
};
|
|
238
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
225
239
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
226
240
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
227
241
|
vi.spyOn(mockExecutor, "executeTool").mockRejectedValue(new Error(errorMessage));
|
|
@@ -256,6 +270,7 @@ describe("PackageSO", () => {
|
|
|
256
270
|
},
|
|
257
271
|
},
|
|
258
272
|
];
|
|
273
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
259
274
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
260
275
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({
|
|
261
276
|
[packageName]: mockPackageInfo,
|
|
@@ -266,11 +281,11 @@ describe("PackageSO", () => {
|
|
|
266
281
|
const detail = await packageSO.getDetailWithTools();
|
|
267
282
|
// Assert
|
|
268
283
|
expect(detail).toEqual({
|
|
284
|
+
type: "mcp-server",
|
|
285
|
+
runtime: "node",
|
|
269
286
|
name: "Test Package",
|
|
270
287
|
packageName: "@test/package",
|
|
271
288
|
description: "A test package for demonstration",
|
|
272
|
-
category: "testing",
|
|
273
|
-
validated: true,
|
|
274
289
|
tools: mockTools,
|
|
275
290
|
});
|
|
276
291
|
});
|
|
@@ -284,6 +299,7 @@ describe("PackageSO", () => {
|
|
|
284
299
|
name: "Test Package",
|
|
285
300
|
description: "Test description",
|
|
286
301
|
};
|
|
302
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
287
303
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
288
304
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
289
305
|
vi.spyOn(mockExecutor, "listTools").mockRejectedValue(new Error("Failed to get tools"));
|
|
@@ -309,6 +325,7 @@ describe("PackageSO", () => {
|
|
|
309
325
|
description: "A minimal package",
|
|
310
326
|
};
|
|
311
327
|
const mockTools = [];
|
|
328
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
312
329
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
313
330
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
314
331
|
vi.spyOn(mockExecutor, "listTools").mockResolvedValue(mockTools);
|
|
@@ -317,12 +334,12 @@ describe("PackageSO", () => {
|
|
|
317
334
|
const detail = await packageSO.getDetailWithTools();
|
|
318
335
|
// Assert
|
|
319
336
|
expect(detail).toEqual({
|
|
337
|
+
type: "mcp-server",
|
|
338
|
+
runtime: "node",
|
|
320
339
|
name: "Minimal Package",
|
|
321
340
|
packageName: "@test/minimal-package",
|
|
322
341
|
description: "A minimal package",
|
|
323
|
-
|
|
324
|
-
validated: undefined,
|
|
325
|
-
tools: mockTools,
|
|
342
|
+
tools: [],
|
|
326
343
|
});
|
|
327
344
|
});
|
|
328
345
|
});
|
|
@@ -342,6 +359,7 @@ describe("PackageSO", () => {
|
|
|
342
359
|
category: "test-category",
|
|
343
360
|
validated: false,
|
|
344
361
|
};
|
|
362
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
345
363
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
346
364
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({
|
|
347
365
|
[packageName]: mockPackageInfo,
|
|
@@ -366,6 +384,7 @@ describe("PackageSO", () => {
|
|
|
366
384
|
name: "Test Package",
|
|
367
385
|
description: "Test description",
|
|
368
386
|
};
|
|
387
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
369
388
|
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
370
389
|
vi.spyOn(mockRepository, "getAllPackages").mockReturnValue({});
|
|
371
390
|
// Act
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { FederatedRegistryProvider } from "../providers/federated-registry-provider";
|
|
3
|
+
describe("FederatedRegistryProvider", () => {
|
|
4
|
+
let mockLocalProvider;
|
|
5
|
+
let mockOfficialProvider;
|
|
6
|
+
let provider;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
// Mock LocalRegistryProvider
|
|
9
|
+
mockLocalProvider = {
|
|
10
|
+
getPackageConfig: vi.fn(),
|
|
11
|
+
exists: vi.fn(),
|
|
12
|
+
};
|
|
13
|
+
// Mock OfficialRegistryProvider
|
|
14
|
+
mockOfficialProvider = {
|
|
15
|
+
getPackageConfig: vi.fn(),
|
|
16
|
+
exists: vi.fn(),
|
|
17
|
+
search: vi.fn(),
|
|
18
|
+
};
|
|
19
|
+
provider = new FederatedRegistryProvider(mockLocalProvider, mockOfficialProvider);
|
|
20
|
+
});
|
|
21
|
+
describe("getPackageConfig", () => {
|
|
22
|
+
it("should return local config when package exists locally", async () => {
|
|
23
|
+
// Arrange
|
|
24
|
+
const packageName = "@modelcontextprotocol/server-filesystem";
|
|
25
|
+
const mockConfig = {
|
|
26
|
+
type: "mcp-server",
|
|
27
|
+
runtime: "node",
|
|
28
|
+
packageName,
|
|
29
|
+
name: "Filesystem Server",
|
|
30
|
+
description: "A server for filesystem operations",
|
|
31
|
+
};
|
|
32
|
+
vi.spyOn(mockLocalProvider, "getPackageConfig").mockResolvedValue(mockConfig);
|
|
33
|
+
// Act
|
|
34
|
+
const result = await provider.getPackageConfig(packageName);
|
|
35
|
+
// Assert
|
|
36
|
+
expect(result).toEqual(mockConfig);
|
|
37
|
+
expect(mockLocalProvider.getPackageConfig).toHaveBeenCalledWith(packageName);
|
|
38
|
+
expect(mockOfficialProvider.getPackageConfig).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
it("should query official provider when local returns null", async () => {
|
|
41
|
+
// Arrange
|
|
42
|
+
const packageName = "@toolsdk.ai/tavily-mcp";
|
|
43
|
+
const officialConfig = {
|
|
44
|
+
type: "mcp-server",
|
|
45
|
+
runtime: "node",
|
|
46
|
+
packageName,
|
|
47
|
+
name: "Tavily MCP Server",
|
|
48
|
+
description: "MCP server for Tavily search",
|
|
49
|
+
};
|
|
50
|
+
vi.spyOn(mockLocalProvider, "getPackageConfig").mockResolvedValue(null);
|
|
51
|
+
vi.spyOn(mockOfficialProvider, "getPackageConfig").mockResolvedValue(officialConfig);
|
|
52
|
+
// Act
|
|
53
|
+
const result = await provider.getPackageConfig(packageName);
|
|
54
|
+
// Assert
|
|
55
|
+
expect(result).toEqual(officialConfig);
|
|
56
|
+
expect(mockLocalProvider.getPackageConfig).toHaveBeenCalledWith(packageName);
|
|
57
|
+
expect(mockOfficialProvider.getPackageConfig).toHaveBeenCalledWith(packageName);
|
|
58
|
+
});
|
|
59
|
+
it("should return null when both providers return null", async () => {
|
|
60
|
+
// Arrange
|
|
61
|
+
const packageName = "non-existent-package";
|
|
62
|
+
vi.spyOn(mockLocalProvider, "getPackageConfig").mockResolvedValue(null);
|
|
63
|
+
vi.spyOn(mockOfficialProvider, "getPackageConfig").mockResolvedValue(null);
|
|
64
|
+
// Act
|
|
65
|
+
const result = await provider.getPackageConfig(packageName);
|
|
66
|
+
// Assert
|
|
67
|
+
expect(result).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
it("should return null when official provider throws error", async () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
const packageName = "@toolsdk.ai/tavily-mcp";
|
|
72
|
+
vi.spyOn(mockLocalProvider, "getPackageConfig").mockResolvedValue(null);
|
|
73
|
+
vi.spyOn(mockOfficialProvider, "getPackageConfig").mockRejectedValue(new Error("Network error"));
|
|
74
|
+
// Act
|
|
75
|
+
const result = await provider.getPackageConfig(packageName);
|
|
76
|
+
// Assert
|
|
77
|
+
expect(result).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("exists", () => {
|
|
81
|
+
it("should return true when package exists locally", async () => {
|
|
82
|
+
// Arrange
|
|
83
|
+
const packageName = "@modelcontextprotocol/server-filesystem";
|
|
84
|
+
vi.spyOn(mockLocalProvider, "exists").mockResolvedValue(true);
|
|
85
|
+
// Act
|
|
86
|
+
const result = await provider.exists(packageName);
|
|
87
|
+
// Assert
|
|
88
|
+
expect(result).toBe(true);
|
|
89
|
+
expect(mockLocalProvider.exists).toHaveBeenCalledWith(packageName);
|
|
90
|
+
expect(mockOfficialProvider.exists).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
it("should check official provider when local returns false", async () => {
|
|
93
|
+
// Arrange
|
|
94
|
+
const packageName = "@toolsdk.ai/tavily-mcp";
|
|
95
|
+
vi.spyOn(mockLocalProvider, "exists").mockResolvedValue(false);
|
|
96
|
+
vi.spyOn(mockOfficialProvider, "exists").mockResolvedValue(true);
|
|
97
|
+
// Act
|
|
98
|
+
const result = await provider.exists(packageName);
|
|
99
|
+
// Assert
|
|
100
|
+
expect(result).toBe(true);
|
|
101
|
+
expect(mockLocalProvider.exists).toHaveBeenCalledWith(packageName);
|
|
102
|
+
expect(mockOfficialProvider.exists).toHaveBeenCalledWith(packageName);
|
|
103
|
+
});
|
|
104
|
+
it("should return false when both providers return false", async () => {
|
|
105
|
+
// Arrange
|
|
106
|
+
const packageName = "non-existent-package";
|
|
107
|
+
vi.spyOn(mockLocalProvider, "exists").mockResolvedValue(false);
|
|
108
|
+
vi.spyOn(mockOfficialProvider, "exists").mockResolvedValue(false);
|
|
109
|
+
// Act
|
|
110
|
+
const result = await provider.exists(packageName);
|
|
111
|
+
// Assert
|
|
112
|
+
expect(result).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
it("should return false when official provider throws error", async () => {
|
|
115
|
+
// Arrange
|
|
116
|
+
const packageName = "@toolsdk.ai/tavily-mcp";
|
|
117
|
+
vi.spyOn(mockLocalProvider, "exists").mockResolvedValue(false);
|
|
118
|
+
vi.spyOn(mockOfficialProvider, "exists").mockRejectedValue(new Error("Network error"));
|
|
119
|
+
// Act
|
|
120
|
+
const result = await provider.exists(packageName);
|
|
121
|
+
// Assert
|
|
122
|
+
expect(result).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { LocalRegistryProvider } from "../providers/local-registry-provider";
|
|
3
|
+
describe("LocalRegistryProvider", () => {
|
|
4
|
+
let mockRepository;
|
|
5
|
+
let provider;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Mock PackageRepository
|
|
8
|
+
mockRepository = {
|
|
9
|
+
getPackageConfig: vi.fn(),
|
|
10
|
+
getAllPackages: vi.fn(),
|
|
11
|
+
exists: vi.fn(),
|
|
12
|
+
};
|
|
13
|
+
provider = new LocalRegistryProvider(mockRepository);
|
|
14
|
+
});
|
|
15
|
+
describe("getPackageConfig", () => {
|
|
16
|
+
it("should return package config when package exists", async () => {
|
|
17
|
+
// Arrange
|
|
18
|
+
const packageName = "@modelcontextprotocol/server-filesystem";
|
|
19
|
+
const mockConfig = {
|
|
20
|
+
type: "mcp-server",
|
|
21
|
+
runtime: "node",
|
|
22
|
+
packageName,
|
|
23
|
+
name: "Filesystem Server",
|
|
24
|
+
description: "A server for filesystem operations",
|
|
25
|
+
};
|
|
26
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
27
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockReturnValue(mockConfig);
|
|
28
|
+
// Act
|
|
29
|
+
const result = await provider.getPackageConfig(packageName);
|
|
30
|
+
// Assert
|
|
31
|
+
expect(result).toEqual(mockConfig);
|
|
32
|
+
expect(mockRepository.exists).toHaveBeenCalledWith(packageName);
|
|
33
|
+
expect(mockRepository.getPackageConfig).toHaveBeenCalledWith(packageName);
|
|
34
|
+
});
|
|
35
|
+
it("should return null when package does not exist", async () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
const packageName = "non-existent-package";
|
|
38
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(false);
|
|
39
|
+
// Act
|
|
40
|
+
const result = await provider.getPackageConfig(packageName);
|
|
41
|
+
// Assert
|
|
42
|
+
expect(result).toBeNull();
|
|
43
|
+
expect(mockRepository.exists).toHaveBeenCalledWith(packageName);
|
|
44
|
+
expect(mockRepository.getPackageConfig).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
it("should return null when getPackageConfig throws error", async () => {
|
|
47
|
+
// Arrange
|
|
48
|
+
const packageName = "@modelcontextprotocol/server-filesystem";
|
|
49
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
50
|
+
vi.spyOn(mockRepository, "getPackageConfig").mockImplementation(() => {
|
|
51
|
+
throw new Error("File read error");
|
|
52
|
+
});
|
|
53
|
+
// Act
|
|
54
|
+
const result = await provider.getPackageConfig(packageName);
|
|
55
|
+
// Assert
|
|
56
|
+
expect(result).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("exists", () => {
|
|
60
|
+
it("should return true when package exists", async () => {
|
|
61
|
+
// Arrange
|
|
62
|
+
const packageName = "@modelcontextprotocol/server-filesystem";
|
|
63
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(true);
|
|
64
|
+
// Act
|
|
65
|
+
const result = await provider.exists(packageName);
|
|
66
|
+
// Assert
|
|
67
|
+
expect(result).toBe(true);
|
|
68
|
+
expect(mockRepository.exists).toHaveBeenCalledWith(packageName);
|
|
69
|
+
});
|
|
70
|
+
it("should return false when package does not exist", async () => {
|
|
71
|
+
// Arrange
|
|
72
|
+
const packageName = "non-existent-package";
|
|
73
|
+
vi.spyOn(mockRepository, "exists").mockReturnValue(false);
|
|
74
|
+
// Act
|
|
75
|
+
const result = await provider.exists(packageName);
|
|
76
|
+
// Assert
|
|
77
|
+
expect(result).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { OfficialRegistryProvider } from "../providers/official-registry-provider";
|
|
3
|
+
/**
|
|
4
|
+
* Integration tests for OfficialRegistryProvider
|
|
5
|
+
* These tests call the real official API
|
|
6
|
+
*/
|
|
7
|
+
describe("OfficialRegistryProvider - Integration", () => {
|
|
8
|
+
const provider = new OfficialRegistryProvider();
|
|
9
|
+
describe("search", () => {
|
|
10
|
+
it("should search and return results from official API", async () => {
|
|
11
|
+
// Act - search for tavily
|
|
12
|
+
const results = await provider.search("tavily");
|
|
13
|
+
// Assert
|
|
14
|
+
expect(results).toBeDefined();
|
|
15
|
+
expect(Array.isArray(results)).toBe(true);
|
|
16
|
+
// Log results for debugging
|
|
17
|
+
console.log(`Found ${results.length} results for 'tavily'`);
|
|
18
|
+
if (results.length > 0) {
|
|
19
|
+
console.log("First result:", JSON.stringify(results[0], null, 2));
|
|
20
|
+
}
|
|
21
|
+
}, 10000); // 10s timeout for network call
|
|
22
|
+
it("should return empty array for non-existent package", async () => {
|
|
23
|
+
// Act
|
|
24
|
+
const results = await provider.search("non-existent-package-xyz-123");
|
|
25
|
+
// Assert
|
|
26
|
+
expect(results).toBeDefined();
|
|
27
|
+
expect(Array.isArray(results)).toBe(true);
|
|
28
|
+
expect(results.length).toBe(0);
|
|
29
|
+
}, 10000);
|
|
30
|
+
});
|
|
31
|
+
describe("getPackageConfig", () => {
|
|
32
|
+
it("should get config for tavily-mcp package", async () => {
|
|
33
|
+
// Act
|
|
34
|
+
const config = await provider.getPackageConfig("tavily");
|
|
35
|
+
// Assert
|
|
36
|
+
if (config) {
|
|
37
|
+
console.log("Tavily package config:", JSON.stringify(config, null, 2));
|
|
38
|
+
expect(config.type).toBe("mcp-server");
|
|
39
|
+
expect(config.runtime).toBe("node");
|
|
40
|
+
expect(config.packageName).toBeDefined();
|
|
41
|
+
expect(config.name).toBeDefined();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log("No tavily package found");
|
|
45
|
+
}
|
|
46
|
+
}, 10000);
|
|
47
|
+
it("should return null for non-existent package", async () => {
|
|
48
|
+
// Act
|
|
49
|
+
const config = await provider.getPackageConfig("non-existent-package-xyz-123");
|
|
50
|
+
// Assert
|
|
51
|
+
expect(config).toBeNull();
|
|
52
|
+
}, 10000);
|
|
53
|
+
});
|
|
54
|
+
describe("exists", () => {
|
|
55
|
+
it("should return true for existing package", async () => {
|
|
56
|
+
// Act
|
|
57
|
+
const exists = await provider.exists("tavily");
|
|
58
|
+
// Assert
|
|
59
|
+
console.log(`Tavily exists: ${exists}`);
|
|
60
|
+
// We expect it to exist, but don't fail the test if the API changes
|
|
61
|
+
expect(typeof exists).toBe("boolean");
|
|
62
|
+
}, 10000);
|
|
63
|
+
it("should return false for non-existent package", async () => {
|
|
64
|
+
// Act
|
|
65
|
+
const exists = await provider.exists("non-existent-package-xyz-123");
|
|
66
|
+
// Assert
|
|
67
|
+
expect(exists).toBe(false);
|
|
68
|
+
}, 10000);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|