{"record":{"id":"1a960babce450423","repo":"bytedance/deer-flow","slug":"mcp-server-server-name-command-must-be-a-singl","errorCode":null,"errorMessage":"MCP server '{server_name}' command must be a single executable name; put parameters in args instead.","messagePattern":"MCP server '(.+?)' command must be a single executable name; put parameters in args instead\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/app/gateway/routers/mcp.py","lineNumber":505,"sourceCode":"    base = set(_DEFAULT_MCP_STDIO_COMMAND_ALLOWLIST)\n    if raw is None:\n        return base\n    extra = {item.strip() for item in raw.split(\",\") if item.strip()}\n    return base | extra\n\n\ndef _stdio_command_name(command: str | None, *, server_name: str) -> str:\n    \"\"\"Normalize and validate a stdio command field from the API boundary.\"\"\"\n    if command is None or not command.strip():\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST,\n            detail=f\"MCP server '{server_name}' with stdio transport requires a command.\",\n        )\n\n    stripped = command.strip()\n    has_path_separator = \"/\" in stripped or \"\\\\\" in stripped\n    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):\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST,\n            detail=(f\"MCP server '{server_name}' command must be a single executable name; put parameters in args instead.\"),\n        )\n\n    return stripped\n\n\ndef _launcher_option_region(args: list[str], *, grammar: _LauncherGrammar) -> list[str]:\n    \"\"\"Return the leading args a package launcher parses as its own options.\n\n    The region ends at a bare ``--`` or at the package name -- the first token\n    that is neither a flag nor the value of one. A ``--flag=value`` token\n    carries its own value and never consumes the next one.\n\n    Arity is looked up case-sensitively, because a launcher's short options are:\n    npm reads ``-c`` as ``--call`` but ``-C`` as ``--prefix``, which takes a\n    value.\n    \"\"\"","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/mcp.py#L487-L523","documentation":"400 raised when the stdio command field is not a single bare executable name: it contains leading/trailing whitespace, path separators ('/' or '\\\\'), internal whitespace, or shell metacharacters. The API executes the command directly without a shell, so only one token is accepted; parameters belong in args.","triggerScenarios":"Submitting command: 'npx -y foo' (flags inside command), command: '/usr/local/bin/npx' (absolute path), command: 'npx; rm -rf /' or 'npx && foo' (metacharacters), or command with stray whitespace.","commonSituations":"Copy-pasting a full CLI invocation from documentation into the command field; hardening-driven rejection of path-qualified binaries; attempts to chain commands or use shell syntax in a field that is exec'd directly.","solutions":["Put only the executable name in command and move all flags into the args array","Use a launcher (npx/uvx/uv run/etc.) from the allowlist as the command and the real package as the first arg","If you need a specific binary path, symlink or install it on PATH and reference it by bare name, or extend the allowlist env var"],"exampleFix":"// before\n{\"command\": \"uvx --from git+https://... mcp-server\"}\n// after\n{\"command\": \"uvx\", \"args\": [\"--from\", \"git+https://...\", \"mcp-server\"]}","handlingStrategy":"validation","validationCode":"const SHELL_METACHARS = new Set(';|&$`><\\\\\"\\'(){}[]!*?~');\nfunction assertSingleExecutable(cmd: string) { const s = cmd.trim(); if (/[/\\\\]/.test(s) || /\\s/.test(s) || [...s].some(c => SHELL_METACHARS.has(c)) || s !== cmd) throw new Error('command must be one bare executable name; put parameters in args'); }","typeGuard":"function isBareExecutableName(cmd: string): boolean { return /^[A-Za-z0-9._-]+$/.test(cmd); }","tryCatchPattern":"null","preventionTips":["Treat command as exactly one token — tokenize the full invocation yourself and split command vs args","Never paste a shell command line into the command field","Avoid path-qualified binaries; put custom tools on PATH via deployment, not via the command string"],"tags":["mcp","stdio","security","validation","http-400"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}