mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-10 14:55:16 -05:00
fix(mcp): Set customized_name at block creation and add pre-run validation
- Set customized_name in metadata when MCP and Agent blocks are created (both legacy and new builder) so titles persist through save/load - Remove convoluted agent_name fallback from NodeHeader and getNodeTitle - Add custom block-level validation in graph pre-run checks so MCP tool arguments are validated before execution - Fix server_name fallback to URL hostname in discover_tools endpoint
This commit is contained in:
@@ -120,7 +120,11 @@ async def discover_tools(
|
||||
)
|
||||
for t in tools
|
||||
],
|
||||
server_name=init_result.get("serverInfo", {}).get("name"),
|
||||
server_name=(
|
||||
init_result.get("serverInfo", {}).get("name")
|
||||
or urlparse(request.server_url).hostname
|
||||
or "MCP"
|
||||
),
|
||||
protocol_version=init_result.get("protocolVersion"),
|
||||
)
|
||||
except HTTPClientError as e:
|
||||
|
||||
@@ -809,6 +809,19 @@ class GraphModel(Graph, GraphMeta):
|
||||
"'credentials' and `*_credentials` are reserved"
|
||||
)
|
||||
|
||||
# Check custom block-level validation (e.g., MCP dynamic tool arguments).
|
||||
# Blocks can override get_missing_input to report additional missing fields
|
||||
# beyond the standard top-level required fields.
|
||||
if for_run:
|
||||
credential_fields = InputSchema.get_credentials_fields()
|
||||
custom_missing = InputSchema.get_missing_input(node.input_default)
|
||||
for field_name in custom_missing:
|
||||
if (
|
||||
field_name not in provided_inputs
|
||||
and field_name not in credential_fields
|
||||
):
|
||||
node_errors[node.id][field_name] = "This field is required"
|
||||
|
||||
# Get input schema properties and check dependencies
|
||||
input_fields = InputSchema.model_fields
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/atoms/Tooltip/BaseTooltip";
|
||||
import { SpecialBlockID } from "@/lib/autogpt-server-api";
|
||||
import { beautifyString, cn } from "@/lib/utils";
|
||||
import { useState } from "react";
|
||||
import { CustomNodeData } from "../CustomNode";
|
||||
@@ -21,31 +20,8 @@ type Props = {
|
||||
|
||||
export const NodeHeader = ({ data, nodeId }: Props) => {
|
||||
const updateNodeData = useNodeStore((state) => state.updateNodeData);
|
||||
const isMCPWithTool =
|
||||
data.block_id === SpecialBlockID.MCP_TOOL &&
|
||||
!!data.hardcodedValues?.selected_tool;
|
||||
|
||||
// Derive MCP server label: prefer server_name, fall back to URL hostname.
|
||||
let mcpServerLabel = "MCP";
|
||||
if (isMCPWithTool) {
|
||||
mcpServerLabel =
|
||||
data.hardcodedValues.server_name ||
|
||||
(() => {
|
||||
try {
|
||||
return new URL(data.hardcodedValues.server_url).hostname;
|
||||
} catch {
|
||||
return "MCP";
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
const title =
|
||||
(data.metadata?.customized_name as string) ||
|
||||
(isMCPWithTool
|
||||
? `${mcpServerLabel}: ${beautifyString(data.hardcodedValues.selected_tool)}`
|
||||
: null) ||
|
||||
data.hardcodedValues?.agent_name ||
|
||||
data.title;
|
||||
const title = (data.metadata?.customized_name as string) || data.title;
|
||||
|
||||
const [isEditingTitle, setIsEditingTitle] = useState(false);
|
||||
const [editedTitle, setEditedTitle] = useState(title);
|
||||
|
||||
@@ -83,14 +83,15 @@ export const Block: BlockComponent = ({
|
||||
available_tools: result.availableTools,
|
||||
credentials: result.credentials ?? undefined,
|
||||
});
|
||||
// Persist the MCP title as customized_name in metadata so it survives
|
||||
// save/load even if server_name is pruned from input_default.
|
||||
if (customNode && result.selectedTool) {
|
||||
const title = `${serverLabel}: ${beautifyString(result.selectedTool)}`;
|
||||
if (customNode) {
|
||||
const title = result.selectedTool
|
||||
? `${serverLabel}: ${beautifyString(result.selectedTool)}`
|
||||
: undefined;
|
||||
updateNodeData(customNode.id, {
|
||||
metadata: {
|
||||
...customNode.data.metadata,
|
||||
customized_name: title,
|
||||
credentials_optional: true,
|
||||
...(title && { customized_name: title }),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -104,7 +105,16 @@ export const Block: BlockComponent = ({
|
||||
setMcpDialogOpen(true);
|
||||
return;
|
||||
}
|
||||
addBlockAndCenter(blockData);
|
||||
const customNode = addBlockAndCenter(blockData);
|
||||
// Set customized_name for agent blocks so the agent's name persists
|
||||
if (customNode && blockData.id === SpecialBlockID.AGENT) {
|
||||
updateNodeData(customNode.id, {
|
||||
metadata: {
|
||||
...customNode.data.metadata,
|
||||
customized_name: blockData.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragStart = (e: React.DragEvent<HTMLButtonElement>) => {
|
||||
|
||||
@@ -42,7 +42,11 @@ 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 { findNewlyAddedBlockCoordinates, getTypeColor } from "@/lib/utils";
|
||||
import {
|
||||
beautifyString,
|
||||
findNewlyAddedBlockCoordinates,
|
||||
getTypeColor,
|
||||
} from "@/lib/utils";
|
||||
import { history } from "../history";
|
||||
import { CustomEdge } from "../CustomEdge/CustomEdge";
|
||||
import ConnectionLine from "../ConnectionLine";
|
||||
@@ -748,9 +752,26 @@ const FlowEditor: React.FC<{
|
||||
block_id: blockID,
|
||||
isOutputStatic: nodeSchema.staticOutput,
|
||||
uiType: nodeSchema.uiType,
|
||||
// MCP blocks have optional credentials (public servers don't need auth)
|
||||
// Set customized_name at creation so it persists through save/load
|
||||
...(blockID === SpecialBlockID.MCP_TOOL && {
|
||||
metadata: { credentials_optional: true },
|
||||
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 },
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -881,8 +902,6 @@ 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$/, "")
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user