fix(frontend/mcp): Remove broken credentials widget and disable auto-connect

- Remove credentials field from MCP dynamic schema since auth is handled
  by the dialog's OAuth flow (the standard credentials widget doesn't
  support MCP as a provider and fails with 404)
- Simplify FormCreator MCP handling — all form fields are tool arguments
- Disable auto-connect on dialog open; pre-fill last URL instead so user
  can edit before connecting
This commit is contained in:
Zamil Majdy
2026-02-09 14:27:36 +04:00
parent df41d02fce
commit 03487f7b4d
4 changed files with 20 additions and 55 deletions

View File

@@ -9,30 +9,22 @@ import { SpecialBlockID } from "@/lib/autogpt-server-api";
* Build a dynamic input schema for MCP blocks.
*
* When a tool has been selected (tool_input_schema is populated), the block
* should render:
* 1. The credentials field (from the static schema)
* 2. The selected tool's input parameters (from tool_input_schema)
* renders only the selected tool's input parameters. Credentials are NOT
* included because authentication is already handled by the MCP dialog's
* OAuth flow and stored server-side.
*
* Static fields like server_url, selected_tool, available_tools, and
* tool_arguments are hidden because they're pre-configured from the dialog.
*/
function buildMCPInputSchema(
staticSchema: Record<string, any>,
toolInputSchema: Record<string, any>,
): Record<string, any> {
const credentialsProp = staticSchema.properties?.credentials;
const staticRequired = staticSchema.required ?? [];
return {
type: "object",
properties: {
...(credentialsProp ? { credentials: credentialsProp } : {}),
...(toolInputSchema.properties ?? {}),
},
required: [
...staticRequired.filter((r: string) => r === "credentials"),
...(toolInputSchema.required ?? []),
],
required: [...(toolInputSchema.required ?? [])],
};
}
@@ -58,10 +50,7 @@ export const useCustomNode = ({
const currentInputSchema = isAgent
? (data.hardcodedValues.input_schema ?? {})
: isMCPWithTool
? buildMCPInputSchema(
data.inputSchema,
data.hardcodedValues.tool_input_schema,
)
? buildMCPInputSchema(data.hardcodedValues.tool_input_schema)
: data.inputSchema;
const currentOutputSchema = isAgent
? (data.hardcodedValues.output_schema ?? {})

View File

@@ -44,12 +44,10 @@ export const FormCreator: React.FC<FormCreatorProps> = React.memo(
inputs: formData,
};
} else if (isMCPWithTool) {
// Separate credentials from tool arguments
const { credentials, ...toolArgs } = formData;
// All form fields are tool arguments (credentials handled by dialog)
updatedValues = {
...getHardCodedValues(nodeId),
...(credentials ? { credentials } : {}),
tool_arguments: toolArgs,
tool_arguments: formData,
};
} else {
updatedValues = formData;
@@ -64,13 +62,7 @@ export const FormCreator: React.FC<FormCreatorProps> = React.memo(
if (isAgent) {
initialValues = hardcodedValues.inputs ?? {};
} else if (isMCPWithTool) {
// Merge credentials + tool_arguments for the combined schema
initialValues = {
...(hardcodedValues.credentials
? { credentials: hardcodedValues.credentials }
: {}),
...(hardcodedValues.tool_arguments ?? {}),
};
initialValues = hardcodedValues.tool_arguments ?? {};
} else {
initialValues = hardcodedValues;
}

View File

@@ -222,20 +222,17 @@ export const CustomNode = React.memo(
!!data.hardcodedValues?.tool_input_schema?.properties;
if (isMCPWithTool) {
const credentialsProp = data.inputSchema?.properties?.credentials;
// 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;
const staticRequired = data.inputSchema?.required ?? [];
data.inputSchema = {
type: "object",
properties: {
...(credentialsProp ? { credentials: credentialsProp } : {}),
...(toolSchema.properties ?? {}),
},
required: [
...staticRequired.filter((r: string) => r === "credentials"),
...(toolSchema.required ?? []),
],
required: [...(toolSchema.required ?? [])],
} as BlockIORootSchema;
}
@@ -562,8 +559,7 @@ export const CustomNode = React.memo(
default:
const getInputPropKey = (key: string) => {
if (nodeType == BlockUIType.AGENT) return `inputs.${key}`;
if (isMCPWithTool && key !== "credentials")
return `tool_arguments.${key}`;
if (isMCPWithTool) return `tool_arguments.${key}`;
return key;
};

View File

@@ -66,33 +66,21 @@ export function MCPToolDialog({
const oauthHandledRef = useRef(false);
const autoConnectAttemptedRef = useRef(false);
// Attempt auto-connect when dialog opens with a stored server URL
// Pre-fill last used server URL when dialog opens (without auto-connecting)
useEffect(() => {
if (!open) {
autoConnectAttemptedRef.current = false;
return;
}
const lastUrl = localStorage.getItem(STORAGE_KEY);
if (!lastUrl || autoConnectAttemptedRef.current) return;
if (autoConnectAttemptedRef.current) return;
autoConnectAttemptedRef.current = true;
setServerUrl(lastUrl);
setLoading(true);
api
.mcpDiscoverTools(lastUrl)
.then((result) => {
setTools(result.tools);
setServerName(result.server_name);
setStep("tool");
})
.catch(() => {
// Stored credential expired or server changed — stay on URL step
})
.finally(() => {
setLoading(false);
});
}, [open, api]);
const lastUrl = localStorage.getItem(STORAGE_KEY);
if (lastUrl) {
setServerUrl(lastUrl);
}
}, [open]);
// Clean up listeners on unmount
useEffect(() => {