diff --git a/web/app/components/tools/mcp/detail/content.tsx b/web/app/components/tools/mcp/detail/content.tsx index b9401d4246..5984cda2b1 100644 --- a/web/app/components/tools/mcp/detail/content.tsx +++ b/web/app/components/tools/mcp/detail/content.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect } from 'react' import type { FC } from 'react' import { useBoolean } from 'ahooks' +import copy from 'copy-to-clipboard' import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' import { @@ -15,6 +16,7 @@ import ActionButton from '@/app/components/base/action-button' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' import Indicator from '@/app/components/header/indicator' +import Tooltip from '@/app/components/base/tooltip' import MCPModal from '../modal' import OperationDropdown from './operation-dropdown' import ListLoading from './list-loading' @@ -159,7 +161,15 @@ const MCPDetailContent: FC = ({
{detail.name}
-
{detail.server_url}
+
+ +
copy(detail.server_identifier || '')}>{detail.server_identifier}
+
+
·
+ +
{detail.server_url}
+
+
void onHide: () => void } @@ -53,10 +54,11 @@ const MCPModal = ({ const { t } = useTranslation() const originalServerUrl = data?.server_url + const [url, setUrl] = React.useState(data?.server_url || '') const [name, setName] = React.useState(data?.name || '') const [appIcon, setAppIcon] = useState(getIcon(data)) - const [url, setUrl] = React.useState(data?.server_url || '') const [showAppIconPicker, setShowAppIconPicker] = useState(false) + const [serverIdentifier, setServerIdentifier] = React.useState(data?.server_identifier || '') const isValidUrl = (string: string) => { try { @@ -73,12 +75,14 @@ const MCPModal = ({ Toast.notify({ type: 'error', message: 'invalid server url' }) return } + // TODO server identifier validation await onConfirm({ - name, server_url: originalServerUrl === url ? '[__HIDDEN__]' : url.trim(), + name, icon_type: appIcon.type, icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId, icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined, + server_identifier: serverIdentifier.trim(), }) onHide() } @@ -132,9 +136,20 @@ const MCPModal = ({ />
+
+
+ {t('tools.mcp.modal.serverIdentifier')} +
+
{t('tools.mcp.modal.serverIdentifierTip')}
+ setServerIdentifier(e.target.value)} + placeholder={t('tools.mcp.modal.serverIdentifierPlaceholder')} + /> +
- +
diff --git a/web/app/components/tools/mcp/provider-card.tsx b/web/app/components/tools/mcp/provider-card.tsx index 4c2d764f25..69e00ac918 100644 --- a/web/app/components/tools/mcp/provider-card.tsx +++ b/web/app/components/tools/mcp/provider-card.tsx @@ -90,23 +90,23 @@ const MCPCard = ({
{data.name}
-
-
- - {data.tools.length > 0 && ( -
{t('tools.mcp.toolsCount', { count: data.tools.length })}
- )} - {!data.tools.length && ( -
{t('tools.mcp.noTools')}
- )} -
-
/
-
{`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}
-
+
{data.server_identifier}
-
{data.server_url}
+
+
+ + {data.tools.length > 0 && ( +
{t('tools.mcp.toolsCount', { count: data.tools.length })}
+ )} + {!data.tools.length && ( +
{t('tools.mcp.noTools')}
+ )} +
+
/
+
{`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}
+
{data.is_team_authorization && data.tools.length > 0 && } {(!data.is_team_authorization || !data.tools.length) && (
diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index cba7d92027..d444ee1f38 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -54,6 +54,7 @@ export type Collection = { // MCP Server server_url?: string updated_at?: number + server_identifier?: string } export type ToolParameter = { diff --git a/web/i18n/en-US/tools.ts b/web/i18n/en-US/tools.ts index 37526e95e1..0f340ac5fc 100644 --- a/web/i18n/en-US/tools.ts +++ b/web/i18n/en-US/tools.ts @@ -169,6 +169,9 @@ const translation = { serverUrl: 'Server URL', serverUrlPlaceholder: 'URL to server endpiont', warning: 'Updating the server address may affect applications currently using this MCP', + serverIdentifier: 'Server Identifier', + serverIdentifierTip: 'This text will be displayed on the client side, providing basic guidance on how to use the application', + serverIdentifierPlaceholder: 'Unique identifier for this server', cancel: 'Cancel', save: 'Save', confirm: 'Add & Authorize', @@ -191,6 +194,7 @@ const translation = { getTools: 'Get tools', toolsNum: '{{count}} tools included', onlyTool: '1 tool included', + identifier: 'Server Identifier (Click to Copy)', server: { title: 'MCP Server', url: 'Server URL', diff --git a/web/i18n/zh-Hans/tools.ts b/web/i18n/zh-Hans/tools.ts index 6e302c17e8..4a62ffd901 100644 --- a/web/i18n/zh-Hans/tools.ts +++ b/web/i18n/zh-Hans/tools.ts @@ -169,6 +169,9 @@ const translation = { serverUrl: '服务端点 URL', serverUrlPlaceholder: '服务端点的 URL', warning: '修改服务端点 URL 可能会影响使用当前 MCP 的应用。', + serverIdentifier: '服务器标识符', + serverIdentifierTip: '此文本将在客户端显示,为如何使用应用程序提供基本指导', + serverIdentifierPlaceholder: '此服务器的唯一标识符', cancel: '取消', save: '保存', confirm: '添加并授权', @@ -191,6 +194,7 @@ const translation = { getTools: '获取工具', toolsNum: '包含 {{count}} 个工具', onlyTool: '包含 1 个工具', + identifier: '服务器标识符 (点击复制)', server: { title: 'MCP 服务', url: '服务端点 URL',