{"record":{"id":"ab0427c15d43f989","repo":"siyuan-note/siyuan","slug":"command-is-required-for-stdio-server","errorCode":null,"errorMessage":"command is required for stdio server","messagePattern":"command is required for stdio server","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/mcp.go","lineNumber":450,"sourceCode":"\t\t\tlogging.LogInfof(\"mcp: server [%s] tool list changed, reconnecting\", server.Name)\n\t\t\tgo reconnectMCPServer(server.ID)\n\t\t},\n\t})\n\n\tswitch server.Type {\n\tcase \"stdio\":\n\t\tsession, cmd, err := connectStdio(ctx, c, server)\n\t\treturn session, cmd, nil, err\n\tcase \"http\":\n\t\treturn connectHTTP(ctx, c, server, interactive)\n\tdefault:\n\t\treturn nil, nil, nil, fmt.Errorf(\"unsupported server type: %s\", server.Type)\n\t}\n}\n\nfunc connectStdio(ctx context.Context, client *mcp.Client, server conf.MCPServer) (*mcp.ClientSession, *exec.Cmd, error) {\n\tif server.Command == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"command is required for stdio server\")\n\t}\n\n\tcmd := exec.Command(server.Command, server.Args...)\n\tcmdEnv, err := buildStdioEnvironment(server, os.LookupEnv, func(value string) string {\n\t\tif model.Conf == nil {\n\t\t\treturn value\n\t\t}\n\t\treturn conf.ResolveSecretsVars(model.Conf.Secrets, model.Conf.Variables, value)\n\t}, runtime.GOOS)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"environment: %w\", err)\n\t}\n\tcmd.Env = cmdEnv\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"stdin pipe: %w\", err)\n\t}\n\tstdout, err := cmd.StdoutPipe()","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/mcp.go#L432-L468","documentation":"Returned by `connectStdio` when a server of type `\"stdio\"` has an empty `Command` field. The stdio transport spawns the server via `exec.Command(server.Command, server.Args...)`, so a missing command leaves nothing to launch; the guard fails fast before attempting the spawn.","triggerScenarios":"A `stdio` MCP server config where `command` is omitted or empty, even though `args` may be present. The check `server.Command == \"\"` is the first thing `connectStdio` does.","commonSituations":"Configuring only `args` and forgetting `command`; a templated config whose `$COMMAND` variable expanded to empty; a server entry meant to be `http` but mistyped as `stdio`.","solutions":["Provide a non-empty `command` for the stdio server, e.g. `\"command\": \"npx\"`.","If the server is HTTP-based, set `type` to `\"http\"` and fill `url` instead of `command`.","Validate the config that any `stdio` entry has both a `command` and (optionally) `args`."],"exampleFix":"// before\n{ \"name\": \"srv\", \"type\": \"stdio\", \"args\": [\"-m\", \"my_server\"] }\n// after\n{ \"name\": \"srv\", \"type\": \"stdio\", \"command\": \"python\", \"args\": [\"-m\", \"my_server\"] }","handlingStrategy":"validation","validationCode":"// Validate stdio server entries before attempting to connect:\nif server.Type == \"stdio\" && strings.TrimSpace(server.Command) == \"\" {\n    return fmt.Errorf(\"command is required for stdio server\")\n}","typeGuard":"func isValidStdioServer(s conf.MCPServer) bool {\n    return s.Type == \"stdio\" && strings.TrimSpace(s.Command) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Always set a non-empty command for stdio MCP servers.","For HTTP servers, set type to http and fill url instead of command.","Validate the full config envelope (type + required transport field) at load time."],"tags":["mcp","config","validation","stdio"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}