import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { CloudServer, CloudServerTool, ServerConfig } from '@/types'; import { apiGet } from '@/utils/fetchInterceptor'; import { useSettingsData } from '@/hooks/useSettingsData'; import MCPRouterApiKeyError from './MCPRouterApiKeyError'; import ServerForm from './ServerForm'; interface CloudServerDetailProps { serverName: string; onBack: () => void; onCallTool?: (serverName: string, toolName: string, args: Record) => Promise; fetchServerTools?: (serverName: string) => Promise; onInstall?: (server: CloudServer, config: ServerConfig) => void; installing?: boolean; isInstalled?: boolean; } const CloudServerDetail: React.FC = ({ serverName, onBack, onCallTool, fetchServerTools, onInstall, installing = false, isInstalled = false }) => { const { t } = useTranslation(); const { mcpRouterConfig } = useSettingsData(); const [server, setServer] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [tools, setTools] = useState([]); const [loadingTools, setLoadingTools] = useState(false); const [toolsApiKeyError, setToolsApiKeyError] = useState(false); const [toolCallLoading, setToolCallLoading] = useState(null); const [toolCallResults, setToolCallResults] = useState>({}); const [toolArgs, setToolArgs] = useState>>({}); const [expandedSchemas, setExpandedSchemas] = useState>({}); const [modalVisible, setModalVisible] = useState(false); const [installError, setInstallError] = useState(null); // Helper function to check if error is MCPRouter API key not configured const isMCPRouterApiKeyError = (errorMessage: string) => { console.error('Checking for MCPRouter API key error:', errorMessage); return errorMessage === 'MCPROUTER_API_KEY_NOT_CONFIGURED' || errorMessage.toLowerCase().includes('mcprouter api key not configured'); }; // Helper function to determine button state for install const getInstallButtonProps = () => { if (isInstalled) { return { className: "bg-green-600 cursor-default px-4 py-2 rounded text-sm font-medium text-white", disabled: true, text: t('market.installed') }; } else if (installing) { return { className: "bg-gray-400 cursor-not-allowed px-4 py-2 rounded text-sm font-medium text-white", disabled: true, text: t('market.installing') }; } else { return { className: "bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded text-sm font-medium text-white transition-colors", disabled: false, text: t('market.install') }; } }; // Handle install button click const handleInstall = () => { if (!isInstalled && onInstall) { setModalVisible(true); setInstallError(null); } }; // Handle modal close const handleModalClose = () => { setModalVisible(false); setInstallError(null); }; // Handle install form submission const handleInstallSubmit = async (payload: any) => { try { if (!server || !onInstall) return; setInstallError(null); onInstall(server, payload.config); setModalVisible(false); } catch (err) { console.error('Error installing server:', err); setInstallError(t('errors.serverInstall')); } }; // Load server details useEffect(() => { const loadServerDetails = async () => { try { setLoading(true); setError(null); const response = await apiGet(`/cloud/servers/${serverName}`); if (response && response.success && response.data) { setServer(response.data); setTools(response.data.tools || []); } else { setError(t('cloud.serverNotFound')); } } catch (err) { console.error('Failed to load server details:', err); setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }; loadServerDetails(); }, [serverName, t]); // Load tools if not already loaded useEffect(() => { const loadTools = async () => { if (server && (!server.tools || server.tools.length === 0) && fetchServerTools) { setLoadingTools(true); setToolsApiKeyError(false); try { const fetchedTools = await fetchServerTools(server.name); setTools(fetchedTools); } catch (error) { console.error('Failed to load tools:', error); const errorMessage = error instanceof Error ? error.message : String(error); if (isMCPRouterApiKeyError(errorMessage)) { setToolsApiKeyError(true); } } finally { setLoadingTools(false); } } }; loadTools(); }, [server?.name, server?.tools, fetchServerTools]); // Format creation date const formatDate = (dateStr: string) => { try { const date = new Date(dateStr); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } catch { return dateStr; } }; // Handle tool argument changes const handleArgChange = (toolName: string, argName: string, value: any) => { setToolArgs(prev => ({ ...prev, [toolName]: { ...prev[toolName], [argName]: value } })); }; // Handle tool call const handleCallTool = async (toolName: string) => { if (!onCallTool || !server) return; setToolCallLoading(toolName); try { const args = toolArgs[toolName] || {}; const result = await onCallTool(server.server_key, toolName, args); setToolCallResults(prev => ({ ...prev, [toolName]: result })); } catch (error) { console.error('Tool call failed:', error); const errorMessage = error instanceof Error ? error.message : String(error); setToolCallResults(prev => ({ ...prev, [toolName]: { error: errorMessage } })); } finally { setToolCallLoading(null); } }; // Toggle schema visibility const toggleSchema = (toolName: string) => { setExpandedSchemas(prev => ({ ...prev, [toolName]: !prev[toolName] })); }; // Render tool input field based on schema const renderToolInput = (tool: CloudServerTool, propName: string, propSchema: any) => { const currentValue = toolArgs[tool.name]?.[propName] || ''; const handleChange = (e: React.ChangeEvent) => { let value: any = e.target.value; // Convert value based on schema type if (propSchema.type === 'number' || propSchema.type === 'integer') { value = value === '' ? undefined : Number(value); } else if (propSchema.type === 'boolean') { value = e.target.value === 'true'; } handleArgChange(tool.name, propName, value); }; if (propSchema.type === 'boolean') { return ( ); } else if (propSchema.type === 'number' || propSchema.type === 'integer') { return ( ); } else { return ( ); } }; return (
{loading ? (

{t('app.loading')}

) : error && !isMCPRouterApiKeyError(error) ? (

{error}

) : !server ? (

{t('cloud.serverNotFound')}

) : (
{/* Server Header Card */}

{server.title || server.name}

{server.name}
{t('cloud.by')} {server.author_name}
{t('cloud.updated')}: {formatDate(server.updated_at)}
{onInstall && !isMCPRouterApiKeyError(error || '') && !toolsApiKeyError && ( )}
{/* Description Card */}

{t('cloud.description')}

{server.description}

{/* Content Card */} {server.content && (

{t('cloud.details')}

{server.content}
)} {/* Tools Card */}

{t('cloud.tools')} {tools.length > 0 && ( {tools.length} )}

{/* Check for API key error */} {toolsApiKeyError && ( )} {loadingTools ? (
{t('cloud.loadingTools')}
) : tools.length === 0 && !toolsApiKeyError ? (

{t('cloud.noTools')}

) : tools.length > 0 ? (
{tools.map((tool, index) => (

TOOL {tool.name}

{tool.description}

{onCallTool && ( )}
{/* Tool inputs */} {tool.inputSchema && tool.inputSchema.properties && Object.keys(tool.inputSchema.properties).length > 0 && (

{t('cloud.parameters')}

{/* Schema content */} {expandedSchemas[tool.name] && (
                                {JSON.stringify(tool.inputSchema, null, 2)}
                              
)}
{Object.entries(tool.inputSchema.properties).map(([propName, propSchema]: [string, any]) => (
{propSchema.description && (

{propSchema.description}

)} {renderToolInput(tool, propName, propSchema)}
))}
)} {/* Tool call result */} {toolCallResults[tool.name] && (
{toolCallResults[tool.name].error ? ( <> {isMCPRouterApiKeyError(toolCallResults[tool.name].error) ? ( ) : ( <>

{t('cloud.error')}

                                    {toolCallResults[tool.name].error}
                                  
)} ) : ( <>

{t('cloud.result')}

                                {JSON.stringify(toolCallResults[tool.name], null, 2)}
                              
)}
)}
))}
) : null}
)} {/* Install Modal */} {modalVisible && server && (
'}`, 'HTTP-Referer': mcpRouterConfig.referer || '', 'X-Title': mcpRouterConfig.title || '' } } }} />
)}
); }; export default CloudServerDetail;