@xiaohui-wang/mcpadvisor
Version:
MCP Advisor & Installation - Find the right MCP server for your needs
112 lines (111 loc) • 3.92 kB
JavaScript
/**
* 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;
}
}
}