Skip to main content
Glama

MCP Advisor

MIT License
88
64
  • Apple
  • Linux
NacosHttpClient.ts9.4 kB
import axios from 'axios'; import { NacosMcpServer } from '../../../types/nacos.js'; import logger from '../../../utils/logger.js'; interface Tool { name: string; description: string; inputSchema: Record<string, any>; } interface NacosMcpServerConfig { name: string; description?: string; protocol?: string; remoteServerConfig?: { exportPath: string; serviceRef?: any; }; backendEndpoints?: Array<{ address: string; port: number; }>; toolSpec?: { tools?: Tool[]; [key: string]: any; }; [key: string]: any; } export class NacosHttpClient { private readonly nacosAddr: string; private readonly userName: string; private readonly passwd: string; private client: ReturnType<typeof axios.create>; constructor(nacosAddr: string, userName: string, passwd: string) { if (!nacosAddr) { throw new Error('nacosAddr cannot be an empty string'); } if (!userName) { throw new Error('userName cannot be an empty string'); } if (!passwd) { throw new Error('passwd cannot be an empty string'); } this.nacosAddr = nacosAddr; this.userName = userName; this.passwd = passwd; this.client = axios.create({ baseURL: `http://${this.nacosAddr}`, headers: { 'Content-Type': 'application/json', 'charset': 'utf-8', 'userName': this.userName, 'password': this.passwd } }); } /** * Make a GET request to the Nacos server * @param url The URL to make the request to * @param config Optional Axios request config * @returns The response data * @throws {Error} If the request fails or returns an error status */ async get<T = any>(url: string, config?: any): Promise<{ data: T }> { try { const response = await this.client.get<T>(url, config); return response; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`GET request to ${url} failed: ${errorMessage}`); throw new Error(`Failed to fetch from Nacos: ${errorMessage}`); } } /** * Get service details from Nacos server * @param serviceName The name of the service to fetch details for * @param groupName The group name of the service (default: 'DEFAULT_GROUP') * @returns Service details including metadata * @throws {Error} If the service is not found or there's an error fetching details */ async getServiceDetail(serviceName: string, groupName: string = 'DEFAULT_GROUP') { try { // First try to get the service using the MCP API if available try { const mcpServer = await this.getMcpServerByName(serviceName); if (mcpServer) { return { name: serviceName, groupName, metadata: { ...mcpServer.agentConfig, description: mcpServer.description, lastUpdated: new Date().toISOString() } }; } } catch (error) { logger.debug(`Failed to fetch MCP server details for ${serviceName}: ${error}`); // Continue with regular service API if MCP API fails } // Fall back to standard Nacos service API const response = await this.client.get(`/nacos/v1/ns/service`, { params: { serviceName, groupName, namespaceId: 'public', clusterName: 'DEFAULT', healthyOnly: false } }); if (!response.data) { throw new Error(`Service ${serviceName} not found in group ${groupName}`); } return { name: serviceName, groupName, metadata: { ...response.data, lastUpdated: new Date().toISOString() } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Failed to get service details for ${serviceName}: ${errorMessage}`); throw new Error(`Failed to get service details: ${errorMessage}`); } } async isReady(): Promise<boolean> { try { const response = await this.client.get<{ data: unknown }>('/nacos/v3/admin/ai/mcp/list'); return response.status === 200; } catch (error) { logger.warn(`Nacos health check failed: ${error}`); return false; } } async getMcpServerByName(name: string): Promise<NacosMcpServer> { const url = `/nacos/v3/admin/ai/mcp?mcpName=${name}`; const mcpServer: NacosMcpServer = { name, description: '', agentConfig: {}, }; try { interface McpServerResponse { data: NacosMcpServerConfig; } const response = await this.client.get<McpServerResponse>(url); if (response.status === 200) { const data = response.data.data; const server: NacosMcpServer = { name: data.name, description: data.description || '', agentConfig: {}, mcpConfigDetail: data, getAgentConfig: () => ({}), toDict: () => ({ ...data, description: data.description || '' }) }; // Add getName and getDescription methods if not present if (!server.getName) { server.getName = () => data.name; } if (!server.getDescription) { server.getDescription = () => data.description || ''; } if (data.protocol !== 'stdio' && data.backendEndpoints?.length) { const endpoint = data.backendEndpoints[0]; const httpSchema = endpoint.port === 443 ? 'https' : 'http'; let url = `${httpSchema}://${endpoint.address}:${endpoint.port}`; if (data.remoteServerConfig?.exportPath) { const exportPath = data.remoteServerConfig.exportPath.startsWith('/') ? data.remoteServerConfig.exportPath : `/${data.remoteServerConfig.exportPath}`; url += exportPath; } server.agentConfig = { ...server.agentConfig, mcpServers: { [server.name]: { name: server.name, description: server.description, url } } }; } return server; } } catch (error) { logger.warn(`Failed to get mcp server ${name}: ${error}`); } return mcpServer; } async getMcpServers(): Promise<NacosMcpServer[]> { const mcpServers: NacosMcpServer[] = []; try { const pageSize = 100; const pageNo = 1; const url = `/nacos/v3/admin/ai/mcp/list?pageNo=${pageNo}&pageSize=${pageSize}`; interface McpListResponse { data: { pageItems: Array<{ name: string; enabled: boolean }>; }; } const response = await this.client.get<McpListResponse>(url); if (response.status !== 200) { logger.warn(`Failed to get mcp server list, status: ${response.status}`); return []; } for (const mcpServerDict of response.data.data.pageItems) { if (mcpServerDict.enabled) { const mcpServer = await this.getMcpServerByName(mcpServerDict.name); if (mcpServer.description) { mcpServers.push(mcpServer); } } } } catch (error) { logger.error('Error getting mcp servers:', error); throw new Error(`Failed to get mcp servers: ${error}`); } return mcpServers; } async updateMcpTools(mcpName: string, tools: Tool[]): Promise<boolean> { try { const url = `/nacos/v3/admin/ai/mcp?mcpName=${mcpName}`; const response = await this.client.get<{ data: NacosMcpServerConfig }>(url); if (response.status === 200) { const data = response.data.data; const toolList = tools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })); const endpointSpecification: Record<string, any> = {}; if (data.protocol !== 'stdio' && data.remoteServerConfig?.serviceRef) { endpointSpecification.data = data.remoteServerConfig.serviceRef; endpointSpecification.type = 'REF'; } const toolSpecification = { ...(data.toolSpec || {}), tools: toolList }; const serverSpecification = { ...data }; delete serverSpecification.toolSpec; delete serverSpecification.backendEndpoints; const params = new URLSearchParams(); params.append('mcpName', mcpName); params.append('serverSpecification', JSON.stringify(serverSpecification)); params.append('endpointSpecification', JSON.stringify(endpointSpecification)); params.append('toolSpecification', JSON.stringify(toolSpecification)); logger.info(`Updating mcp tools for ${mcpName}`); const updateResponse = await this.client.put('/nacos/v3/admin/ai/mcp', params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', // Other headers like userName and password are already configured in the client } }); return updateResponse.status === 200; } return false; } catch (error) { logger.error('Error updating mcp tools:', error); return false; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/istarwyh/mcpadvisor'

If you have feedback or need assistance with the MCP directory API, please join our Discord server