fix(config): align MCP discovery semantics and MCP server editor behavior

This commit is contained in:
Gabrielsv01 2026-05-08 07:27:45 -03:00
parent d7f77e7744
commit c1442a9b42
3 changed files with 79 additions and 14 deletions

View file

@ -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<string, string>,
previous: Record<string, string>,
): Record<string, string | null> {
const patch: Record<string, string | null> = { ...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<string, unknown> = {
@ -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,

View file

@ -279,7 +279,7 @@ export function MCPSection({
>
<Input
type="number"
min={0}
min={1}
value={form.mcpDiscoveryTTL}
onChange={(e) =>
onFieldChange("mcpDiscoveryTTL", e.target.value)
@ -294,7 +294,7 @@ export function MCPSection({
>
<Input
type="number"
min={0}
min={1}
value={form.mcpDiscoveryMaxSearchResults}
onChange={(e) =>
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({
)
}
>
<SelectTrigger>
<SelectTrigger
aria-label={t("pages.config.mcp_server_discovery_mode")}
>
<SelectValue />
</SelectTrigger>
<SelectContent>
@ -416,7 +419,9 @@ export function MCPSection({
)
}
>
<SelectTrigger>
<SelectTrigger
aria-label={t("pages.config.mcp_server_discovery_mode")}
>
<SelectValue
placeholder={t(
"pages.config.mcp_server_discovery_mode",
@ -444,6 +449,9 @@ export function MCPSection({
placeholder={t(
"pages.config.mcp_server_url_placeholder",
)}
aria-label={t(
"pages.config.mcp_server_url_placeholder",
)}
onChange={(e) =>
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(

View file

@ -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",