'use client' import type { ComponentProps, KeyboardEvent, MouseEvent } from 'react' import type { ToolWithProvider } from '../../workflow/types' import { AlertDialog, AlertDialogActions, AlertDialogCancelButton, AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, } from '@langgenius/dify-ui/alert-dialog' import { cn } from '@langgenius/dify-ui/cn' import { StatusDot } from '@langgenius/dify-ui/status-dot' import { useBoolean } from 'ahooks' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Icon from '@/app/components/plugins/card/base/card-icon' import { useCanManageMCP } from '@/app/components/tools/hooks/use-tool-permissions' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import { useDeleteMCP, useUpdateMCP } from '@/service/use-tools' import OperationDropdown from './detail/operation-dropdown' import MCPModal from './modal' type Props = Readonly<{ currentProvider?: ToolWithProvider data: ToolWithProvider handleSelect: (providerID: string) => void onUpdate: (providerID: string) => void onDeleted: () => void }> type MCPModalConfirmPayload = Parameters['onConfirm']>[0] type MutationResult = { result?: string } const isMCPCardActionTarget = (target: EventTarget | null) => { return target instanceof HTMLElement && Boolean(target.closest('[data-mcp-card-action]')) } const MCPCard = ({ currentProvider, data, onUpdate, handleSelect, onDeleted }: Props) => { const { t } = useTranslation() const { formatTimeFromNow } = useFormatTimeFromNow() const canManageMCP = useCanManageMCP() const isConfigured = data.is_team_authorization && data.tools.length > 0 const updatedAtText = data.updated_at ? `${t(($) => $['mcp.updateTime'], { ns: 'tools' })} ${formatTimeFromNow(data.updated_at * 1000)}` : undefined const { mutateAsync: updateMCP } = useUpdateMCP({}) const { mutateAsync: deleteMCP } = useDeleteMCP({}) const [isOperationShow, setIsOperationShow] = useState(false) 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 handleUpdate = useCallback( async (form: MCPModalConfirmPayload) => { if (!canManageMCP) return const res = (await updateMCP({ ...form, provider_id: data.id, })) as MutationResult if (res.result === 'success') { hideUpdateModal() onUpdate(data.id) } }, [canManageMCP, data, updateMCP, hideUpdateModal, onUpdate], ) const handleDelete = useCallback(async () => { if (!canManageMCP) return showDeleting() const res = (await deleteMCP(data.id)) as MutationResult hideDeleting() if (res.result === 'success') { hideDeleteConfirm() onDeleted() } }, [canManageMCP, showDeleting, deleteMCP, data.id, hideDeleting, hideDeleteConfirm, onDeleted]) const handleSelectProvider = useCallback(() => { handleSelect(data.id) }, [data.id, handleSelect]) const handleCardClick = useCallback( (event: MouseEvent) => { if (isMCPCardActionTarget(event.target)) return handleSelectProvider() }, [handleSelectProvider], ) const handleCardKeyDown = useCallback( (event: KeyboardEvent) => { if (isMCPCardActionTarget(event.target)) return if (event.key !== 'Enter' && event.key !== ' ') return event.preventDefault() handleSelectProvider() }, [handleSelectProvider], ) return (
{data.name}
{data.server_identifier}
{data.tools.length > 0 && (
{t(($) => $['mcp.toolsCount'], { ns: 'tools', count: data.tools.length })}
)} {!data.tools.length && (
{t(($) => $['mcp.noTools'], { ns: 'tools' })}
)} {updatedAtText && ( <>
ยท
{updatedAtText}
)}
{isConfigured && } {!isConfigured && (
{t(($) => $['mcp.noConfigured'], { ns: 'tools' })}
)}
{canManageMCP && ( )} {canManageMCP && isShowUpdateModal && ( )} !open && hideDeleteConfirm()} >
{t(($) => $['mcp.delete'], { ns: 'tools' })}
{t(($) => $['mcp.deleteConfirmTitle'], { ns: 'tools', mcp: data.name })}
{t(($) => $['operation.cancel'], { ns: 'common' })} {t(($) => $['operation.confirm'], { ns: 'common' })}
) } export default MCPCard