{"record":{"id":"405ccc2984ac0b4b","repo":"charmbracelet/crush","slug":"mcp-stdio-config-requires-a-non-empty-command-fi","errorCode":null,"errorMessage":"mcp stdio config requires a non-empty 'command' field","messagePattern":"mcp stdio config requires a non-empty 'command' field","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/mcp/init.go","lineNumber":1043,"sourceCode":"\treturn err\n}\n\nfunc maybeTimeoutErr(err error, timeout time.Duration) error {\n\tif errors.Is(err, context.Canceled) {\n\t\treturn fmt.Errorf(\"timed out after %s\", timeout)\n\t}\n\treturn err\n}\n\nfunc createTransport(ctx context.Context, cfg *config.ConfigStore, name string, m config.MCPConfig, resolver config.VariableResolver) (mcp.Transport, *mcpoauth.Handler, error) {\n\tswitch m.Type {\n\tcase config.MCPStdio:\n\t\tcommand, err := resolver.ResolveValue(m.Command)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"invalid mcp command: %w\", err)\n\t\t}\n\t\tif strings.TrimSpace(command) == \"\" {\n\t\t\treturn nil, nil, fmt.Errorf(\"mcp stdio config requires a non-empty 'command' field\")\n\t\t}\n\t\targs, err := m.ResolvedArgs(resolver)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tenvs, err := m.ResolvedEnv(resolver)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tcmd := exec.CommandContext(ctx, home.Long(command), args...)\n\t\tcmd.Env = append(os.Environ(), envs...)\n\t\t// Run the child in its own process group and kill the whole group when\n\t\t// the session context is cancelled. A stdio server often spawns its own\n\t\t// children (signal-mcp launches signal-cli); os/exec's default\n\t\t// cancellation kills only the direct child, orphaning the rest with\n\t\t// PPID 1 — production accumulated 15+ such zombies over two days.\n\t\tconfigureStdioProcess(cmd)\n\t\treturn &mcp.CommandTransport{","sourceCodeStart":1025,"sourceCodeEnd":1061,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/mcp/init.go#L1025-L1061","documentation":"After resolving an MCP stdio command, createTransport validates that it is not empty/whitespace. An empty command means no process could be spawned, so init fails with this fixed message.","triggerScenarios":"An MCP stdio entry with an empty or whitespace-only 'command' field, or a command whose value resolves (via env substitution) to an empty string.","commonSituations":"Command stored in an env var that is unset or empty; config placeholder left blank; YAML/Bash quoting accidentally producing an empty string.","solutions":["Set a non-empty command path in the MCP stdio config","If the command comes from an env var, ensure it is exported and non-empty before starting","Remove trailing/leading whitespace issues by quoting the value","Use MCPHttp type instead if you actually meant to configure a URL-based server"],"exampleFix":"// before\nmcp myserver command ''\n// after\nmcp myserver command 'npx' args '-y' '@modelcontextprotocol/server-filesystem' '/tmp'","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(cfg.Command) == \"\" {\n    return errors.New(\"mcp stdio config requires a non-empty 'command' field\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always fill the command field for stdio-type MCP servers","If the command comes from an env var, assert it is non-empty at startup","Validate your crushrc/crush.json mcp entries after editing","Use http type for URL-based servers instead of leaving command blank"],"tags":["mcp","config","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}