diff --git a/gui/src/pages/config/sections/ToolsSection.tsx b/gui/src/pages/config/sections/ToolsSection.tsx index a48bd5df7ac..7984548b0e4 100644 --- a/gui/src/pages/config/sections/ToolsSection.tsx +++ b/gui/src/pages/config/sections/ToolsSection.tsx @@ -160,7 +160,8 @@ function MCPServerPreview({ sectionKey: string; }) => { const isExpanded = expandedSections[sectionKey]; - const hasItems = items.length > 0; + const safeItems = Array.isArray(items) ? items : []; + const hasItems = safeItems.length > 0; return (
@@ -176,7 +177,7 @@ function MCPServerPreview({ {icon} {title}
- {items.length} + {safeItems.length}
@@ -186,14 +187,14 @@ function MCPServerPreview({
{hasItems ? (
- {items.map((item, idx) => { + {safeItems.map((item, idx) => { return (
- {item.name} - {item.description && ( + {item?.name ?? "Unknown"} + {item?.description && (
{item.description}
@@ -337,7 +338,7 @@ function MCPServerPreview({ allToolsOff={allToolsOff} duplicateDetection={duplicateDetection} /> - {server.prompts.length > 0 && ( + {Array.isArray(server.prompts) && server.prompts.length > 0 && ( )} - {(server.resources.length > 0 || - server.resourceTemplates.length > 0) && ( + {(Array.isArray(server.resources) && server.resources.length > 0) || + (Array.isArray(server.resourceTemplates) && + server.resourceTemplates.length > 0) ? ( } sectionKey={`${server.id}-resources`} /> - )} + ) : null}
{/* Error display below expandable section */} @@ -447,10 +452,26 @@ export function ToolsSection() { .map((server) => [server.name, server]) ?? [], ); - return (servers ?? []).map((doc: MCPServerStatus) => ({ - block: doc, - blockFromYaml: yamlServersByName.get(doc.name), - })); + // Filter out any invalid servers and ensure they have required properties + return (servers ?? []) + .filter((doc: MCPServerStatus) => { + // Ensure server has required properties to prevent render issues + return ( + doc && + typeof doc.id === "string" && + typeof doc.name === "string" && + doc.name.length > 0 && + Array.isArray(doc.prompts) && + Array.isArray(doc.resources) && + Array.isArray(doc.resourceTemplates) && + Array.isArray(doc.errors) && + Array.isArray(doc.infos) + ); + }) + .map((doc: MCPServerStatus) => ({ + block: doc, + blockFromYaml: yamlServersByName.get(doc.name), + })); }, [servers, selectedProfile]); const handleAddMcpServer = () => { @@ -524,7 +545,7 @@ export function ToolsSection() { {mergedBlocks.length > 0 ? ( mergedBlocks.map(({ block, blockFromYaml }) => ( { + // Filter out invalid servers that could cause render loops + return ( + server && + typeof server === "object" && + typeof server.id === "string" && + server.id.length > 0 && + typeof server.name === "string" && + server.name.length > 0 && + // Ensure arrays exist even if empty + Array.isArray(server.prompts) && + Array.isArray(server.resources) && + Array.isArray(server.resourceTemplates) && + Array.isArray(server.errors) && + Array.isArray(server.infos) + ); + }); +} + export const INITIAL_CONFIG_SLICE: ConfigState = { configError: undefined, config: EMPTY_CONFIG, @@ -66,7 +96,13 @@ export const configSlice = createSlice({ if (!config) { state.config = EMPTY_CONFIG; } else { - state.config = config; + // Sanitize MCP server statuses to prevent render issues + state.config = { + ...config, + mcpServerStatuses: sanitizeMcpServerStatuses( + config.mcpServerStatuses, + ), + }; } state.loading = false; }, @@ -74,7 +110,11 @@ export const configSlice = createSlice({ state, { payload: config }: PayloadAction, ) => { - state.config = config; + // Sanitize MCP server statuses when updating config + state.config = { + ...config, + mcpServerStatuses: sanitizeMcpServerStatuses(config.mcpServerStatuses), + }; }, setConfigLoading: (state, { payload: loading }: PayloadAction) => { state.loading = loading;