{"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\t// stdio 环境变量插值不受密钥 AllowedHosts 约束：目标是本地子进程而非网络主机，管理员在 Env 中\n\t// 引用 {{secrets.NAME}} 本身就是对该服务器的显式授权，与直接写入明文属于同一信任级别。\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)","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/mcp.go#L432-L468","documentation":"connectStdio launches the MCP server as a local subprocess via exec.Command, so the configured Command field must be a non-empty executable path or name. This guard fires before any process is spawned when server.Command is the empty string. Without a command there is no stdio peer to speak MCP over.","triggerScenarios":"connectServer dispatches a server with Type \"stdio\" whose conf.MCPServer.Command is \"\" — e.g. the entry was created with a type but no command, or the command field was cleared.","commonSituations":"Adding a stdio server in config and forgetting to fill in the executable, a config migration or UI edit clearing the command field, or copying a template entry with an empty command placeholder.","solutions":["Set the Command field to the MCP server executable (absolute path recommended)","If the executable is invoked via npx/uvx, set Command to that launcher (e.g. \"npx\") and put the package in Args","Re-add the stdio server via the settings UI, filling in the command field","Validate the config entry before saving: type=stdio requires a non-empty command"],"exampleFix":"// before\n{\"name\": \"filesystem\", \"type\": \"stdio\", \"command\": \"\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]}\n// after\n{\"name\": \"filesystem\", \"type\": \"stdio\", \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]}","handlingStrategy":"validation","validationCode":"if server.Type == \"stdio\" && strings.TrimSpace(server.Command) == \"\" {\n    return fmt.Errorf(\"stdio server [%s] needs a command\", server.Name)\n}","typeGuard":"null","tryCatchPattern":"if err := connectStdio(ctx, client, server); err != nil {\n    if strings.Contains(err.Error(), \"command is required\") {\n        return fmt.Errorf(\"configure command for stdio server %q\", server.Name)\n    }\n    return err\n}","preventionTips":["Make the command field required in the add-server UI when type is stdio","Always fill both command and args together; test the command line in a shell first","Validate config entries at load time and disable invalid servers with a visible warning"],"tags":["mcp","config","stdio","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}