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) {