Fix MCP config form state preservation and validation
This commit is contained in:
parent
3a96d8b1ea
commit
f1737f7100
3 changed files with 34 additions and 12 deletions
|
|
@ -152,11 +152,13 @@ export function ConfigPage() {
|
||||||
id: `mcp-${Date.now()}-${nextIndex}`,
|
id: `mcp-${Date.now()}-${nextIndex}`,
|
||||||
name: "",
|
name: "",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
deferredOverride: null,
|
||||||
type: "stdio",
|
type: "stdio",
|
||||||
url: "",
|
url: "",
|
||||||
command: "",
|
command: "",
|
||||||
argsText: "",
|
argsText: "",
|
||||||
envText: "{}",
|
envText: "{}",
|
||||||
|
envFile: "",
|
||||||
headersText: "{}",
|
headersText: "{}",
|
||||||
}
|
}
|
||||||
updateField("mcpServers", [...form.mcpServers, server])
|
updateField("mcpServers", [...form.mcpServers, server])
|
||||||
|
|
@ -264,16 +266,20 @@ export function ConfigPage() {
|
||||||
"Cron exec timeout",
|
"Cron exec timeout",
|
||||||
{ min: 0 },
|
{ min: 0 },
|
||||||
)
|
)
|
||||||
const mcpDiscoveryTTL = parseIntField(
|
const mcpDiscoveryValidationEnabled =
|
||||||
form.mcpDiscoveryTTL,
|
form.mcpEnabled && form.mcpDiscoveryEnabled
|
||||||
"MCP discovery ttl",
|
const mcpDiscoveryTTL = mcpDiscoveryValidationEnabled
|
||||||
{ min: 0 },
|
? parseIntField(form.mcpDiscoveryTTL, "MCP discovery ttl", {
|
||||||
)
|
min: 0,
|
||||||
const mcpDiscoveryMaxSearchResults = parseIntField(
|
})
|
||||||
form.mcpDiscoveryMaxSearchResults,
|
: Number.parseInt(EMPTY_FORM.mcpDiscoveryTTL, 10)
|
||||||
"MCP discovery max search results",
|
const mcpDiscoveryMaxSearchResults = mcpDiscoveryValidationEnabled
|
||||||
{ min: 0 },
|
? parseIntField(
|
||||||
)
|
form.mcpDiscoveryMaxSearchResults,
|
||||||
|
"MCP discovery max search results",
|
||||||
|
{ min: 0 },
|
||||||
|
)
|
||||||
|
: Number.parseInt(EMPTY_FORM.mcpDiscoveryMaxSearchResults, 10)
|
||||||
const execConfigPatch: Record<string, unknown> = {
|
const execConfigPatch: Record<string, unknown> = {
|
||||||
enabled: form.execEnabled,
|
enabled: form.execEnabled,
|
||||||
}
|
}
|
||||||
|
|
@ -290,6 +296,7 @@ export function ConfigPage() {
|
||||||
name: server.name.trim(),
|
name: server.name.trim(),
|
||||||
url: server.url.trim(),
|
url: server.url.trim(),
|
||||||
command: server.command.trim(),
|
command: server.command.trim(),
|
||||||
|
envFile: server.envFile.trim(),
|
||||||
}))
|
}))
|
||||||
.filter((server) => server.name !== "")
|
.filter((server) => server.name !== "")
|
||||||
|
|
||||||
|
|
@ -321,6 +328,11 @@ export function ConfigPage() {
|
||||||
.map((name) => [name, null] as const)
|
.map((name) => [name, null] as const)
|
||||||
|
|
||||||
const upsertServerEntries = normalizedServers.map((server) => {
|
const upsertServerEntries = normalizedServers.map((server) => {
|
||||||
|
const deferredPatch =
|
||||||
|
server.deferredOverride === null
|
||||||
|
? {}
|
||||||
|
: { deferred: server.deferredOverride }
|
||||||
|
|
||||||
if (server.type !== "stdio") {
|
if (server.type !== "stdio") {
|
||||||
if (server.url === "") {
|
if (server.url === "") {
|
||||||
throw new Error(`MCP server ${server.name} requires a URL.`)
|
throw new Error(`MCP server ${server.name} requires a URL.`)
|
||||||
|
|
@ -329,6 +341,7 @@ export function ConfigPage() {
|
||||||
return [
|
return [
|
||||||
server.name,
|
server.name,
|
||||||
{
|
{
|
||||||
|
...deferredPatch,
|
||||||
enabled: server.enabled,
|
enabled: server.enabled,
|
||||||
type: server.type,
|
type: server.type,
|
||||||
url: server.url,
|
url: server.url,
|
||||||
|
|
@ -351,6 +364,7 @@ export function ConfigPage() {
|
||||||
return [
|
return [
|
||||||
server.name,
|
server.name,
|
||||||
{
|
{
|
||||||
|
...deferredPatch,
|
||||||
enabled: server.enabled,
|
enabled: server.enabled,
|
||||||
type: "stdio",
|
type: "stdio",
|
||||||
command: server.command,
|
command: server.command,
|
||||||
|
|
@ -359,9 +373,9 @@ export function ConfigPage() {
|
||||||
server.envText,
|
server.envText,
|
||||||
`MCP server ${server.name} env`,
|
`MCP server ${server.name} env`,
|
||||||
),
|
),
|
||||||
|
env_file: server.envFile === "" ? null : server.envFile,
|
||||||
url: null,
|
url: null,
|
||||||
headers: null,
|
headers: null,
|
||||||
env_file: null,
|
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,7 @@ export function MCPSection({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{server.type === "http" ? (
|
{server.type !== "stdio" ? (
|
||||||
<div className="mt-3 grid gap-3">
|
<div className="mt-3 grid gap-3">
|
||||||
<Input
|
<Input
|
||||||
value={server.url}
|
value={server.url}
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,13 @@ export interface MCPServerForm {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
deferredOverride: boolean | null
|
||||||
type: MCPServerType
|
type: MCPServerType
|
||||||
url: string
|
url: string
|
||||||
command: string
|
command: string
|
||||||
argsText: string
|
argsText: string
|
||||||
envText: string
|
envText: string
|
||||||
|
envFile: string
|
||||||
headersText: string
|
headersText: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +146,10 @@ function asBool(value: unknown): boolean {
|
||||||
return value === true
|
return value === true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function asOptionalBool(value: unknown): boolean | null {
|
||||||
|
return typeof value === "boolean" ? value : null
|
||||||
|
}
|
||||||
|
|
||||||
function asNumberString(value: unknown, fallback: string): string {
|
function asNumberString(value: unknown, fallback: string): string {
|
||||||
if (typeof value === "number" && Number.isFinite(value)) {
|
if (typeof value === "number" && Number.isFinite(value)) {
|
||||||
return String(value)
|
return String(value)
|
||||||
|
|
@ -193,11 +199,13 @@ function mapMCPServers(value: unknown): MCPServerForm[] {
|
||||||
id: makeMCPServerID(name),
|
id: makeMCPServerID(name),
|
||||||
name,
|
name,
|
||||||
enabled: cfg.enabled !== false,
|
enabled: cfg.enabled !== false,
|
||||||
|
deferredOverride: asOptionalBool(cfg.deferred),
|
||||||
type,
|
type,
|
||||||
url,
|
url,
|
||||||
command: asString(cfg.command),
|
command: asString(cfg.command),
|
||||||
argsText: argsList.join("\n"),
|
argsText: argsList.join("\n"),
|
||||||
envText: JSON.stringify(env, null, 2),
|
envText: JSON.stringify(env, null, 2),
|
||||||
|
envFile: asString(cfg.env_file),
|
||||||
headersText: JSON.stringify(headers, null, 2),
|
headersText: JSON.stringify(headers, null, 2),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue