@toolsdk.ai/registry 1.0.102 → 1.0.103

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 CHANGED
@@ -1,15 +1,43 @@
1
+ import fs from "node:fs/promises";
1
2
  import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
2
4
  import { serve } from "@hono/node-server";
3
5
  import { swaggerUI } from "@hono/swagger-ui";
4
6
  import { OpenAPIHono } from "@hono/zod-openapi";
5
7
  import dotenv from "dotenv";
8
+ import { searchRoutes } from "../search/search-route";
9
+ import searchService from "../search/search-service";
6
10
  import { packageRoutes } from "./package-route";
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
7
13
  dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });
8
14
  dotenv.config({ path: path.resolve(process.cwd(), ".env") });
15
+ const initializeSearchService = async () => {
16
+ try {
17
+ await searchService.initialize();
18
+ console.log("🔍 Search service initialized");
19
+ }
20
+ catch (error) {
21
+ console.warn("⚠️ Search service initialization failed:", error.message);
22
+ console.log("💡 Install and start MeiliSearch to enable enhanced search features");
23
+ }
24
+ };
9
25
  const app = new OpenAPIHono();
10
26
  app.route("/api/v1", packageRoutes);
11
- app.get("/", (c) => {
12
- return c.text("MCP Registry API Server is running!");
27
+ if (process.env.ENABLE_SEARCH === "true") {
28
+ initializeSearchService().catch(console.error);
29
+ app.route("/api/v1", searchRoutes);
30
+ }
31
+ app.get("/", async (c) => {
32
+ try {
33
+ const htmlPath = path.join(__dirname, "..", "search", "search.html");
34
+ const html = await fs.readFile(htmlPath, "utf8");
35
+ return c.html(html);
36
+ }
37
+ catch (error) {
38
+ console.error("Failed to load home page:", error);
39
+ return c.text("MCP Registry API Server is running!");
40
+ }
13
41
  });
14
42
  app.get("/api/meta", (c) => {
15
43
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -31,7 +59,7 @@ app.onError((err, c) => {
31
59
  console.error("Server Error:", err);
32
60
  return c.json({ success: false, code: 500, message: "Internal server error" }, 500);
33
61
  });
34
- const port = process.env.MCP_SERVER_PORT ? parseInt(process.env.MCP_SERVER_PORT, 10) : 3000;
62
+ const port = process.env.MCP_SERVER_PORT ? parseInt(process.env.MCP_SERVER_PORT, 10) : 3003;
35
63
  console.log(`Server is running on: http://localhost:${port}`);
36
64
  serve({
37
65
  fetch: app.fetch,
@@ -9,11 +9,11 @@ export declare const packageHandler: {
9
9
  success: boolean;
10
10
  code: number;
11
11
  message: string;
12
- }, 404, "json">) | (globalThis.Response & import("hono").TypedResponse<{
12
+ }, 400, "json">) | (globalThis.Response & import("hono").TypedResponse<{
13
13
  success: boolean;
14
14
  code: number;
15
15
  message: string;
16
- }, 400, "json">) | (globalThis.Response & import("hono").TypedResponse<{
16
+ }, 404, "json">) | (globalThis.Response & import("hono").TypedResponse<{
17
17
  success: boolean;
18
18
  code: number;
19
19
  message: string;
@@ -77,11 +77,11 @@ export declare const packageHandler: {
77
77
  success: boolean;
78
78
  code: number;
79
79
  message: string;
80
- }, 404, "json">) | (globalThis.Response & import("hono").TypedResponse<{
80
+ }, 400, "json">) | (globalThis.Response & import("hono").TypedResponse<{
81
81
  success: boolean;
82
82
  code: number;
83
83
  message: string;
84
- }, 400, "json">) | (globalThis.Response & import("hono").TypedResponse<{
84
+ }, 404, "json">) | (globalThis.Response & import("hono").TypedResponse<{
85
85
  success: boolean;
86
86
  code: number;
87
87
  message: string;
@@ -1,6 +1,9 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import { getMcpClient, getPackageConfigByKey, typedAllPackagesList } from "../helper.js";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
4
7
  export class PackageSO {
5
8
  async executeTool(request) {
6
9
  const mcpServerConfig = getPackageConfigByKey(request.packageName);
package/dist/helper.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare const typedAllPackagesList: Record<string, {
11
11
  }> | undefined;
12
12
  }>;
13
13
  export declare function getPackageConfigByKey(packageKey: string): MCPServerPackageConfig;
14
+ export declare function getPackageJSON(packageName: string): any;
14
15
  export declare function getMcpClient(mcpServerConfig: MCPServerPackageConfig, env?: Record<string, string>): Promise<{
15
16
  client: Client<{
16
17
  method: string;
package/dist/helper.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import assert from "node:assert";
2
2
  import fs from "node:fs";
3
3
  import * as path from "node:path";
4
+ import { fileURLToPath } from "node:url";
4
5
  import toml from "@iarna/toml";
5
6
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
7
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
@@ -8,6 +9,8 @@ import axios from "axios";
8
9
  import semver from "semver";
9
10
  import allPackagesList from "../indexes/packages-list.json";
10
11
  import { MCPServerPackageConfigSchema, PackagesListSchema } from "./schema";
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
11
14
  export const typedAllPackagesList = PackagesListSchema.parse(allPackagesList);
12
15
  export function getPackageConfigByKey(packageKey) {
13
16
  const value = typedAllPackagesList[packageKey];
@@ -20,8 +23,12 @@ export function getPackageConfigByKey(packageKey) {
20
23
  const mcpServerConfig = MCPServerPackageConfigSchema.parse(JSON.parse(jsonStr));
21
24
  return mcpServerConfig;
22
25
  }
23
- function getPackageJSON(packageName) {
26
+ export function getPackageJSON(packageName) {
24
27
  const packageJSONFilePath = `${__dirname}/../node_modules/${packageName}/package.json`;
28
+ // Check if the package exists in node_modules
29
+ if (!fs.existsSync(packageJSONFilePath)) {
30
+ throw new Error(`Package '${packageName}' not found in node_modules. Install it first with: pnpm add ${packageName}`);
31
+ }
25
32
  const packageJSONStr = fs.readFileSync(packageJSONFilePath, "utf8");
26
33
  const packageJSON = JSON.parse(packageJSONStr);
27
34
  return packageJSON;
@@ -100,6 +107,7 @@ export function updatePackageJsonDependencies({ packageDeps, enableValidation =
100
107
  "@hono/swagger-ui": "^0.5.2",
101
108
  "@hono/zod-openapi": "^0.16.4",
102
109
  "@iarna/toml": "^2.2.5",
110
+ meilisearch: "^0.33.0",
103
111
  lodash: "^4.17.21",
104
112
  zod: "^3.23.30",
105
113
  axios: "^1.9.0",
@@ -0,0 +1,3 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ declare const searchRoutes: OpenAPIHono<import("hono").Env, {}, "/">;
3
+ export { searchRoutes };
@@ -0,0 +1,305 @@
1
+ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
2
+ import { BaseResponseSchema } from "../schema";
3
+ import { createErrorResponse, createResponse, createRouteResponses } from "../utils";
4
+ import searchService from "./search-service";
5
+ const searchRoutes = new OpenAPIHono();
6
+ // Define search query parameter schema
7
+ const searchQuerySchema = z.object({
8
+ q: z.string().openapi({
9
+ description: "Search query",
10
+ example: "database",
11
+ }),
12
+ limit: z.coerce.number().min(1).max(100).optional().openapi({
13
+ description: "Limit the number of results",
14
+ example: 20,
15
+ }),
16
+ offset: z.coerce.number().min(0).optional().openapi({
17
+ description: "Offset for pagination",
18
+ example: 0,
19
+ }),
20
+ category: z.string().optional().openapi({
21
+ description: "Filter by category",
22
+ example: "databases",
23
+ }),
24
+ });
25
+ // Define search result response schema
26
+ const searchResultSchema = z
27
+ .object({
28
+ id: z.string(),
29
+ name: z.string(),
30
+ packageName: z.string(),
31
+ description: z.string(),
32
+ category: z.string(),
33
+ validated: z.boolean(),
34
+ author: z.string(),
35
+ toolCount: z.number(),
36
+ hasTools: z.boolean(),
37
+ popularity: z.number(),
38
+ })
39
+ .openapi("SearchResult");
40
+ const searchResponseSchema = BaseResponseSchema.extend({
41
+ data: z
42
+ .object({
43
+ hits: z.array(searchResultSchema),
44
+ query: z.string(),
45
+ processingTimeMs: z.number(),
46
+ limit: z.number(),
47
+ offset: z.number(),
48
+ estimatedTotalHits: z.number(),
49
+ })
50
+ .optional(),
51
+ }).openapi("SearchResponse");
52
+ // Search route definition
53
+ const searchRoute = createRoute({
54
+ method: "get",
55
+ path: "/search",
56
+ request: {
57
+ query: searchQuerySchema,
58
+ },
59
+ responses: createRouteResponses(searchResponseSchema, {
60
+ includeErrorResponses: true,
61
+ }),
62
+ });
63
+ searchRoutes.openapi(searchRoute, async (c) => {
64
+ try {
65
+ const { q, limit, offset, category } = c.req.valid("query");
66
+ // Build filter condition
67
+ const filter = category ? `category = '${category}'` : undefined;
68
+ // Execute search
69
+ const results = await searchService.search(q, {
70
+ limit,
71
+ offset,
72
+ filter,
73
+ });
74
+ const response = createResponse(results);
75
+ return c.json(response, 200);
76
+ }
77
+ catch (error) {
78
+ console.error("Search failed:", error.stack);
79
+ const errorResponse = createErrorResponse(error.message || "Search failed", 500);
80
+ return c.json(errorResponse, 500);
81
+ }
82
+ });
83
+ // Define suggest query parameter schema
84
+ const suggestQuerySchema = z.object({
85
+ q: z.string().openapi({
86
+ description: "Suggestion query",
87
+ example: "data",
88
+ }),
89
+ limit: z.coerce.number().min(1).max(20).optional().openapi({
90
+ description: "Limit the number of results",
91
+ example: 10,
92
+ }),
93
+ });
94
+ // Define suggest result response schema
95
+ const suggestResultSchema = z
96
+ .object({
97
+ name: z.string(),
98
+ packageName: z.string(),
99
+ category: z.string(),
100
+ highlighted: z.string(),
101
+ })
102
+ .openapi("SuggestResult");
103
+ const suggestResponseSchema = BaseResponseSchema.extend({
104
+ data: z
105
+ .object({
106
+ suggestions: z.array(suggestResultSchema),
107
+ })
108
+ .optional(),
109
+ }).openapi("SuggestResponse");
110
+ // Search suggest route definition
111
+ const suggestRoute = createRoute({
112
+ method: "get",
113
+ path: "/search/suggest",
114
+ request: {
115
+ query: suggestQuerySchema,
116
+ },
117
+ responses: createRouteResponses(suggestResponseSchema, {
118
+ includeErrorResponses: true,
119
+ }),
120
+ });
121
+ searchRoutes.openapi(suggestRoute, async (c) => {
122
+ try {
123
+ const { q, limit } = c.req.valid("query");
124
+ const suggestions = await searchService.suggest(q, limit);
125
+ const response = createResponse({ suggestions });
126
+ return c.json(response, 200);
127
+ }
128
+ catch (error) {
129
+ console.error("Failed to get suggestions:", error.stack);
130
+ const errorResponse = createErrorResponse(error.message || "Failed to get suggestions");
131
+ return c.json(errorResponse, 400);
132
+ }
133
+ });
134
+ // Define facets response schema
135
+ const facetsResponseSchema = BaseResponseSchema.extend({
136
+ data: z
137
+ .object({
138
+ categories: z.record(z.string(), z.number()).optional(),
139
+ authors: z.record(z.string(), z.number()).optional(),
140
+ validated: z.record(z.string(), z.number()).optional(),
141
+ })
142
+ .optional(),
143
+ }).openapi("FacetsResponse");
144
+ // Facets route definition
145
+ const facetsRoute = createRoute({
146
+ method: "get",
147
+ path: "/search/facets",
148
+ responses: createRouteResponses(facetsResponseSchema, {
149
+ includeErrorResponses: true,
150
+ }),
151
+ });
152
+ searchRoutes.openapi(facetsRoute, async (c) => {
153
+ try {
154
+ // Get facets information
155
+ const facets = await searchService.getFacets();
156
+ const response = createResponse({
157
+ categories: facets === null || facets === void 0 ? void 0 : facets.category,
158
+ authors: facets === null || facets === void 0 ? void 0 : facets.author,
159
+ validated: facets === null || facets === void 0 ? void 0 : facets.validated,
160
+ });
161
+ return c.json(response, 200);
162
+ }
163
+ catch (error) {
164
+ console.error("Failed to get facets:", error.stack);
165
+ const errorResponse = createErrorResponse(error.message || "Failed to get facets", 500);
166
+ return c.json(errorResponse, 500);
167
+ }
168
+ });
169
+ // Define health check response schema
170
+ const healthResponseSchema = BaseResponseSchema.extend({
171
+ data: z
172
+ .object({
173
+ status: z.string(),
174
+ host: z.string(),
175
+ initialized: z.boolean(),
176
+ indexName: z.string(),
177
+ documentCount: z.number(),
178
+ })
179
+ .optional(),
180
+ }).openapi("HealthResponse");
181
+ // Health check route definition
182
+ const healthRoute = createRoute({
183
+ method: "get",
184
+ path: "/search/health",
185
+ responses: createRouteResponses(healthResponseSchema, {
186
+ includeErrorResponses: true,
187
+ }),
188
+ });
189
+ searchRoutes.openapi(healthRoute, async (c) => {
190
+ try {
191
+ // Perform health check
192
+ const health = await searchService.healthCheck();
193
+ const response = createResponse(health);
194
+ return c.json(response, 200);
195
+ }
196
+ catch (error) {
197
+ console.error("Health check failed:", error.stack);
198
+ const errorResponse = createErrorResponse(error.message || "Health check failed", 500);
199
+ return c.json(errorResponse, 500);
200
+ }
201
+ });
202
+ // Define management response schema
203
+ const managementResponseSchema = BaseResponseSchema.extend({
204
+ data: z
205
+ .object({
206
+ message: z.string(),
207
+ details: z.any().optional(),
208
+ })
209
+ .optional(),
210
+ }).openapi("ManagementResponse");
211
+ // Initialize search service route definition
212
+ const initRoute = createRoute({
213
+ method: "post",
214
+ path: "/search/manage/init",
215
+ responses: createRouteResponses(managementResponseSchema, {
216
+ includeErrorResponses: true,
217
+ }),
218
+ });
219
+ searchRoutes.openapi(initRoute, async (c) => {
220
+ try {
221
+ // Initialize search service
222
+ await searchService.initialize();
223
+ const response = createResponse({
224
+ message: "Search service initialized successfully",
225
+ });
226
+ return c.json(response, 200);
227
+ }
228
+ catch (error) {
229
+ console.error("Failed to initialize search service:", error.stack);
230
+ const errorResponse = createErrorResponse(error.message || "Failed to initialize search service", 500);
231
+ return c.json(errorResponse, 500);
232
+ }
233
+ });
234
+ // Index packages route definition
235
+ const indexRoute = createRoute({
236
+ method: "post",
237
+ path: "/search/manage/index",
238
+ responses: createRouteResponses(managementResponseSchema, {
239
+ includeErrorResponses: true,
240
+ }),
241
+ });
242
+ searchRoutes.openapi(indexRoute, async (c) => {
243
+ try {
244
+ // Index all packages
245
+ const stats = await searchService.indexPackages();
246
+ const response = createResponse({
247
+ message: `Indexed ${stats.numberOfDocuments} documents`,
248
+ details: stats,
249
+ });
250
+ return c.json(response, 200);
251
+ }
252
+ catch (error) {
253
+ console.error("Failed to index packages:", error.stack);
254
+ const errorResponse = createErrorResponse(error.message || "Failed to index packages", 500);
255
+ return c.json(errorResponse, 500);
256
+ }
257
+ });
258
+ // Clear index route definition
259
+ const clearRoute = createRoute({
260
+ method: "post",
261
+ path: "/search/manage/clear",
262
+ responses: createRouteResponses(managementResponseSchema, {
263
+ includeErrorResponses: true,
264
+ }),
265
+ });
266
+ searchRoutes.openapi(clearRoute, async (c) => {
267
+ try {
268
+ // Clear the index
269
+ await searchService.clearIndex();
270
+ const response = createResponse({
271
+ message: "Index cleared successfully",
272
+ });
273
+ return c.json(response, 200);
274
+ }
275
+ catch (error) {
276
+ console.error("Failed to clear index:", error.stack);
277
+ const errorResponse = createErrorResponse(error.message || "Failed to clear index", 500);
278
+ return c.json(errorResponse, 500);
279
+ }
280
+ });
281
+ // Get index stats route definition
282
+ const statsRoute = createRoute({
283
+ method: "get",
284
+ path: "/search/manage/stats",
285
+ responses: createRouteResponses(managementResponseSchema, {
286
+ includeErrorResponses: true,
287
+ }),
288
+ });
289
+ searchRoutes.openapi(statsRoute, async (c) => {
290
+ try {
291
+ // Get index statistics
292
+ const stats = await searchService.getStats();
293
+ const response = createResponse({
294
+ message: "Index statistics retrieved successfully",
295
+ details: stats,
296
+ });
297
+ return c.json(response, 200);
298
+ }
299
+ catch (error) {
300
+ console.error("Failed to get index statistics:", error.stack);
301
+ const errorResponse = createErrorResponse(error.message || "Failed to get index statistics", 500);
302
+ return c.json(errorResponse, 500);
303
+ }
304
+ });
305
+ export { searchRoutes };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * MeiliSearch Service for Awesome MCP Registry
3
+ * Handles search indexing and querying for MCP packages
4
+ */
5
+ import { type Index, MeiliSearch } from "meilisearch";
6
+ interface PackageData {
7
+ name?: string;
8
+ description?: string;
9
+ category?: string;
10
+ validated?: boolean;
11
+ tools?: Record<string, {
12
+ description?: string;
13
+ }>;
14
+ path?: string;
15
+ }
16
+ interface SearchOptions {
17
+ limit?: number;
18
+ offset?: number;
19
+ filter?: string | string[];
20
+ sort?: string[];
21
+ matchingStrategy?: string;
22
+ }
23
+ interface SearchResults {
24
+ hits: unknown[];
25
+ query: string;
26
+ processingTimeMs: number;
27
+ limit: number;
28
+ offset: number;
29
+ estimatedTotalHits: number;
30
+ }
31
+ interface IndexStats {
32
+ numberOfDocuments: number;
33
+ isIndexing: boolean;
34
+ fieldDistribution: Record<string, number>;
35
+ }
36
+ declare class SearchService {
37
+ private host;
38
+ private apiKey;
39
+ private indexName;
40
+ protected client: MeiliSearch;
41
+ protected _index: Index | null;
42
+ protected isInitialized: boolean;
43
+ constructor(indexName?: string);
44
+ /**
45
+ * Get the initialization status
46
+ */
47
+ getIsInitialized(): boolean;
48
+ /**
49
+ * Get the MeiliSearch client
50
+ */
51
+ getClient(): MeiliSearch;
52
+ get index(): Index | null;
53
+ set index(value: Index | null);
54
+ initialize(): Promise<void>;
55
+ /**
56
+ * Configure search index settings for optimal MCP package search
57
+ */
58
+ configureIndex(): Promise<void>;
59
+ /**
60
+ * Transform package data for search indexing
61
+ */
62
+ transformPackageForIndex(packageName: string, packageData: PackageData): Record<string, unknown>;
63
+ /**
64
+ * Create a safe ID for MeiliSearch (alphanumeric, hyphens, underscores only)
65
+ */
66
+ createSafeId(packageName: string): string;
67
+ /**
68
+ * Calculate a popularity score for ranking
69
+ */
70
+ calculatePopularityScore(packageData: PackageData): number;
71
+ /**
72
+ * Extract author from package name
73
+ */
74
+ extractAuthor(packageName: string): string;
75
+ /**
76
+ * Extract relevant keywords from package data
77
+ */
78
+ extractKeywords(packageData: PackageData, packageName: string): string;
79
+ /**
80
+ * Index all packages from the packages-list.json file
81
+ */
82
+ indexPackages(): Promise<IndexStats>;
83
+ /**
84
+ * Search packages with advanced options
85
+ */
86
+ search(query: string, options?: SearchOptions): Promise<SearchResults>;
87
+ /**
88
+ * Get search suggestions/autocomplete
89
+ */
90
+ suggest(query: string, limit?: number): Promise<{
91
+ name: string;
92
+ packageName: string;
93
+ category: string;
94
+ highlighted: string;
95
+ }[]>;
96
+ /**
97
+ * Get faceted search results (for filters)
98
+ */
99
+ getFacets(): Promise<Record<string, unknown> | undefined>;
100
+ /**
101
+ * Get index statistics
102
+ */
103
+ getStats(): Promise<IndexStats>;
104
+ /**
105
+ * Clear the index
106
+ */
107
+ clearIndex(): Promise<void>;
108
+ /**
109
+ * Health check for the search service
110
+ */
111
+ healthCheck(): Promise<{
112
+ status: string;
113
+ host: string;
114
+ initialized: boolean;
115
+ indexName: string;
116
+ documentCount: number;
117
+ }>;
118
+ }
119
+ declare const searchService: SearchService;
120
+ export default searchService;