From a4d194cb076c23457be54bac945bf50eb2eeeb34 Mon Sep 17 00:00:00 2001 From: Zamil Majdy Date: Thu, 12 Feb 2026 18:08:44 +0400 Subject: [PATCH] fix(frontend): Address PR review comments - Remove legacy-builder changes: revert BlocksControl.tsx, CustomNode.tsx, and Flow.tsx to dev state - Move MCPToolDialog.tsx from legacy-builder/ to components/ and update import path in NewControlPanel/NewBlockMenu/Block.tsx - Revert out-of-scope CredentialsSelect auto-defaulting behavior - Remove `as any` cast in CredentialsInput.tsx display name --- .../{legacy-builder => }/MCPToolDialog.tsx | 0 .../NewControlPanel/NewBlockMenu/Block.tsx | 2 +- .../legacy-builder/BlocksControl.tsx | 341 ++++++++---------- .../legacy-builder/CustomNode/CustomNode.tsx | 40 +- .../components/legacy-builder/Flow/Flow.tsx | 29 +- .../CredentialsInput/CredentialsInput.tsx | 2 +- .../CredentialsSelect/CredentialsSelect.tsx | 47 +-- 7 files changed, 169 insertions(+), 292 deletions(-) rename autogpt_platform/frontend/src/app/(platform)/build/components/{legacy-builder => }/MCPToolDialog.tsx (100%) diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/MCPToolDialog.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/MCPToolDialog.tsx similarity index 100% rename from autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/MCPToolDialog.tsx rename to autogpt_platform/frontend/src/app/(platform)/build/components/MCPToolDialog.tsx diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/Block.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/Block.tsx index 2edd5686c6..07c6795808 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/Block.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/Block.tsx @@ -13,7 +13,7 @@ import { BlockUIType, SpecialBlockID } from "@/lib/autogpt-server-api"; import { MCPToolDialog, type MCPToolDialogResult, -} from "@/app/(platform)/build/components/legacy-builder/MCPToolDialog"; +} from "@/app/(platform)/build/components/MCPToolDialog"; interface Props extends ButtonHTMLAttributes { title?: string; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx index 283a7b2093..99b66fe1dc 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/BlocksControl.tsx @@ -29,10 +29,6 @@ import { TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; import { GraphMeta } from "@/lib/autogpt-server-api"; -import { - MCPToolDialog, - type MCPToolDialogResult, -} from "@/app/(platform)/build/components/legacy-builder/MCPToolDialog"; import jaro from "jaro-winkler"; import { getV1GetSpecificGraph } from "@/app/api/__generated__/endpoints/graphs/graphs"; import { okData } from "@/app/api/helpers"; @@ -98,7 +94,6 @@ export function BlocksControl({ const [searchQuery, setSearchQuery] = useState(""); const deferredSearchQuery = useDeferredValue(searchQuery); const [selectedCategory, setSelectedCategory] = useState(null); - const [mcpDialogOpen, setMcpDialogOpen] = useState(false); const blocks = useSearchableBlocks(_blocks); @@ -191,32 +186,11 @@ export function BlocksControl({ setSelectedCategory(null); }, []); - const handleMCPToolConfirm = useCallback( - (result: MCPToolDialogResult) => { - addBlock(SpecialBlockID.MCP_TOOL, "MCPToolBlock", { - server_url: result.serverUrl, - server_name: result.serverName, - selected_tool: result.selectedTool, - tool_input_schema: result.toolInputSchema, - available_tools: result.availableTools, - credentials: result.credentials ?? undefined, - }); - setMcpDialogOpen(false); - }, - [addBlock], - ); - // Handler to add a block, fetching graph data on-demand for agent blocks const handleAddBlock = useCallback( async (block: _Block & { notAvailable: string | null }) => { if (block.notAvailable) return; - // For MCP blocks, open the configuration dialog instead of placing directly - if (block.uiType === BlockUIType.MCP_TOOL) { - setMcpDialogOpen(true); - return; - } - // For agent blocks, fetch the full graph to get schemas if (block.uiType === BlockUIType.AGENT && block.hardcodedValues) { const graphID = block.hardcodedValues.graph_id as string; @@ -256,179 +230,162 @@ export function BlocksControl({ }, [blocks]); return ( - <> - open || resetFilters()} + open || resetFilters()} + > + + + + + + + Blocks + + - - - - - - - Blocks - - - - -
- -
-
- - setSearchQuery(e.target.value)} - className="rounded-lg px-8 py-5 dark:bg-slate-800 dark:text-white" - data-id="blocks-control-search-input" - autoComplete="off" - /> -
-
- {categories.map((category) => { - const color = getPrimaryCategoryColor([ - { category: category || "All", description: "" }, - ]); - const colorClass = - selectedCategory === category ? `${color}` : ""; - return ( -
- setSelectedCategory( - selectedCategory === category ? null : category, - ) - } - > - {beautifyString((category || "All").toLowerCase())} -
- ); - })} -
-
- - - {filteredAvailableBlocks.map((block) => ( - { - if ( - block.notAvailable || - block.uiType === BlockUIType.MCP_TOOL + Blocks + + +
+ + setSearchQuery(e.target.value)} + className="rounded-lg px-8 py-5 dark:bg-slate-800 dark:text-white" + data-id="blocks-control-search-input" + autoComplete="off" + /> +
+
+ {categories.map((category) => { + const color = getPrimaryCategoryColor([ + { category: category || "All", description: "" }, + ]); + const colorClass = + selectedCategory === category ? `${color}` : ""; + return ( +
+ setSelectedCategory( + selectedCategory === category ? null : category, ) - return; - e.dataTransfer.effectAllowed = "copy"; - e.dataTransfer.setData( - "application/reactflow", - JSON.stringify({ - blockId: block.id, - blockName: block.name, - hardcodedValues: block?.hardcodedValues || {}, - }), - ); - }} - onClick={() => handleAddBlock(block)} - title={block.notAvailable ?? undefined} + } > -
+ {beautifyString((category || "All").toLowerCase())} +
+ ); + })} +
+ + + + {filteredAvailableBlocks.map((block) => ( + { + if (block.notAvailable) return; + e.dataTransfer.effectAllowed = "copy"; + e.dataTransfer.setData( + "application/reactflow", + JSON.stringify({ + blockId: block.id, + blockName: block.name, + hardcodedValues: block?.hardcodedValues || {}, + }), + ); + }} + onClick={() => handleAddBlock(block)} + title={block.notAvailable ?? undefined} + > +
-
-
- - - - - - -
-
+
+ - -
+ + + + +
- - ))} - - - - - - - setMcpDialogOpen(false)} - onConfirm={handleMCPToolConfirm} - /> - +
+ +
+
+
+ ))} +
+
+
+
+
); } diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx index 7b1abdbfbd..834603cc4a 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode.tsx @@ -215,26 +215,6 @@ export const CustomNode = React.memo( } } - // MCP Tool block: display the selected tool's dynamic schema - const isMCPWithTool = - data.uiType === BlockUIType.MCP_TOOL && - !!data.hardcodedValues?.tool_input_schema?.properties; - - if (isMCPWithTool) { - // Show only the tool's input parameters. Credentials are NOT included - // because authentication is handled by the MCP dialog's OAuth flow - // and stored server-side. - const toolSchema = data.hardcodedValues.tool_input_schema; - - data.inputSchema = { - type: "object", - properties: { - ...(toolSchema.properties ?? {}), - }, - required: [...(toolSchema.required ?? [])], - } as BlockIORootSchema; - } - const setHardcodedValues = useCallback( (values: any) => { updateNodeData(id, { hardcodedValues: values }); @@ -395,9 +375,7 @@ export const CustomNode = React.memo( const displayTitle = customTitle || - (isMCPWithTool - ? `${data.hardcodedValues.server_name || "MCP"}: ${beautifyString(data.hardcodedValues.selected_tool || "")}` - : beautifyString(data.blockType?.replace(/Block$/, "") || data.title)); + beautifyString(data.blockType?.replace(/Block$/, "") || data.title); useEffect(() => { isInitialSetup.current = false; @@ -411,15 +389,6 @@ export const CustomNode = React.memo( data.inputSchema, ), }); - } else if (isMCPWithTool) { - // MCP dialog already configured server_url, selected_tool, etc. - // Just ensure tool_arguments is initialized. - if (!data.hardcodedValues.tool_arguments) { - setHardcodedValues({ - ...data.hardcodedValues, - tool_arguments: {}, - }); - } } else { setHardcodedValues( fillObjectDefaultsFromSchema(data.hardcodedValues, data.inputSchema), @@ -556,11 +525,8 @@ export const CustomNode = React.memo( ); default: - const getInputPropKey = (key: string) => { - if (nodeType == BlockUIType.AGENT) return `inputs.${key}`; - if (isMCPWithTool) return `tool_arguments.${key}`; - return key; - }; + const getInputPropKey = (key: string) => + nodeType == BlockUIType.AGENT ? `inputs.${key}` : key; return keys.map(([propKey, propSchema]) => { const isRequired = data.inputSchema.required?.includes(propKey); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/Flow/Flow.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/Flow/Flow.tsx index c12ec18ce1..babe10b912 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/Flow/Flow.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/Flow/Flow.tsx @@ -42,11 +42,7 @@ import { getV1GetSpecificGraph } from "@/app/api/__generated__/endpoints/graphs/ import { okData } from "@/app/api/helpers"; import { IncompatibilityInfo } from "../../../hooks/useSubAgentUpdate/types"; import { Key, storage } from "@/services/storage/local-storage"; -import { - beautifyString, - findNewlyAddedBlockCoordinates, - getTypeColor, -} from "@/lib/utils"; +import { findNewlyAddedBlockCoordinates, getTypeColor } from "@/lib/utils"; import { history } from "../history"; import { CustomEdge } from "../CustomEdge/CustomEdge"; import ConnectionLine from "../ConnectionLine"; @@ -752,27 +748,6 @@ const FlowEditor: React.FC<{ block_id: blockID, isOutputStatic: nodeSchema.staticOutput, uiType: nodeSchema.uiType, - // Set customized_name at creation so it persists through save/load - ...(nodeSchema.uiType === BlockUIType.MCP_TOOL && { - metadata: { - credentials_optional: true, - ...(finalHardcodedValues.selected_tool && { - customized_name: `${ - finalHardcodedValues.server_name || - (() => { - try { - return new URL(finalHardcodedValues.server_url).hostname; - } catch { - return "MCP"; - } - })() - }: ${beautifyString(finalHardcodedValues.selected_tool)}`, - }), - }, - }), - ...(blockID === SpecialBlockID.AGENT && { - metadata: { customized_name: blockName }, - }), }, }; @@ -902,6 +877,8 @@ const FlowEditor: React.FC<{ return ( node.data.metadata?.customized_name || + (node.data.uiType == BlockUIType.AGENT && + node.data.hardcodedValues.agent_name) || node.data.blockType.replace(/Block$/, "") ); }, diff --git a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/CredentialsInput.tsx b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/CredentialsInput.tsx index 6dc692cc9c..eba814fe08 100644 --- a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/CredentialsInput.tsx +++ b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/CredentialsInput.tsx @@ -86,7 +86,7 @@ export function CredentialsInput({ handleCredentialSelect, } = hookData; - const displayName = (schema as any).display_name || toDisplayName(provider); + const displayName = toDisplayName(provider); const selectedCredentialIsSystem = selectedCredential && isSystemCredential(selectedCredential); diff --git a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/CredentialsSelect/CredentialsSelect.tsx b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/CredentialsSelect/CredentialsSelect.tsx index 8f1b06f120..18e772dd00 100644 --- a/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/CredentialsSelect/CredentialsSelect.tsx +++ b/autogpt_platform/frontend/src/components/contextual/CredentialsInput/components/CredentialsSelect/CredentialsSelect.tsx @@ -1,5 +1,4 @@ import { CredentialsMetaInput } from "@/app/api/__generated__/models/credentialsMetaInput"; -import { useEffect, useRef } from "react"; import { getCredentialDisplayName } from "../../helpers"; import { CredentialRow } from "../CredentialRow/CredentialRow"; @@ -42,23 +41,17 @@ export function CredentialsSelect({ } } - // Resolve the selected credential — treat stale/deleted IDs as unselected const selectedCredential = selectedCredentials ? credentials.find((c) => c.id === selectedCredentials.id) : null; - // When credentials exist and nothing is matched, - // default to the first credential instead of "None" - const effectiveCredential = - selectedCredential ?? (credentials.length > 0 ? credentials[0] : null); - - const displayCredential = effectiveCredential + const displayCredential = selectedCredential ? { - id: effectiveCredential.id, - title: effectiveCredential.title, - username: effectiveCredential.username, - type: effectiveCredential.type, - provider: effectiveCredential.provider, + id: selectedCredential.id, + title: selectedCredential.title, + username: selectedCredential.username, + type: selectedCredential.type, + provider: selectedCredential.provider, } : allowNone ? { @@ -74,37 +67,16 @@ export function CredentialsSelect({ provider: provider, }; - // Use matched credential ID (not the raw selectedCredentials.id which may be stale) - const defaultValue = - effectiveCredential?.id ?? - (credentials.length > 0 ? credentials[0].id : "__none__"); - - // Notify parent when defaulting to a credential so the value is captured on submit - const hasNotifiedDefault = useRef(false); - useEffect(() => { - if (hasNotifiedDefault.current) return; - if (selectedCredential) return; // Already matched — no need to override - if (credentials.length > 0) { - hasNotifiedDefault.current = true; - onSelectCredential(credentials[0].id); - } - }, [credentials, selectedCredential, onSelectCredential]); - return (