@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.
Files changed (97) hide show
  1. package/README.md +66 -11
  2. package/dist/api/index.d.ts +1 -1
  3. package/dist/api/index.js +23 -25
  4. package/dist/domains/config/config-route.d.ts +2 -0
  5. package/dist/domains/config/config-route.js +31 -0
  6. package/dist/domains/config/config-schema.d.ts +57 -0
  7. package/dist/domains/config/config-schema.js +10 -0
  8. package/dist/domains/config/config-types.d.ts +3 -0
  9. package/dist/domains/executor/executor-factory.d.ts +9 -0
  10. package/dist/domains/executor/executor-factory.js +17 -0
  11. package/dist/domains/executor/executor-types.d.ts +15 -0
  12. package/dist/domains/executor/local-executor.d.ts +12 -0
  13. package/dist/domains/executor/local-executor.js +48 -0
  14. package/dist/domains/executor/sandbox-executor.d.ts +16 -0
  15. package/dist/domains/executor/sandbox-executor.js +83 -0
  16. package/dist/domains/package/package-handler.d.ts +17 -0
  17. package/dist/domains/package/package-handler.js +58 -0
  18. package/dist/domains/package/package-repository.d.ts +9 -0
  19. package/dist/domains/package/package-repository.js +26 -0
  20. package/dist/domains/package/package-route.d.ts +2 -0
  21. package/dist/{api → domains/package}/package-route.js +38 -52
  22. package/dist/domains/package/package-schema.d.ts +244 -0
  23. package/dist/domains/package/package-schema.js +52 -0
  24. package/dist/domains/package/package-so.d.ts +78 -0
  25. package/dist/domains/package/package-so.js +61 -0
  26. package/dist/domains/package/package-so.test.js +378 -0
  27. package/dist/domains/package/package-types.d.ts +9 -0
  28. package/dist/domains/package/package-types.js +1 -0
  29. package/dist/domains/sandbox/clients/daytona-client.d.ts +17 -0
  30. package/dist/domains/sandbox/clients/daytona-client.js +112 -0
  31. package/dist/domains/sandbox/clients/sandock-client.d.ts +19 -0
  32. package/dist/domains/sandbox/clients/sandock-client.js +178 -0
  33. package/dist/domains/sandbox/sandbox-factory.d.ts +8 -0
  34. package/dist/domains/sandbox/sandbox-factory.js +23 -0
  35. package/dist/domains/sandbox/sandbox-pool-so.d.ts +25 -0
  36. package/dist/domains/sandbox/sandbox-pool-so.js +123 -0
  37. package/dist/domains/sandbox/sandbox-types.d.ts +25 -0
  38. package/dist/domains/sandbox/sandbox-types.js +1 -0
  39. package/dist/domains/sandbox/sandbox-utils.d.ts +3 -0
  40. package/dist/domains/sandbox/sandbox-utils.js +109 -0
  41. package/dist/domains/search/search-handler.d.ts +47 -0
  42. package/dist/domains/search/search-handler.js +113 -0
  43. package/dist/domains/search/search-route.d.ts +2 -0
  44. package/dist/domains/search/search-route.js +101 -0
  45. package/dist/domains/search/search-schema.d.ts +384 -0
  46. package/dist/domains/search/search-schema.js +99 -0
  47. package/dist/domains/search/search-so.d.ts +55 -0
  48. package/dist/{search/search-service.js → domains/search/search-so.js} +200 -297
  49. package/dist/shared/config/environment.d.ts +16 -0
  50. package/dist/shared/config/environment.js +41 -0
  51. package/dist/shared/schemas/common-schema.d.ts +249 -0
  52. package/dist/{schema.js → shared/schemas/common-schema.js} +37 -80
  53. package/dist/shared/schemas/index.d.ts +1 -0
  54. package/dist/shared/schemas/index.js +1 -0
  55. package/dist/shared/scripts-helpers/index.d.ts +60 -0
  56. package/dist/shared/scripts-helpers/index.js +61 -0
  57. package/dist/shared/utils/file-util.d.ts +1 -0
  58. package/dist/shared/utils/file-util.js +5 -0
  59. package/dist/shared/utils/index.d.ts +5 -0
  60. package/dist/shared/utils/index.js +5 -0
  61. package/dist/shared/utils/mcp-client-util.d.ts +31 -0
  62. package/dist/shared/utils/mcp-client-util.js +79 -0
  63. package/dist/shared/utils/package-util.d.ts +6 -0
  64. package/dist/shared/utils/package-util.js +53 -0
  65. package/dist/shared/utils/promise-util.d.ts +1 -0
  66. package/dist/shared/utils/promise-util.js +14 -0
  67. package/dist/{utils.d.ts → shared/utils/response-util.d.ts} +6 -2
  68. package/dist/{utils.js → shared/utils/response-util.js} +1 -6
  69. package/dist/shared/utils/string-util.d.ts +1 -0
  70. package/dist/shared/utils/string-util.js +25 -0
  71. package/dist/shared/utils/validation-util.d.ts +12 -0
  72. package/dist/shared/utils/validation-util.js +99 -0
  73. package/indexes/categories-list.json +1 -0
  74. package/indexes/packages-list.json +6 -0
  75. package/package.json +9 -2
  76. package/packages/developer-tools/neurolink.json +23 -0
  77. package/packages/search-data-extraction/ref-tools-mcp.json +7 -2
  78. package/README.dev.md +0 -195
  79. package/dist/api/package-handler.d.ts +0 -18
  80. package/dist/api/package-handler.js +0 -72
  81. package/dist/api/package-route.d.ts +0 -2
  82. package/dist/api/package-so.d.ts +0 -19
  83. package/dist/api/package-so.js +0 -263
  84. package/dist/api/package.test.js +0 -19
  85. package/dist/helper.d.ts +0 -72
  86. package/dist/helper.js +0 -278
  87. package/dist/sandbox/mcp-sandbox-client.d.ts +0 -37
  88. package/dist/sandbox/mcp-sandbox-client.js +0 -428
  89. package/dist/schema.d.ts +0 -806
  90. package/dist/search/search-route.d.ts +0 -3
  91. package/dist/search/search-route.js +0 -305
  92. package/dist/search/search-service.d.ts +0 -120
  93. package/dist/search/search.test.js +0 -100
  94. package/dist/types.d.ts +0 -27
  95. /package/dist/{api/package.test.d.ts → domains/config/config-types.js} +0 -0
  96. /package/dist/{search/search.test.d.ts → domains/executor/executor-types.js} +0 -0
  97. /package/dist/{types.js → domains/package/package-so.test.d.ts} +0 -0
