From 48969b49f771ce4b68cdedbc29d4ff89294c25b2 Mon Sep 17 00:00:00 2001 From: wizardchen Date: Thu, 16 Jul 2026 19:24:37 +0800 Subject: [PATCH] fix(agent): show disabled and unavailable MCP services in editor selector Preserve selected MCP IDs in the agent editor when services are disabled or missing, display them as disabled options with clear labels, and tighten setting-row layout so long multi-select tags do not overflow. --- frontend/src/i18n/locales/en-US.ts | 1 + frontend/src/i18n/locales/ko-KR.ts | 1 + frontend/src/i18n/locales/ru-RU.ts | 3 +- frontend/src/i18n/locales/zh-CN.ts | 1 + frontend/src/views/agent/AgentEditorModal.vue | 80 ++++++++++++++++--- 5 files changed, 72 insertions(+), 14 deletions(-) diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index 43767bdc..bec39e97 100755 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -5936,6 +5936,7 @@ export default { authWaitTimeout: 'Authentication Wait Timeout (s)', authWaitTimeoutDesc: 'Maximum seconds to wait for you to complete OAuth authentication when prompted during a conversation; the prompt is skipped after it elapses (only affects OAuth MCP services).', authWaitTimeoutPlaceholder: 'Default 600 seconds', + unavailableService: 'Unavailable service', }, llmCallTimeout: { label: "LLM Call Timeout", diff --git a/frontend/src/i18n/locales/ko-KR.ts b/frontend/src/i18n/locales/ko-KR.ts index 5b3f56f4..a9131acf 100755 --- a/frontend/src/i18n/locales/ko-KR.ts +++ b/frontend/src/i18n/locales/ko-KR.ts @@ -5940,6 +5940,7 @@ export default { authWaitTimeout: '인증 대기 시간(초)', authWaitTimeoutDesc: '대화 중 OAuth 인증이 필요할 때 인증 완료를 기다리는 최대 시간(초)이며, 초과하면 인증 요청을 건너뜁니다(OAuth를 사용하는 MCP 서비스에만 적용).', authWaitTimeoutPlaceholder: '기본 600초', + unavailableService: '사용할 수 없는 서비스', }, llmCallTimeout: { label: "LLM 호출 타임아웃", diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts index db81516b..874bd2ba 100755 --- a/frontend/src/i18n/locales/ru-RU.ts +++ b/frontend/src/i18n/locales/ru-RU.ts @@ -5438,7 +5438,8 @@ export default { selectDesc: 'Выберите MCP-сервисы для включения', selectPlaceholder: 'Выберите MCP-сервисы', authWaitTimeout: 'Тайм-аут ожидания аутентификации (с)', - authWaitTimeoutDesc: 'Максимальное время ожидания (в секундах) завершения OAuth-аутентификации при запросе во время диалога; по его истечении запрос пропускается (влияет только на MCP-сервисы с OAuth).' + authWaitTimeoutDesc: 'Максимальное время ожидания (в секундах) завершения OAuth-аутентификации при запросе во время диалога; по его истечении запрос пропускается (влияет только на MCP-сервисы с OAuth).', + unavailableService: 'Недоступный сервис', }, llmCallTimeout: { label: 'Таймаут вызова LLM', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index 243a4e95..fe3c4501 100755 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -5961,6 +5961,7 @@ export default { authWaitTimeout: "授权等待超时(秒)", authWaitTimeoutDesc: "对话中触发 OAuth 授权时,等待你完成授权的最长秒数,超时后自动跳过授权提示(仅对使用 OAuth 的 MCP 服务生效)。", authWaitTimeoutPlaceholder: "默认 600 秒", + unavailableService: "不可用服务", }, llmCallTimeout: { label: "LLM 调用超时", diff --git a/frontend/src/views/agent/AgentEditorModal.vue b/frontend/src/views/agent/AgentEditorModal.vue index 84c76ddd..28371e7d 100644 --- a/frontend/src/views/agent/AgentEditorModal.vue +++ b/frontend/src/views/agent/AgentEditorModal.vue @@ -1189,7 +1189,7 @@ -
+

{{ $t('agentEditor.mcp.selectDesc') }}

@@ -1197,7 +1197,8 @@
- +
@@ -1641,7 +1642,6 @@ import { } from '@/api/agent'; import { type ModelConfig } from '@/api/model'; import { type AgentNotReadyReasonKey, agentRequiresRerankModel } from '@/utils/agent-readiness'; -import { type MCPService } from '@/api/mcp-service'; import { type SkillInfo } from '@/api/skill'; import { type WebSearchProviderEntity } from '@/api/web-search-provider'; import { type StorageEngineStatusItem, type PromptTemplate, type PromptTemplatesConfig } from '@/api/system'; @@ -1817,7 +1817,43 @@ const agentTypePresets = ref([]); // Agent 系统提示词模板缓存(用于切换智能体类型时根据 system_prompt_id 解析出实际文本填入) const agentSystemPromptTemplates = ref([]); const intentPromptTemplates = ref([]); -const mcpOptions = ref<{ label: string; value: string }[]>([]); +type McpSelectOption = { label: string; value: string; disabled?: boolean }; + +const mcpOptions = computed(() => { + const services = editorResources.mcpServices || []; + const selectedIds = new Set(formData.value.config.mcp_services || []); + const serviceById = new Map(services.map((mcp) => [mcp.id, mcp])); + const options: McpSelectOption[] = []; + + for (const mcp of services) { + if (mcp.enabled) { + options.push({ label: mcp.name, value: mcp.id }); + } + } + + for (const id of selectedIds) { + const mcp = serviceById.get(id); + if (mcp && !mcp.enabled) { + options.push({ + label: `${mcp.name} (${t('mcpSettings.disabled')})`, + value: mcp.id, + disabled: true, + }); + } else if (!mcp) { + options.push({ + label: t('agentEditor.mcp.unavailableService'), + value: id, + disabled: true, + }); + } + } + + return options; +}); + +const showMcpServiceSelect = computed(() => + mcpOptions.value.length > 0 || (formData.value.config.mcp_services?.length ?? 0) > 0, +); const webSearchProviderList = ref([]); const skillOptions = ref<{ name: string; description: string }[]>([]); // 是否允许启用 Skills(取决于后端沙箱是否启用,disabled 时为 false;未请求前为 false 避免闪显) @@ -3293,10 +3329,6 @@ const loadDependencies = async () => { .map((shared: any) => mapKbToOption(shared.knowledge_base, true, shared.org_name)); kbOptions.value = [...myKbs, ...sharedKbs]; - mcpOptions.value = editorResources.mcpServices - .filter((mcp: MCPService) => mcp.enabled) - .map((mcp: MCPService) => ({ label: mcp.name, value: mcp.id })); - skillsAvailable.value = editorResources.skillsAvailable; skillOptions.value = editorResources.skills; @@ -4606,8 +4638,10 @@ const handleSave = async () => { display: flex; align-items: flex-start; justify-content: space-between; + gap: 24px; padding: 16px 0; border-bottom: 1px solid var(--td-component-stroke); + min-width: 0; &:last-child { border-bottom: none; @@ -4665,9 +4699,10 @@ const handleSave = async () => { } .setting-info { - flex: 1; - max-width: 55%; - padding-right: 24px; + flex: 0 0 42%; + max-width: 42%; + min-width: 0; + padding-right: 0; &.full-width { max-width: 100%; @@ -4711,15 +4746,18 @@ const handleSave = async () => { } .setting-control { - flex-shrink: 0; - min-width: 360px; + flex: 1 1 58%; + min-width: 0; + max-width: 58%; display: flex; justify-content: flex-end; align-items: flex-start; + overflow: hidden; &.setting-control-full { width: 100%; min-width: 100%; + max-width: 100%; justify-content: flex-start; } @@ -4728,6 +4766,22 @@ const handleSave = async () => { :deep(.t-input), :deep(.t-textarea) { width: 100%; + min-width: 0; + } + + :deep(.t-select-input) { + min-width: 0; + } + + :deep(.t-select .t-tag) { + max-width: 160px; + } + + :deep(.t-select .t-tag__text) { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } :deep(.t-input-number) {