siyuan-note/siyuan · error

command is required for stdio server

Error message

command is required for stdio server

What it means

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.

Solutions

  1. Set the Command field to the MCP server executable (absolute path recommended)
  2. If the executable is invoked via npx/uvx, set Command to that launcher (e.g. "npx") and put the package in Args
  3. Re-add the stdio server via the settings UI, filling in the command field
  4. Validate the config entry before saving: type=stdio requires a non-empty command

Example fix

// before
{"name": "filesystem", "type": "stdio", "command": "", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]}
// after
{"name": "filesystem", "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]}
Defensive patterns

Strategy: validation

Validate before calling

if server.Type == "stdio" && strings.TrimSpace(server.Command) == "" {
    return fmt.Errorf("stdio server [%s] needs a command", server.Name)
}

Type guard

null

Try / catch

if err := connectStdio(ctx, client, server); err != nil {
    if strings.Contains(err.Error(), "command is required") {
        return fmt.Errorf("configure command for stdio server %q", server.Name)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/ab0427c15d43f989. Report an issue: GitHub.

Appendix: source

Thrown at kernel/mcp/client/mcp.go:450

			logging.LogInfof("mcp: server [%s] tool list changed, reconnecting", server.Name)
			go reconnectMCPServer(server.ID)
		},
	})

	switch server.Type {
	case "stdio":
		session, cmd, err := connectStdio(ctx, c, server)
		return session, cmd, nil, err
	case "http":
		return connectHTTP(ctx, c, server, interactive)
	default:
		return nil, nil, nil, fmt.Errorf("unsupported server type: %s", server.Type)
	}
}

func connectStdio(ctx context.Context, client *mcp.Client, server conf.MCPServer) (*mcp.ClientSession, *exec.Cmd, error) {
	if server.Command == "" {
		return nil, nil, fmt.Errorf("command is required for stdio server")
	}

	cmd := exec.Command(server.Command, server.Args...)
	// stdio 环境变量插值不受密钥 AllowedHosts 约束:目标是本地子进程而非网络主机,管理员在 Env 中
	// 引用 {{secrets.NAME}} 本身就是对该服务器的显式授权,与直接写入明文属于同一信任级别。
	cmdEnv, err := buildStdioEnvironment(server, os.LookupEnv, func(value string) string {
		if model.Conf == nil {
			return value
		}
		return conf.ResolveSecretsVars(model.Conf.Secrets, model.Conf.Variables, value)
	}, runtime.GOOS)
	if err != nil {
		return nil, nil, fmt.Errorf("environment: %w", err)
	}
	cmd.Env = cmdEnv
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, nil, fmt.Errorf("stdin pipe: %w", err)

View on GitHub (pinned to 9f775e8a12)