{"record":{"id":"5b54eee13f253b54","repo":"bytedance/deer-flow","slug":"mcp-server-name-uses-disallowed-stdio-command","errorCode":null,"errorMessage":"MCP server '{name}' uses disallowed stdio command '{command_name}'. Allowed commands: {allowed}. Configure {_MCP_STDIO_COMMAND_ALLOWLIST_ENV} to extend this list.","messagePattern":"MCP server '(.+?)' uses disallowed stdio command '(.+?)'\\. Allowed commands: (.+?)\\. Configure (.+?) to extend this list\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/app/gateway/routers/mcp.py","lineNumber":608,"sourceCode":"\n    Local config files can still express arbitrary advanced setups, but the\n    HTTP API is an untrusted boundary. Restricting stdio commands here reduces\n    the blast radius of a compromised authenticated browser session.\n\n    The command name alone is not a meaningful restriction, so the launcher's\n    ``args`` and ``env`` are screened for the flags and variables that turn an\n    allowlisted binary into an arbitrary code evaluator.\n    \"\"\"\n    allowed_commands = _allowed_stdio_commands()\n    for name, server in request.mcp_servers.items():\n        transport_type = (server.type or \"stdio\").lower()\n        if transport_type != \"stdio\":\n            continue\n\n        command_name = _stdio_command_name(server.command, server_name=name)\n        if command_name not in allowed_commands:\n            allowed = \", \".join(sorted(allowed_commands)) or \"<none>\"\n            raise HTTPException(\n                status_code=status.HTTP_400_BAD_REQUEST,\n                detail=(f\"MCP server '{name}' uses disallowed stdio command '{command_name}'. Allowed commands: {allowed}. Configure {_MCP_STDIO_COMMAND_ALLOWLIST_ENV} to extend this list.\"),\n            )\n\n        exec_flag = _arbitrary_exec_arg(server.args, command=command_name)\n        if exec_flag is not None:\n            raise HTTPException(\n                status_code=status.HTTP_400_BAD_REQUEST,\n                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.\"),\n            )\n\n        for env_name in server.env:\n            if env_name.strip().upper() in _CODE_INJECTING_ENV_VARS:\n                raise HTTPException(\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                    detail=(f\"MCP server '{name}' sets environment variable '{env_name}', which would run arbitrary code at process startup.\"),\n                )\n","sourceCodeStart":590,"sourceCodeEnd":626,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/mcp.py#L590-L626","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Switch the command to an allowlisted launcher (e.g. uvx or npx) that fetches/runs your server","Set DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST to a comma-separated list including your binary, then restart the Gateway","Check the error body: it lists the currently allowed commands and the exact env var name"],"exampleFix":"# before\n{\"command\": \"python\", \"args\": [\"server.py\"]}\n# after (option 1)\n{\"command\": \"uvx\", \"args\": [\"--from\", \"./my-server\", \"my-server\"]}\n# after (option 2)\n# env: DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST=\"python\"","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['npx', 'uvx', 'uv']); // keep in sync with backend defaults + env extension\nfunction assertAllowedCommand(cmd: string) { if (!ALLOWED.has(cmd)) throw new Error(`command '${cmd}' not allowlisted; extend DEERFLOW_MCP_STDIO_COMMAND_ALLOWLIST on the server`); }","typeGuard":"null","tryCatchPattern":"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; }","preventionTips":["Prefer allowlisted launchers (npx/uvx) that fetch packages by name","Record the allowlist env var in each environment's provisioning so dev/prod match","On 400, read the detail — it enumerates the currently allowed commands"],"tags":["mcp","stdio","security","allowlist","http-400"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}