bytedance/deer-flow · error · HTTPException

MCP server '{name}' uses disallowed stdio command '{command_

Error message

MCP server '{name}' uses disallowed stdio command '{command_name}'. Allowed commands: {allowed}. Configure {_MCP_STDIO_COMMAND_ALLOWLIST_ENV} to extend this list.

What it means

400 raised during MCP config validation when a stdio server's command name is not in the allowlist of executable names. The allowlist defaults to known-safe launchers (npx, uvx, uv, etc.) and can be extended via the environment variable DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST (the constant referenced by _MCP_STDIO_COMMAND_ALLOWLIST_ENV). This confines API-created MCP servers to vetted binaries.

Source

Thrown at backend/app/gateway/routers/mcp.py:608

    Local config files can still express arbitrary advanced setups, but the
    HTTP API is an untrusted boundary. Restricting stdio commands here reduces
    the blast radius of a compromised authenticated browser session.

    The command name alone is not a meaningful restriction, so the launcher's
    ``args`` and ``env`` are screened for the flags and variables that turn an
    allowlisted binary into an arbitrary code evaluator.
    """
    allowed_commands = _allowed_stdio_commands()
    for name, server in request.mcp_servers.items():
        transport_type = (server.type or "stdio").lower()
        if transport_type != "stdio":
            continue

        command_name = _stdio_command_name(server.command, server_name=name)
        if command_name not in allowed_commands:
            allowed = ", ".join(sorted(allowed_commands)) or "<none>"
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=(f"MCP server '{name}' uses disallowed stdio command '{command_name}'. Allowed commands: {allowed}. Configure {_MCP_STDIO_COMMAND_ALLOWLIST_ENV} to extend this list."),
            )

        exec_flag = _arbitrary_exec_arg(server.args, command=command_name)
        if exec_flag is not None:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=(f"MCP server '{name}' passes '{exec_flag}' to '{command_name}', which would run arbitrary code. Point the server at a package or module instead."),
            )

        for env_name in server.env:
            if env_name.strip().upper() in _CODE_INJECTING_ENV_VARS:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=(f"MCP server '{name}' sets environment variable '{env_name}', which would run arbitrary code at process startup."),
                )

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Switch the command to an allowlisted launcher (e.g. uvx or npx) that fetches/runs your server
  2. Set DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST to a comma-separated list including your binary, then restart the Gateway
  3. Check the error body: it lists the currently allowed commands and the exact env var name

Example fix

# before
{"command": "python", "args": ["server.py"]}
# after (option 1)
{"command": "uvx", "args": ["--from", "./my-server", "my-server"]}
# after (option 2)
# env: DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST="python"
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = new Set(['npx', 'uvx', 'uv']); // keep in sync with backend defaults + env extension
function assertAllowedCommand(cmd: string) { if (!ALLOWED.has(cmd)) throw new Error(`command '${cmd}' not allowlisted; extend DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST on the server`); }

Type guard

null

Try / catch

try { await putMcpConfig(payload); } catch (e) { if (e.status === 400 && /disallowed stdio command/.test(e.detail)) { const allowed = parseAllowedFromDetail(e.detail); /* switch command or ask operator to extend env */ } throw e; }

Prevention

When it happens

Trigger: Submitting command: 'node', 'python', 'docker', or any custom binary not in the default allowlist; deploying a config that worked in an environment with a custom allowlist env var into one without it.

Common situations: Trying to run a MCP server via python server.py instead of an allowlisted launcher; environments where operators extend the allowlist differently across dev/prod; version upgrades that changed the default allowlist contents.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/5b54eee13f253b54. Report an issue: GitHub.