From c1442a9b423c552789494932e264ecab4f118e44 Mon Sep 17 00:00:00 2001 From: Gabrielsv01 Date: Fri, 8 May 2026 07:27:45 -0300 Subject: [PATCH] fix(config): align MCP discovery semantics and MCP server editor behavior --- .../src/components/config/config-page.tsx | 58 ++++++++++++++++--- .../src/components/config/config-sections.tsx | 31 ++++++++-- web/frontend/src/i18n/locales/en.json | 4 +- 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/web/frontend/src/components/config/config-page.tsx b/web/frontend/src/components/config/config-page.tsx index d099346c7..6846cb3a8 100644 --- a/web/frontend/src/components/config/config-page.tsx +++ b/web/frontend/src/components/config/config-page.tsx @@ -43,6 +43,21 @@ import { Button } from "@/components/ui/button" import { showSaveSuccessOrRestartToast } from "@/lib/restart-required" import { refreshGatewayState } from "@/store/gateway" +function buildStringMapMergePatch( + next: Record, + previous: Record, +): Record { + const patch: Record = { ...next } + + for (const key of Object.keys(previous)) { + if (!(key in next)) { + patch[key] = null + } + } + + return patch +} + export function ConfigPage() { const { t } = useTranslation() const queryClient = useQueryClient() @@ -279,13 +294,13 @@ export function ConfigPage() { form.mcpDiscoveryTTL, "MCP discovery ttl", { - min: 0, + min: 1, }, ) mcpDiscoveryPatch.max_search_results = parseIntField( form.mcpDiscoveryMaxSearchResults, "MCP discovery max search results", - { min: 0 }, + { min: 1 }, ) } const execConfigPatch: Record = { @@ -335,8 +350,19 @@ export function ConfigPage() { .filter((name) => !currentServerNames.has(name)) .map((name) => [name, null] as const) + const baselineServersByName = new Map( + baseline.mcpServers + .map((server) => ({ + ...server, + name: server.name.trim(), + })) + .filter((server) => server.name !== "") + .map((server) => [server.name, server] as const), + ) + const upsertServerEntries = normalizedServers.map((server) => { const deferredPatch = { deferred: server.deferredOverride } + const baselineServer = baselineServersByName.get(server.name) if (server.type !== "stdio") { if (server.url === "") { @@ -364,9 +390,17 @@ export function ConfigPage() { enabled: server.enabled, type: server.type, url: server.url, - headers: parseJSONObjectField( - server.headersText, - `MCP server ${server.name} headers`, + headers: buildStringMapMergePatch( + parseJSONObjectField( + server.headersText, + `MCP server ${server.name} headers`, + ), + baselineServer + ? parseJSONObjectField( + baselineServer.headersText, + `Saved MCP server ${server.name} headers`, + ) + : {}, ), command: null, args: null, @@ -388,9 +422,17 @@ export function ConfigPage() { type: "stdio", command: server.command, args: parseMultilineList(server.argsText), - env: parseJSONObjectField( - server.envText, - `MCP server ${server.name} env`, + env: buildStringMapMergePatch( + parseJSONObjectField( + server.envText, + `MCP server ${server.name} env`, + ), + baselineServer + ? parseJSONObjectField( + baselineServer.envText, + `Saved MCP server ${server.name} env`, + ) + : {}, ), env_file: server.envFile === "" ? null : server.envFile, url: null, diff --git a/web/frontend/src/components/config/config-sections.tsx b/web/frontend/src/components/config/config-sections.tsx index 022eb8fdc..f71f025a6 100644 --- a/web/frontend/src/components/config/config-sections.tsx +++ b/web/frontend/src/components/config/config-sections.tsx @@ -279,7 +279,7 @@ export function MCPSection({ > onFieldChange("mcpDiscoveryTTL", e.target.value) @@ -294,7 +294,7 @@ export function MCPSection({ > onFieldChange( @@ -366,6 +366,7 @@ export function MCPSection({ placeholder={t( "pages.config.mcp_server_name_placeholder", )} + aria-label={t("pages.config.mcp_server_name_placeholder")} onChange={(e) => onServerFieldChange(server.id, "name", e.target.value) } @@ -381,7 +382,9 @@ export function MCPSection({ ) } > - + @@ -416,7 +419,9 @@ export function MCPSection({ ) } > - + onServerFieldChange(server.id, "url", e.target.value) } @@ -453,6 +461,9 @@ export function MCPSection({ placeholder={t( "pages.config.mcp_server_headers_placeholder", )} + aria-label={t( + "pages.config.mcp_server_headers_placeholder", + )} className="min-h-[88px] font-mono text-xs" onChange={(e) => onServerFieldChange( @@ -470,6 +481,9 @@ export function MCPSection({ placeholder={t( "pages.config.mcp_server_command_placeholder", )} + aria-label={t( + "pages.config.mcp_server_command_placeholder", + )} onChange={(e) => onServerFieldChange( server.id, @@ -483,6 +497,9 @@ export function MCPSection({ placeholder={t( "pages.config.mcp_server_env_file_placeholder", )} + aria-label={t( + "pages.config.mcp_server_env_file_placeholder", + )} onChange={(e) => onServerFieldChange( server.id, @@ -496,6 +513,9 @@ export function MCPSection({ placeholder={t( "pages.config.mcp_server_args_placeholder", )} + aria-label={t( + "pages.config.mcp_server_args_placeholder", + )} className="min-h-[88px] font-mono text-xs" onChange={(e) => onServerFieldChange( @@ -510,6 +530,9 @@ export function MCPSection({ placeholder={t( "pages.config.mcp_server_env_placeholder", )} + aria-label={t( + "pages.config.mcp_server_env_placeholder", + )} className="min-h-[88px] font-mono text-xs" onChange={(e) => onServerFieldChange( diff --git a/web/frontend/src/i18n/locales/en.json b/web/frontend/src/i18n/locales/en.json index fb58611ea..e38df8665 100644 --- a/web/frontend/src/i18n/locales/en.json +++ b/web/frontend/src/i18n/locales/en.json @@ -706,8 +706,8 @@ "mcp_enabled_hint": "Turn MCP server integration on or off.", "mcp_discovery_enabled": "Enable MCP Discovery", "mcp_discovery_enabled_hint": "Allow MCP discovery tools to search registered MCP servers.", - "mcp_discovery_ttl": "Discovery Cache TTL (minutes)", - "mcp_discovery_ttl_hint": "How long MCP discovery results stay cached.", + "mcp_discovery_ttl": "Discovered tool unlock TTL", + "mcp_discovery_ttl_hint": "How many tool-execution TTL ticks discovered tools remain available after search.", "mcp_discovery_max_results": "Discovery Max Results", "mcp_discovery_max_results_hint": "Maximum MCP discovery matches returned per query.", "mcp_discovery_use_bm25": "Use BM25 Ranking",