langflow-ai/langflow · warning · Error

Failed to install MCP

Error message

Failed to install MCP

What it means

500 (arguably should be 403) returned by POST /{project_id}/install when the request's client IP is not a loopback address. The install flow writes MCP client config files on the user's machine, so Langflow restricts it to same-machine requests via get_client_ip + is_local_ip.

Source

Thrown at src/frontend/src/controllers/API/queries/mcp/use-add-mcp-server.ts:49

      }
      if (body.args && body.args.length > 0) {
        payload.args = body.args;
      }
      if (body.env && Object.keys(body.env).length > 0) {
        payload.env = body.env;
      }
      if (body.headers && Object.keys(body.headers).length > 0) {
        payload.headers = body.headers;
      }

      const res = await api.post(
        `${getURL("MCP_SERVERS", undefined, true)}/${body.name}`,
        payload,
      );

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

  const mutation: UseMutationResult<AddMCPServerResponse, any, MCPServerType> =
    mutate(["useAddMCPServer"], addMCPServer, {
      ...options,
      retry: 0,
      onSuccess: (data, variables, context) => {
        queryClient.invalidateQueries({
          queryKey: ["useGetMCPServers"],
        });
        options?.onSuccess?.(data, variables, context);
      },

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Make the install request from the same machine Langflow runs on (http://localhost:7860 or an SSH tunnel).
  2. If behind a proxy, configure it to pass X-Forwarded-For and ensure Langflow trusts/honors it, so loopback clients are detected.
  3. Alternatively copy the generated MCP JSON manually from the UI instead of using /install.
Defensive patterns

Strategy: validation

Validate before calling

import socket
local = client_ip in {"127.0.0.1", "::1"} or ipaddress.ip_address(client_ip).is_loopback
if not local:
    # use an SSH tunnel or localhost UI instead of calling /install remotely

Type guard

import ipaddress
def is_loopback(ip_str: str) -> bool:
    try:
        return ipaddress.ip_address(ip_str).is_loopback
    except ValueError:
        return ip_str == "localhost"

Prevention

When it happens

Trigger: Clicking 'install to Cursor/Claude/Windsurf' in a UI served over LAN/public, or through a reverse proxy that does not forward the real client IP (X-Forwarded-For missing), so the server sees the proxy IP instead of 127.0.0.1.

Common situations: Accessing Langflow via http://<lan-ip>:7860 or a domain behind nginx/tunnel; docker port-mapping where the source IP is NAT'd; SSH port-forward usually still works because it appears as loopback.

Related errors


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