agentscope-ai/agentscope · error · ValueError

Enable tools should be a list of strings, but got {self.enab

Error message

Enable tools should be a list of strings, but got {self.enable_tools}.

What it means

MCPClient validates that enable_tools, when provided, is a list of strings (tool names). Passing a single string, a set, a tuple, None-like values, or a list containing non-strings raises this ValueError at construction time.

Source

Thrown at src/agentscope/mcp/_mcp_client.py:140

        if not re.fullmatch(r"[a-zA-Z0-9_-]+", self.name):
            raise ValueError(
                f"MCPClient name '{self.name}' contains characters not "
                f"allowed by LLM providers (only [a-zA-Z0-9_-] are "
                f"permitted). Please rename it.",
            )

        # STDIO MCP must be stateful
        if self.mcp_config.type == "stdio_mcp" and not self.is_stateful:
            raise ValueError(
                "STDIO MCP must be stateful (is_stateful=True).",
            )

        # Check arguments for self.enable_tools and disable_tools
        if self.enable_tools is not None:
            if not isinstance(self.enable_tools, list) or any(
                not isinstance(_, str) for _ in self.enable_tools
            ):
                raise ValueError(
                    "Enable tools should be a list of strings, but got "
                    f"{self.enable_tools}.",
                )

        if self.disable_tools is not None:
            if not isinstance(self.disable_tools, list) or any(
                not isinstance(_, str) for _ in self.disable_tools
            ):
                raise ValueError(
                    "Disable tools should be a list of strings, but got "
                    f"{self.disable_tools}.",
                )

        if self.enable_tools is not None and self.disable_tools is not None:
            intersection = set(self.enable_tools).intersection(
                set(self.disable_tools),
            )
            if len(intersection) != 0:

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Wrap the value in a list: enable_tools=["search"]
  2. Ensure every element is a str; convert non-strings explicitly before passing
  3. Validate config-derived values before constructing MCPClient

Example fix

// before
client = MCPClient(name="db", mcp_config=cfg, enable_tools="query")

// after
client = MCPClient(name="db", mcp_config=cfg, enable_tools=["query"])
Defensive patterns

Strategy: validation

Validate before calling

enable_tools = None if enable_tools is None else [str(t) for t in enable_tools]
assert isinstance(enable_tools, list)
client = MCPClient(name=n, mcp_config=cfg, enable_tools=enable_tools)

Type guard

def is_tool_name_list(v) -> bool:
    return v is None or (isinstance(v, list) and all(isinstance(x, str) for x in v))

Try / catch

try:
    MCPClient(name=n, mcp_config=cfg, enable_tools=enable_tools)
except ValueError:
    enable_tools = list(map(str, enable_tools or [])) or None
    client = MCPClient(name=n, mcp_config=cfg, enable_tools=enable_tools)

Prevention

When it happens

Trigger: MCPClient(..., enable_tools="search") (bare string instead of list), enable_tools={"search"} (set), or enable_tools=["search", 3] (mixed types).

Common situations: Passing a single tool name as a convenience string; deserializing tool filters from JSON/YAML where a scalar or non-list sneaks in; refactoring from a tuple to a list API.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/0cff0836c1c303d2. Report an issue: GitHub.