bytedance/deer-flow · error · HTTPException

MCP server '{server_name}' with stdio transport requires a c

Error message

MCP server '{server_name}' with stdio transport requires a command.

What it means

400 raised by _stdio_command_name when an API-managed stdio MCP server definition has a command field that is None, empty, or whitespace-only. stdio servers spawn a local process, so a command is mandatory — the API boundary rejects the definition before anything is persisted or executed.

Source

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

        return [_merge_extra_value_preserving_masked(key, nested_value, existing_value[index], existing_present=True) for index, nested_value in enumerate(incoming_value)]

    return incoming_value


def _allowed_stdio_commands() -> set[str]:
    """Return executable names allowed for API-managed stdio MCP servers."""
    raw = os.environ.get(_MCP_STDIO_COMMAND_ALLOWLIST_ENV)
    base = set(_DEFAULT_MCP_STDIO_COMMAND_ALLOWLIST)
    if raw is None:
        return base
    extra = {item.strip() for item in raw.split(",") if item.strip()}
    return base | extra


def _stdio_command_name(command: str | None, *, server_name: str) -> str:
    """Normalize and validate a stdio command field from the API boundary."""
    if command is None or not command.strip():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"MCP server '{server_name}' with stdio transport requires a command.",
        )

    stripped = command.strip()
    has_path_separator = "/" in stripped or "\\" in stripped
    if stripped != command or has_path_separator or any(ch.isspace() for ch in stripped) or any(ch in stripped for ch in _SHELL_METACHARS):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=(f"MCP server '{server_name}' command must be a single executable name; put parameters in args instead."),
        )

    return stripped


def _launcher_option_region(args: list[str], *, grammar: _LauncherGrammar) -> list[str]:
    """Return the leading args a package launcher parses as its own options.

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Add the executable name to the command field (e.g. command: 'npx') and move any flags into args
  2. If the server is remote, set type to 'sse' or 'http' and provide url instead of command
  3. Validate the payload locally against McpServerConfigResponse before submitting

Example fix

// before
{"my-server": {"type": "stdio", "args": ["-y", "some-mcp"]}}
// after
{"my-server": {"type": "stdio", "command": "npx", "args": ["-y", "some-mcp"]}}
Defensive patterns

Strategy: validation

Validate before calling

function assertStdioServer(name: string, cfg: {type?: string; command?: string; url?: string}) { const t = (cfg.type ?? 'stdio').toLowerCase(); if (t === 'stdio' && !(cfg.command && cfg.command.trim())) throw new Error(`server ${name}: stdio transport requires command`); if (t !== 'stdio' && !cfg.url) throw new Error(`server ${name}: ${t} transport requires url`); }

Type guard

function isStdioServerConfig(cfg: {type?: string; command?: string; url?: string}): cfg is {type: 'stdio'; command: string; args?: string[]} { return (cfg.type ?? 'stdio').toLowerCase() === 'stdio' && typeof cfg.command === 'string' && cfg.command.trim().length > 0; }

Try / catch

null

Prevention

When it happens

Trigger: POST/PUT to /api/mcp/config with a server entry of type stdio (the default when type is omitted) whose command is missing; a payload where the command ended up in args and command was left null; enabling a stored server whose raw JSON lacks a command field.

Common situations: Hand-written extensions_config.json or API payloads modeled after SSE/HTTP servers (which need url, not command); copy-paste config snippets that omit the command key; frontend forms defaulting transport to stdio without requiring the command input.

Related errors


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