'use client' import type { FC } from 'react' import type { ToolWithProvider } from '../../../workflow/types' import { RiCloseLine, RiLoader2Line, RiLoopLeftLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' import * as React from 'react' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import Icon from '@/app/components/plugins/card/base/card-icon' import { useAppContext } from '@/context/app-context' import { openOAuthPopup } from '@/hooks/use-oauth' import { useAuthorizeMCP, useDeleteMCP, useInvalidateMCPTools, useMCPTools, useUpdateMCP, useUpdateMCPTools, } from '@/service/use-tools' import { cn } from '@/utils/classnames' import MCPModal from '../modal' import ListLoading from './list-loading' import OperationDropdown from './operation-dropdown' import ToolItem from './tool-item' type Props = { detail: ToolWithProvider onUpdate: (isDelete?: boolean) => void onHide: () => void isTriggerAuthorize: boolean onFirstCreate: () => void } const MCPDetailContent: FC = ({ detail, onUpdate, onHide, isTriggerAuthorize, onFirstCreate, }) => { const { t } = useTranslation() const { isCurrentWorkspaceManager } = useAppContext() const { data, isFetching: isGettingTools } = useMCPTools(detail.is_team_authorization ? detail.id : '') const invalidateMCPTools = useInvalidateMCPTools() const { mutateAsync: updateTools, isPending: isUpdating } = useUpdateMCPTools() const { mutateAsync: authorizeMcp, isPending: isAuthorizing } = useAuthorizeMCP() const toolList = data?.tools || [] const [isShowUpdateConfirm, { setTrue: showUpdateConfirm, setFalse: hideUpdateConfirm, }] = useBoolean(false) const handleUpdateTools = useCallback(async () => { hideUpdateConfirm() if (!detail) return await updateTools(detail.id) invalidateMCPTools(detail.id) onUpdate() }, [detail, hideUpdateConfirm, invalidateMCPTools, onUpdate, updateTools]) const { mutateAsync: updateMCP } = useUpdateMCP({}) const { mutateAsync: deleteMCP } = useDeleteMCP({}) const [isShowUpdateModal, { setTrue: showUpdateModal, setFalse: hideUpdateModal, }] = useBoolean(false) const [isShowDeleteConfirm, { setTrue: showDeleteConfirm, setFalse: hideDeleteConfirm, }] = useBoolean(false) const [deleting, { setTrue: showDeleting, setFalse: hideDeleting, }] = useBoolean(false) const handleOAuthCallback = useCallback(() => { if (!isCurrentWorkspaceManager) return if (!detail.id) return handleUpdateTools() }, [detail.id, handleUpdateTools, isCurrentWorkspaceManager]) const handleAuthorize = useCallback(async () => { onFirstCreate() if (!isCurrentWorkspaceManager) return if (!detail) return try { const res = await authorizeMcp({ provider_id: detail.id, }) if (res.result === 'success') handleUpdateTools() else if (res.authorization_url) openOAuthPopup(res.authorization_url, handleOAuthCallback) } catch { // On authorization error, refresh the parent component state // to update the connection status indicator onUpdate() } }, [onFirstCreate, isCurrentWorkspaceManager, detail, authorizeMcp, handleUpdateTools, handleOAuthCallback, onUpdate]) const handleUpdate = useCallback(async (data: any) => { if (!detail) return const res = await updateMCP({ ...data, provider_id: detail.id, }) if ((res as any)?.result === 'success') { hideUpdateModal() onUpdate() handleAuthorize() } }, [detail, updateMCP, hideUpdateModal, onUpdate, handleAuthorize]) const handleDelete = useCallback(async () => { if (!detail) return showDeleting() const res = await deleteMCP(detail.id) hideDeleting() if ((res as any)?.result === 'success') { hideDeleteConfirm() onUpdate(true) } }, [detail, showDeleting, deleteMCP, hideDeleting, hideDeleteConfirm, onUpdate]) useEffect(() => { if (isTriggerAuthorize) handleAuthorize() }, []) if (!detail) return null return ( <>
{detail.name}
copy(detail.server_identifier || '')}>{detail.server_identifier}
ยท
{detail.server_url}
{!isAuthorizing && detail.is_team_authorization && ( )} {!detail.is_team_authorization && !isAuthorizing && ( )} {isAuthorizing && ( )}
{((detail.is_team_authorization && isGettingTools) || isUpdating) && ( <>
{!isUpdating &&
{t('mcp.gettingTools', { ns: 'tools' })}
} {isUpdating &&
{t('mcp.updateTools', { ns: 'tools' })}
}
)} {!isUpdating && detail.is_team_authorization && !isGettingTools && !toolList.length && (
{t('mcp.toolsEmpty', { ns: 'tools' })}
)} {!isUpdating && !isGettingTools && toolList.length > 0 && ( <>
{toolList.length > 1 &&
{t('mcp.toolsNum', { ns: 'tools', count: toolList.length })}
} {toolList.length === 1 &&
{t('mcp.onlyTool', { ns: 'tools' })}
}
{toolList.map(tool => ( ))}
)} {!isUpdating && !detail.is_team_authorization && (
{!isAuthorizing &&
{t('mcp.authorizingRequired', { ns: 'tools' })}
} {isAuthorizing &&
{t('mcp.authorizing', { ns: 'tools' })}
}
{t('mcp.authorizeTip', { ns: 'tools' })}
)}
{isShowUpdateModal && ( )} {isShowDeleteConfirm && ( {t('mcp.deleteConfirmTitle', { ns: 'tools', mcp: detail.name })} )} onCancel={hideDeleteConfirm} onConfirm={handleDelete} isLoading={deleting} isDisabled={deleting} /> )} {isShowUpdateConfirm && ( )} ) } export default MCPDetailContent