UNPKG

@xiaohui-wang/mcpadvisor

Version:

MCP Advisor & Installation - Find the right MCP server for your needs

112 lines (111 loc) 3.92 kB
/** * Meilisearch 搜索提供者实现 * 使用 Meilisearch 搜索引擎和 GetMCP API 资源获取器,支持本地和云端实例 */ import { MemoryCache } from '../../common/cache/memoryCache.js'; import { GETMCP_API_URL, CACHE_TTL_MS } from '../../../config/constants.js'; import { GetMcpResourceFetcher, } from '../../common/api/getMcpResourceFetcher.js'; import { FailoverMeilisearchClient } from '../../providers/meilisearch/controller.js'; import logger from '../../../utils/logger.js'; /** * 将 GetMCP API 响应转换为 Meilisearch 文档 */ const convertToMeilisearchDocuments = (data) => Object.entries(data).map(([id, server]) => ({ id, title: server.display_name, description: server.description, github_url: server.repository.url, categories: Array.isArray(server.categories) ? server.categories.join(',') : '', tags: Array.isArray(server.tags) ? server.tags.join(',') : '', installations: server.installations || {}, })); /** * 将 Meilisearch 搜索结果转换为 MCPServerResponse */ const convertHitToServerResponse = (hit) => ({ id: hit.id, title: hit.title, description: hit.description, sourceUrl: hit.github_url, similarity: hit._rankingScore || 0.5, installations: hit.installations || {}, }); /** * Meilisearch 搜索提供者实现 * 支持本地和云端实例,包含故障转移机制 */ export class MeilisearchSearchProvider { resourceFetcher; cache; client; /** * 构造函数 */ constructor(resourceFetcher, cache, client) { this.resourceFetcher = resourceFetcher || new GetMcpResourceFetcher(GETMCP_API_URL); this.cache = cache || new MemoryCache(CACHE_TTL_MS); this.client = client || new FailoverMeilisearchClient(); logger.info('MeilisearchSearchProvider initialized with failover support'); } /** * 搜索 MCP 服务器 */ async search(params) { const query = [params.taskDescription, ...(params.keywords || []), ...(params.capabilities || [])].join(' ').trim(); try { logger.info(`Searching for MCP servers with query: ${query}`); // 确保数据已加载 await this.ensureDataLoaded(); // 执行 Meilisearch 搜索 const results = await this.client.search(query, { limit: 10 }); const serverResponses = results.hits.map(convertHitToServerResponse); logger.debug(`Found ${serverResponses.length} results from Meilisearch`); return serverResponses; } catch (error) { logger.error(`Error searching with Meilisearch: ${error instanceof Error ? error.message : String(error)}`); throw error; } } /** * 健康检查 */ async healthCheck() { try { if (this.client.healthCheck) { return await this.client.healthCheck(); } return true; } catch (error) { logger.error('Meilisearch health check failed:', error); return false; } } /** * 确保数据已从 API 加载 */ async ensureDataLoaded() { // 检查是否有有效的缓存数据 const cachedData = this.cache.get(); if (cachedData) { return; } try { // 从 API 获取数据 const data = await this.resourceFetcher.fetchData(); // 缓存数据 this.cache.set(data); // 将数据转换为 Meilisearch 文档格式 const documents = convertToMeilisearchDocuments(data); logger.info(`Loaded ${documents.length} MCP servers`); } catch (error) { logger.error(`Error loading data: ${error instanceof Error ? error.message : String(error)}`); throw error; } } }