@@ -1,117 +1,89 @@
1
- /**
2
- * MeiliSearch Service for Awesome MCP Registry
3
- * Handles search indexing and querying for MCP packages
4
- */
5
1
  import fs from "node:fs/promises";
6
- import path from "node:path";
7
2
  import { MeiliSearch } from "meilisearch";
8
- import { getDirname } from "../utils";
9
- const __dirname = getDirname(import.meta.url);
10
- class SearchService {
11
- constructor(indexName = "mcp-packages") {
12
- // MeiliSearch configuration
13
- this.host = process.env.MEILI_HTTP_ADDR || "http://localhost:7700";
14
- this.apiKey = process.env.MEILI_MASTER_KEY || null;
3
+ import { getMeiliSearchConfig } from "../../shared/config/environment";
4
+ /**
5
+ * Search Service Object
6
+ * Manages MeiliSearch connection and indexing with singleton pattern
7
+ */
8
+ export class SearchSO {
9
+ constructor(client, _index, host, indexName) {
10
+ this.client = client;
11
+ this._index = _index;
12
+ this.host = host;
15
13
  this.indexName = indexName;
16
- // Initialize MeiliSearch client
17
- this.client = new MeiliSearch({
18
- host: this.host,
19
- apiKey: this.apiKey || undefined,
20
- });
21
- this._index = null;
22
- this.isInitialized = false;
23
- }
24
- /**
25
- * Get the initialization status
26
- */
27
- getIsInitialized() {
28
- return this.isInitialized;
29
- }
30
- /**
31
- * Get the MeiliSearch client
32
- */
33
- getClient() {
34
- return this.client;
35
14
  }
36
15
  get index() {
37
16
  return this._index;
38
17
  }
39
- set index(value) {
40
- this._index = value;
18
+ get isInitialized() {
19
+ return this._index !== null;
41
20
  }
42
- async initialize() {
21
+ static async getInstance(host, apiKey, indexName = "mcp-packages") {
22
+ var _a;
23
+ if ((_a = SearchSO.instance) === null || _a === void 0 ? void 0 : _a.isInitialized) {
24
+ return SearchSO.instance;
25
+ }
26
+ const meiliConfig = getMeiliSearchConfig();
27
+ const meiliHost = host || meiliConfig.host;
28
+ const meiliKey = apiKey || meiliConfig.apiKey || undefined;
29
+ const client = new MeiliSearch({
30
+ host: meiliHost,
31
+ apiKey: meiliKey,
32
+ });
33
+ console.log(`Connecting to MeiliSearch at ${meiliHost}...`);
34
+ await client.health();
35
+ console.log("✅ MeiliSearch is healthy");
36
+ let index;
43
37
  try {
44
- console.log(`Connecting to MeiliSearch at ${this.host}...`);
45
- // Check if MeiliSearch is running
46
- await this.client.health();
47
- console.log("✅ MeiliSearch is healthy");
48
- // Create or get the index
49
- try {
50
- await this.client.createIndex(this.indexName, { primaryKey: "id" });
51
- console.log(`✅ Created new index: ${this.indexName}`);
52
- }
53
- catch (error) {
54
- if (error.message.includes("exists")) {
55
- console.log(`✅ Using existing index: ${this.indexName}`);
56
- }
57
- else {
58
- throw error;
59
- }
60
- }
61
- // Get the index object
62
- this.index = await this.client.getIndex(this.indexName);
63
- // Configure search settings
64
- await this.configureIndex();
65
- this.isInitialized = true;
66
- console.log("✅ Search service initialized successfully");
38
+ await client.createIndex(indexName, { primaryKey: "id" });
39
+ console.log(`✅ Created new index: ${indexName}`);
40
+ index = await client.getIndex(indexName);
67
41
  }
68
42
  catch (error) {
69
- console.error("❌ Failed to initialize search service:", error.message);
70
- console.log("💡 Make sure MeiliSearch is running on", this.host);
71
- throw error;
43
+ if (error.message.includes("exists")) {
44
+ console.log(`✅ Using existing index: ${indexName}`);
45
+ }
46
+ index = await client.getIndex(indexName);
72
47
  }
48
+ const searchSO = new SearchSO(client, index, meiliHost, indexName);
49
+ await searchSO.configureIndex();
50
+ console.log("✅ Search service initialized successfully");
51
+ SearchSO.instance = searchSO;
52
+ return searchSO;
73
53
  }
74
- /**
75
- * Configure search index settings for optimal MCP package search
76
- */
77
54
  async configureIndex() {
55
+ if (!this._index)
56
+ return;
78
57
  try {
79
- // Configure searchable attributes (ranked by importance)
80
- if (this.index) {
81
- await this.index.updateSettings({
82
- searchableAttributes: [
83
- "name",
84
- "packageName",
85
- "description",
86
- "tools",
87
- "category",
88
- "author",
89
- "keywords",
90
- ],
91
- // Configure filterable attributes
92
- filterableAttributes: ["category", "validated", "author", "hasTools", "popularity"],
93
- // Configure sortable attributes
94
- sortableAttributes: ["popularity", "name", "category"],
95
- // Configure ranking rules for relevance
96
- rankingRules: [
97
- "words",
98
- "typo",
99
- "proximity",
100
- "attribute",
101
- "sort",
102
- "exactness",
103
- "popularity:desc",
104
- ],
105
- // Configure synonyms for better search
106
- synonyms: {
107
- ai: ["artificial intelligence", "machine learning", "ml"],
108
- db: ["database"],
109
- api: ["rest", "graphql"],
110
- auth: ["authentication", "authorization"],
111
- mcp: ["model context protocol"],
112
- },
113
- });
114
- }
58
+ await this._index.updateSettings({
59
+ searchableAttributes: [
60
+ "name",
61
+ "packageName",
62
+ "description",
63
+ "tools",
64
+ "category",
65
+ "author",
66
+ "keywords",
67
+ ],
68
+ filterableAttributes: ["category", "validated", "author", "hasTools", "popularity"],
69
+ sortableAttributes: ["popularity", "name", "category"],
70
+ rankingRules: [
71
+ "words",
72
+ "typo",
73
+ "proximity",
74
+ "attribute",
75
+ "sort",
76
+ "exactness",
77
+ "popularity:desc",
78
+ ],
79
+ synonyms: {
80
+ ai: ["artificial intelligence", "machine learning", "ml"],
81
+ db: ["database"],
82
+ api: ["rest", "graphql"],
83
+ auth: ["authentication", "authorization"],
84
+ mcp: ["model context protocol"],
85
+ },
86
+ });
115
87
  console.log("✅ Search index configured");
116
88
  }
117
89
  catch (error) {
@@ -119,166 +91,13 @@ class SearchService {
119
91
  throw error;
120
92
  }
121
93
  }
122
- /**
123
- * Transform package data for search indexing
124
- */
125
- transformPackageForIndex(packageName, packageData) {
126
- // Extract tools information
127
- const tools = packageData.tools || {};
128
- const toolNames = Object.keys(tools);
129
- const toolDescriptions = Object.values(tools)
130
- .map((t) => t.description || "")
131
- .join(" ");
132
- // Calculate basic popularity score (can be enhanced with real metrics)
133
- const popularity = this.calculatePopularityScore(packageData);
134
- // Create a safe ID by encoding special characters
135
- const safeId = this.createSafeId(packageName);
136
- return {
137
- id: safeId,
138
- name: packageData.name || packageName,
139
- packageName: packageName,
140
- description: packageData.description || "",
141
- category: packageData.category || "uncategorized",
142
- validated: packageData.validated || false,
143
- author: this.extractAuthor(packageName),
144
- tools: `${toolNames.join(" ")} ${toolDescriptions}`,
145
- toolCount: toolNames.length,
146
- hasTools: toolNames.length > 0,
147
- keywords: this.extractKeywords(packageData, packageName),
148
- popularity: popularity,
149
- path: packageData.path || "",
150
- };
151
- }
152
- /**
153
- * Create a safe ID for MeiliSearch (alphanumeric, hyphens, underscores only)
154
- */
155
- createSafeId(packageName) {
156
- return packageName
157
- .replace(/@/g, "at-")
158
- .replace(/\//g, "-")
159
- .replace(/\./g, "_")
160
- .replace(/:/g, "-")
161
- .replace(/[^a-zA-Z0-9\-_]/g, "_")
162
- .substring(0, 500); // Keep under 511 byte limit
163
- }
164
- /**
165
- * Calculate a popularity score for ranking
166
- */
167
- calculatePopularityScore(packageData) {
168
- let score = 0;
169
- // Boost for validated packages
170
- if (packageData.validated)
171
- score += 10;
172
- // Boost for packages with tools
173
- const toolCount = Object.keys(packageData.tools || {}).length;
174
- score += toolCount * 2;
175
- // Boost for packages with good descriptions
176
- if (packageData.description && packageData.description.length > 50)
177
- score += 5;
178
- return score;
179
- }
180
- /**
181
- * Extract author from package name
182
- */
183
- extractAuthor(packageName) {
184
- if (packageName.startsWith("@")) {
185
- return packageName.split("/")[0].substring(1);
186
- }
187
- return "";
188
- }
189
- /**
190
- * Extract relevant keywords from package data
191
- */
192
- extractKeywords(packageData, packageName) {
193
- const keywords = [];
194
- // Add category as keyword
195
- if (packageData.category) {
196
- keywords.push(packageData.category.replace(/-/g, " "));
197
- }
198
- // Extract keywords from description
199
- if (packageData.description) {
200
- const desc = packageData.description.toLowerCase();
201
- const commonKeywords = [
202
- "api",
203
- "database",
204
- "auth",
205
- "search",
206
- "ai",
207
- "ml",
208
- "tool",
209
- "server",
210
- "client",
211
- ];
212
- commonKeywords.forEach((keyword) => {
213
- if (desc.includes(keyword))
214
- keywords.push(keyword);
215
- });
216
- }
217
- // Extract from package name
218
- const nameWords = packageName
219
- .replace(/[@/\-_]/g, " ")
220
- .split(" ")
221
- .filter((w) => w.length > 2);
222
- keywords.push(...nameWords);
223
- return [...new Set(keywords)].join(" ");
224
- }
225
- /**
226
- * Index all packages from the packages-list.json file
227
- */
228
- async indexPackages() {
229
- if (!this.isInitialized) {
230
- throw new Error("Search service not initialized. Call initialize() first.");
231
- }
232
- try {
233
- console.log("📥 Loading packages data...");
234
- const packagesPath = path.join(__dirname, "..", "..", "indexes", "packages-list.json");
235
- const packagesData = await fs.readFile(packagesPath, "utf8");
236
- const packages = JSON.parse(packagesData);
237
- console.log(`📦 Found ${Object.keys(packages).length} packages to index`);
238
- // Transform packages for indexing
239
- const documents = Object.entries(packages).map(([packageName, packageData]) => this.transformPackageForIndex(packageName, packageData));
240
- console.log("🔄 Indexing packages...");
241
- // Add documents to index in batches
242
- const batchSize = 1000;
243
- const batches = [];
244
- for (let i = 0; i < documents.length; i += batchSize) {
245
- batches.push(documents.slice(i, i + batchSize));
246
- }
247
- for (let i = 0; i < batches.length; i++) {
248
- const batch = batches[i];
249
- if (this.index) {
250
- const task = await this.index.addDocuments(batch);
251
- console.log(`📝 Indexed batch ${i + 1}/${batches.length} (${batch.length} documents) - Task ID: ${task.taskUid}`);
252
- // Wait for task completion
253
- await this.client.waitForTask(task.taskUid);
254
- }
255
- }
256
- // Get final stats
257
- if (!this.index) {
258
- throw new Error("Index is not initialized");
259
- }
260
- const stats = await this.index.getStats();
261
- console.log(`✅ Indexing complete! ${stats.numberOfDocuments} documents indexed`);
262
- return stats;
263
- }
264
- catch (error) {
265
- console.error("❌ Failed to index packages:", error.message);
266
- throw error;
267
- }
268
- }
269
- /**
270
- * Search packages with advanced options
271
- */
272
94
  async search(query, options = {}) {
273
- if (!this.isInitialized) {
274
- throw new Error("Search service not initialized. Call initialize() first.");
95
+ if (!this._index) {
96
+ throw new Error("Search index not initialized. Call getInstance() first.");
275
97
  }
276
98
  try {
277
99
  const searchOptions = Object.assign({ limit: options.limit || 20, offset: options.offset || 0, filter: options.filter || undefined, sort: options.sort || ["popularity:desc"], attributesToHighlight: ["name", "description"], attributesToCrop: ["description"], cropLength: 100, highlightPreTag: "<mark>", highlightPostTag: "</mark>", matchingStrategy: options.matchingStrategy || "last" }, options);
278
- if (!this.index) {
279
- throw new Error("Index is not initialized");
280
- }
281
- const results = await this.index.search(query, searchOptions);
100
+ const results = await this._index.search(query, searchOptions);
282
101
  return {
283
102
  hits: results.hits,
284
103
  query: results.query,
@@ -293,18 +112,12 @@ class SearchService {
293
112
  throw error;
294
113
  }
295
114
  }
296
- /**
297
- * Get search suggestions/autocomplete
298
- */
299
115
  async suggest(query, limit = 10) {
300
- if (!this.isInitialized) {
301
- throw new Error("Search service not initialized. Call initialize() first.");
116
+ if (!this._index) {
117
+ throw new Error("Search index not initialized");
302
118
  }
303
119
  try {
304
- if (!this.index) {
305
- throw new Error("Index is not initialized");
306
- }
307
- const results = await this.index.search(query, {
120
+ const results = await this._index.search(query, {
308
121
  limit: limit,
309
122
  attributesToRetrieve: ["name", "packageName", "category"],
310
123
  attributesToHighlight: ["name"],
@@ -325,18 +138,46 @@ class SearchService {
325
138
  throw error;
326
139
  }
327
140
  }
328
- /**
329
- * Get faceted search results (for filters)
330
- */
331
- async getFacets() {
332
- if (!this.isInitialized) {
333
- throw new Error("Search service not initialized. Call initialize() first.");
141
+ async indexPackages(packagesPath) {
142
+ if (!this._index) {
143
+ throw new Error("Search index not initialized");
334
144
  }
335
145
  try {
336
- if (!this.index) {
337
- throw new Error("Index is not initialized");
146
+ console.log("📥 Loading packages data...");
147
+ const packagesData = await fs.readFile(packagesPath, "utf8");
148
+ const packages = JSON.parse(packagesData);
149
+ console.log(`📦 Found ${Object.keys(packages).length} packages to index`);
150
+ // Transform packages for indexing
151
+ const documents = Object.entries(packages).map(([packageName, packageData]) => this.transformPackageForIndex(packageName, packageData));
152
+ console.log("🔄 Indexing packages...");
153
+ // Add documents to index in batches
154
+ const batchSize = 1000;
155
+ const batches = [];
156
+ for (let i = 0; i < documents.length; i += batchSize) {
157
+ batches.push(documents.slice(i, i + batchSize));
158
+ }
159
+ for (let i = 0; i < batches.length; i++) {
160
+ const batch = batches[i];
161
+ const task = await this._index.addDocuments(batch);
162
+ console.log(`📝 Indexed batch ${i + 1}/${batches.length} (${batch.length} documents) - Task ID: ${task.taskUid}`);
163
+ await this.client.waitForTask(task.taskUid);
338
164
  }
339
- const results = await this.index.search("", {
165
+ // Get final stats
166
+ const stats = await this._index.getStats();
167
+ console.log(`✅ Indexing complete! ${stats.numberOfDocuments} documents indexed`);
168
+ return stats;
169
+ }
170
+ catch (error) {
171
+ console.error("❌ Failed to index packages:", error.message);
172
+ throw error;
173
+ }
174
+ }
175
+ async getFacets() {
176
+ if (!this._index) {
177
+ throw new Error("Search index not initialized");
178
+ }
179
+ try {
180
+ const results = await this._index.search("", {
340
181
  limit: 0,
341
182
  facets: ["category", "validated", "author"],
342
183
  });
@@ -347,36 +188,24 @@ class SearchService {
347
188
  throw error;
348
189
  }
349
190
  }
350
- /**
351
- * Get index statistics
352
- */
353
191
  async getStats() {
354
- if (!this.isInitialized) {
355
- throw new Error("Search service not initialized. Call initialize() first.");
192
+ if (!this._index) {
193
+ throw new Error("Search index not initialized");
356
194
  }
357
195
  try {
358
- if (!this.index) {
359
- throw new Error("Index is not initialized");
360
- }
361
- return await this.index.getStats();
196
+ return await this._index.getStats();
362
197
  }
363
198
  catch (error) {
364
199
  console.error("❌ Failed to get stats:", error.message);
365
200
  throw error;
366
201
  }
367
202
  }
368
- /**
369
- * Clear the index
370
- */
371
203
  async clearIndex() {
372
- if (!this.isInitialized) {
373
- throw new Error("Search service not initialized. Call initialize() first.");
204
+ if (!this._index) {
205
+ throw new Error("Search index not initialized");
374
206
  }
375
207
  try {
376
- if (!this.index) {
377
- throw new Error("Index is not initialized");
378
- }
379
- const task = await this.index.deleteAllDocuments();
208
+ const task = await this._index.deleteAllDocuments();
380
209
  await this.client.waitForTask(task.taskUid);
381
210
  console.log("✅ Index cleared");
382
211
  }
@@ -385,9 +214,6 @@ class SearchService {
385
214
  throw error;
386
215
  }
387
216
  }
388
- /**
389
- * Health check for the search service
390
- */
391
217
  async healthCheck() {
392
218
  try {
393
219
  await this.client.health();
@@ -410,7 +236,84 @@ class SearchService {
410
236
  };
411
237
  }
412
238
  }
239
+ transformPackageForIndex(packageName, packageData) {
240
+ const tools = packageData.tools || {};
241
+ const toolNames = Object.keys(tools);
242
+ const toolDescriptions = Object.values(tools)
243
+ .map((t) => t.description || "")
244
+ .join(" ");
245
+ const popularity = this.calculatePopularityScore(packageData);
246
+ const safeId = this.createSafeId(packageName);
247
+ return {
248
+ id: safeId,
249
+ name: packageData.name || packageName,
250
+ packageName: packageName,
251
+ description: packageData.description || "",
252
+ category: packageData.category || "uncategorized",
253
+ validated: packageData.validated || false,
254
+ author: this.extractAuthor(packageName),
255
+ tools: `${toolNames.join(" ")} ${toolDescriptions}`,
256
+ toolCount: toolNames.length,
257
+ hasTools: toolNames.length > 0,
258
+ keywords: this.extractKeywords(packageData, packageName),
259
+ popularity: popularity,
260
+ path: packageData.path || "",
261
+ };
262
+ }
263
+ createSafeId(packageName) {
264
+ return packageName
265
+ .replace(/@/g, "at-")
266
+ .replace(/\//g, "-")
267
+ .replace(/\./g, "_")
268
+ .replace(/:/g, "-")
269
+ .replace(/[^a-zA-Z0-9\-_]/g, "_")
270
+ .substring(0, 500);
271
+ }
272
+ calculatePopularityScore(packageData) {
273
+ let score = 0;
274
+ if (packageData.validated)
275
+ score += 10;
276
+ const toolCount = Object.keys(packageData.tools || {}).length;
277
+ score += toolCount * 2;
278
+ if (packageData.description && packageData.description.length > 50)
279
+ score += 5;
280
+ return score;
281
+ }
282
+ extractAuthor(packageName) {
283
+ if (packageName.startsWith("@")) {
284
+ return packageName.split("/")[0].substring(1);
285
+ }
286
+ return "";
287
+ }
288
+ extractKeywords(packageData, packageName) {
289
+ const keywords = [];
290
+ if (packageData.category) {
291
+ keywords.push(packageData.category.replace(/-/g, " "));
292
+ }
293
+ if (packageData.description) {
294
+ const desc = packageData.description.toLowerCase();
295
+ const commonKeywords = [
296
+ "api",
297
+ "database",
298
+ "auth",
299
+ "search",
300
+ "ai",
301
+ "ml",
302
+ "tool",
303
+ "server",
304
+ "client",
305
+ ];
306
+ commonKeywords.forEach((keyword) => {
307
+ if (desc.includes(keyword))
308
+ keywords.push(keyword);
309
+ });
310
+ }
311
+ const nameWords = packageName
312
+ .replace(/[@/\-_]/g, " ")
313
+ .split(" ")
314
+ .filter((w) => w.length > 2);
315
+ keywords.push(...nameWords);
316
+ return [...new Set(keywords)].join(" ");
317
+ }
413
318
  }
414
- // Export singleton instance
415
- const searchService = new SearchService();
416
- export default searchService;
319
+ SearchSO.instance = null;
@@ -0,0 +1,16 @@
1
+ import type { MCPSandboxProvider } from "../../domains/sandbox/sandbox-types";
2
+ export declare function getSandboxProvider(): MCPSandboxProvider;
3
+ export declare function getDaytonaConfig(): {
4
+ apiKey: string;
5
+ apiUrl: string | undefined;
6
+ };
7
+ export declare function getSandockConfig(): {
8
+ apiKey: string;
9
+ apiUrl: string;
10
+ };
11
+ export declare function getMeiliSearchConfig(): {
12
+ host: string;
13
+ apiKey: string | null;
14
+ };
15
+ export declare function getServerPort(): number;
16
+ export declare function isSearchEnabled(): boolean;
@@ -0,0 +1,41 @@
1
+ import path from "node:path";
2
+ import dotenv from "dotenv";
3
+ dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });
4
+ dotenv.config({ path: path.resolve(process.cwd(), ".env") });
5
+ export function getSandboxProvider() {
6
+ console.log("process.env.MCP_SANDBOX_PROVIDER", process.env.MCP_SANDBOX_PROVIDER);
7
+ const provider = (process.env.MCP_SANDBOX_PROVIDER || "LOCAL").toUpperCase();
8
+ if (provider === "LOCAL" ||
9
+ provider === "DAYTONA" ||
10
+ provider === "SANDOCK" ||
11
+ provider === "E2B") {
12
+ return provider;
13
+ }
14
+ console.warn(`[Environment] Unsupported MCP_SANDBOX_PROVIDER value '${provider}', falling back to LOCAL mode`);
15
+ return "LOCAL";
16
+ }
17
+ export function getDaytonaConfig() {
18
+ return {
19
+ apiKey: process.env.DAYTONA_API_KEY || "",
20
+ apiUrl: process.env.DAYTONA_API_URL,
21
+ };
22
+ }
23
+ export function getSandockConfig() {
24
+ return {
25
+ apiKey: process.env.SANDOCK_API_KEY || "",
26
+ apiUrl: process.env.SANDOCK_API_URL || "https://sandock.ai",
27
+ };
28
+ }
29
+ export function getMeiliSearchConfig() {
30
+ return {
31
+ host: process.env.MEILI_HTTP_ADDR || "http://localhost:7700",
32
+ apiKey: process.env.MEILI_MASTER_KEY || null,
33
+ };
34
+ }
35
+ export function getServerPort() {
36
+ const port = process.env.PORT || process.env.MCP_SERVER_PORT;
37
+ return port ? parseInt(port, 10) : 3003;
38
+ }
39
+ export function isSearchEnabled() {
40
+ return process.env.ENABLE_SEARCH === "true";
41
+ }