Files
mcphub/src/services/cloudService.ts

274 lines
7.6 KiB
TypeScript

import axios, { AxiosRequestConfig } from 'axios';
import {
CloudServer,
CloudTool,
MCPRouterResponse,
MCPRouterListServersResponse,
MCPRouterListToolsResponse,
MCPRouterCallToolResponse,
} from '../types/index.js';
import { loadOriginalSettings } from '../config/index.js';
// MCPRouter API default base URL
const DEFAULT_MCPROUTER_API_BASE = 'https://api.mcprouter.to/v1';
// Get MCPRouter API config from system configuration
const getMCPRouterConfig = () => {
const settings = loadOriginalSettings();
const mcpRouterConfig = settings.systemConfig?.mcpRouter;
return {
apiKey: mcpRouterConfig?.apiKey || process.env.MCPROUTER_API_KEY || '',
referer: mcpRouterConfig?.referer || process.env.MCPROUTER_REFERER || 'https://www.mcphubx.com',
title: mcpRouterConfig?.title || process.env.MCPROUTER_TITLE || 'MCPHub',
baseUrl:
mcpRouterConfig?.baseUrl || process.env.MCPROUTER_API_BASE || DEFAULT_MCPROUTER_API_BASE,
};
};
// Get axios config with MCPRouter headers
const getAxiosConfig = (): AxiosRequestConfig => {
const mcpRouterConfig = getMCPRouterConfig();
return {
headers: {
Authorization: mcpRouterConfig.apiKey ? `Bearer ${mcpRouterConfig.apiKey}` : '',
'HTTP-Referer': mcpRouterConfig.referer || 'https://www.mcphubx.com',
'X-Title': mcpRouterConfig.title || 'MCPHub',
'Content-Type': 'application/json',
},
};
};
// List all available cloud servers
export const getCloudServers = async (): Promise<CloudServer[]> => {
try {
const axiosConfig = getAxiosConfig();
const mcpRouterConfig = getMCPRouterConfig();
const response = await axios.post<MCPRouterResponse<MCPRouterListServersResponse>>(
`${mcpRouterConfig.baseUrl}/list-servers`,
{},
axiosConfig,
);
const data = response.data;
if (data.code !== 0) {
throw new Error(data.message || 'Failed to fetch servers');
}
return data.data.servers || [];
} catch (error) {
console.error('Error fetching cloud market servers:', error);
throw error;
}
};
// Get a specific cloud server by name
export const getCloudServerByName = async (name: string): Promise<CloudServer | null> => {
try {
const servers = await getCloudServers();
return servers.find((server) => server.name === name || server.config_name === name) || null;
} catch (error) {
console.error(`Error fetching cloud server ${name}:`, error);
throw error;
}
};
// List tools for a specific cloud server
export const getCloudServerTools = async (serverKey: string): Promise<CloudTool[]> => {
try {
const axiosConfig = getAxiosConfig();
const mcpRouterConfig = getMCPRouterConfig();
if (
!axiosConfig.headers?.['Authorization'] ||
axiosConfig.headers['Authorization'] === 'Bearer '
) {
throw new Error('MCPROUTER_API_KEY_NOT_CONFIGURED');
}
const response = await axios.post<MCPRouterResponse<MCPRouterListToolsResponse>>(
`${mcpRouterConfig.baseUrl}/list-tools`,
{
server: serverKey,
},
axiosConfig,
);
const data = response.data;
if (data.code !== 0) {
throw new Error(data.message || 'Failed to fetch tools');
}
return data.data.tools || [];
} catch (error) {
console.error(`Error fetching tools for server ${serverKey}:`, error);
throw error;
}
};
// Call a tool on a cloud server
export const callCloudServerTool = async (
serverName: string,
toolName: string,
args: Record<string, any>,
): Promise<MCPRouterCallToolResponse> => {
try {
const axiosConfig = getAxiosConfig();
const mcpRouterConfig = getMCPRouterConfig();
if (
!axiosConfig.headers?.['Authorization'] ||
axiosConfig.headers['Authorization'] === 'Bearer '
) {
throw new Error('MCPROUTER_API_KEY_NOT_CONFIGURED');
}
const response = await axios.post<MCPRouterResponse<MCPRouterCallToolResponse>>(
`${mcpRouterConfig.baseUrl}/call-tool`,
{
server: serverName,
name: toolName,
arguments: args,
},
axiosConfig,
);
const data = response.data;
if (data.code !== 0) {
throw new Error(data.message || 'Failed to call tool');
}
return data.data;
} catch (error) {
console.error(`Error calling tool ${toolName} on server ${serverName}:`, error);
throw error;
}
};
// Get all categories from cloud servers
export const getCloudCategories = async (): Promise<string[]> => {
try {
const servers = await getCloudServers();
const categories = new Set<string>();
servers.forEach((server) => {
// Extract categories from content or description
// This is a simple implementation, you might want to parse the content more sophisticatedly
if (server.content) {
const categoryMatches = server.content.match(/category[:\s]*([^,\n]+)/gi);
if (categoryMatches) {
categoryMatches.forEach((match) => {
const category = match.replace(/category[:\s]*/i, '').trim();
if (category) categories.add(category);
});
}
}
});
return Array.from(categories).sort();
} catch (error) {
console.error('Error fetching cloud market categories:', error);
throw error;
}
};
// Get all tags from cloud servers
export const getCloudTags = async (): Promise<string[]> => {
try {
const servers = await getCloudServers();
const tags = new Set<string>();
servers.forEach((server) => {
// Extract tags from content or description
if (server.content) {
const tagMatches = server.content.match(/tag[s]?[:\s]*([^,\n]+)/gi);
if (tagMatches) {
tagMatches.forEach((match) => {
const tag = match.replace(/tag[s]?[:\s]*/i, '').trim();
if (tag) tags.add(tag);
});
}
}
});
return Array.from(tags).sort();
} catch (error) {
console.error('Error fetching cloud market tags:', error);
throw error;
}
};
// Search cloud servers by query
export const searchCloudServers = async (query: string): Promise<CloudServer[]> => {
try {
const servers = await getCloudServers();
const searchTerms = query
.toLowerCase()
.split(' ')
.filter((term) => term.length > 0);
if (searchTerms.length === 0) {
return servers;
}
return servers.filter((server) => {
const searchText = [
server.name,
server.title,
server.description,
server.content,
server.author_name,
]
.join(' ')
.toLowerCase();
return searchTerms.some((term) => searchText.includes(term));
});
} catch (error) {
console.error('Error searching cloud market servers:', error);
throw error;
}
};
// Filter cloud servers by category
export const filterCloudServersByCategory = async (category: string): Promise<CloudServer[]> => {
try {
const servers = await getCloudServers();
if (!category) {
return servers;
}
return servers.filter((server) => {
const content = (server.content || '').toLowerCase();
return content.includes(category.toLowerCase());
});
} catch (error) {
console.error('Error filtering cloud market servers by category:', error);
throw error;
}
};
// Filter cloud servers by tag
export const filterCloudServersByTag = async (tag: string): Promise<CloudServer[]> => {
try {
const servers = await getCloudServers();
if (!tag) {
return servers;
}
return servers.filter((server) => {
const content = (server.content || '').toLowerCase();
return content.includes(tag.toLowerCase());
});
} catch (error) {
console.error('Error filtering cloud market servers by tag:', error);
throw error;
}
};