langflow-ai/langflow · warning · Error

Failed to install MCP

Error message

Failed to install MCP

What it means

400 from the install endpoint validating the transport field: after lowercasing, body.transport must be 'sse' or 'streamablehttp'. This is the only request-validation error in the flow and depends purely on the payload.

Source

Thrown at src/frontend/src/controllers/API/queries/mcp/use-patch-install-mcp.ts:41

export const usePatchInstallMCP: useMutationFunctionType<
  PatchInstallMCPParams,
  PatchInstallMCPBody,
  PatchInstallMCPResponse
> = (params, options?) => {
  const { mutate, queryClient } = UseRequestProcessor();

  async function patchInstallMCP(
    body: PatchInstallMCPBody,
  ): Promise<PatchInstallMCPResponse> {
    try {
      const res = await api.post(
        `${getURL("MCP")}/${params.project_id}/install`,
        body,
      );

      return { message: res.data?.message || "MCP installed successfully" };
    } catch (error: unknown) {
      throw new Error(
        extractApiErrorMessage(
          error as Parameters<typeof extractApiErrorMessage>[0],
          "Failed to install MCP",
        ),
      );
    }
  }

  const mutation: UseMutationResult<
    PatchInstallMCPResponse,
    any,
    PatchInstallMCPBody
  > = mutate(["usePatchInstallMCP", params.project_id], patchInstallMCP, {
    ...options,
    onSuccess: (data, variables, context) => {
      queryClient.invalidateQueries({
        queryKey: ["useGetInstalledMCP", params.project_id],
      });

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Send transport: "sse" or transport: "streamablehttp" (no separator).
  2. Omit the field entirely if SSE is acceptable.
  3. Update any scripts that copied the hyphenated spelling from docs or URLs.

Example fix

// before
{"client": "cursor", "transport": "streamable-http"}
// after
{"client": "cursor", "transport": "streamablehttp"}
Defensive patterns

Strategy: validation

Validate before calling

VALID_TRANSPORTS = {"sse", "streamablehttp"}
transport = (body.get("transport") or "sse").lower()
if transport not in VALID_TRANSPORTS:
    body["transport"] = "sse"  # or reject client-side with a clear message

Type guard

def is_valid_transport(t: str | None) -> bool:
    return (t or "sse").lower() in {"sse", "streamablehttp"}

Prevention

When it happens

Trigger: POST /{project_id}/install with transport='streamable-http', 'HTTP', 'websocket', or any other string; omitting transport is safe because the default is 'sse'.

Common situations: Hand-crafted curl requests using the hyphenated form 'streamable-http'; older clients using 'streamable_http' with an underscore.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/cb9f8c5a56766593. Report an issue: GitHub.