{"record":{"id":"b4721ad23c02457a","repo":"agentscope-ai/agentscope","slug":"mcpclient-name-self-name-contains-characters-n","errorCode":null,"errorMessage":"MCPClient name '{self.name}' contains characters not allowed by LLM providers (only [a-zA-Z0-9_-] are permitted). Please rename it.","messagePattern":"MCPClient name '(.+?)' contains characters not allowed by LLM providers \\(only \\[a-zA-Z0-9_-\\] are permitted\\)\\. Please rename it\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agentscope/mcp/_mcp_client.py","lineNumber":123,"sourceCode":"    _stack: AsyncExitStack | None = PrivateAttr(default=None)\n    _is_connected: bool = PrivateAttr(default=False)\n    _cached_tools: list[mcp.types.Tool] | None = PrivateAttr(default=None)\n\n    @property\n    def is_connected(self) -> bool:\n        \"\"\"Whether the client is currently connected.\n\n        Returns:\n            True if connected, False otherwise.\n        \"\"\"\n        return self._is_connected\n\n    def model_post_init(self, __context: Any) -> None:\n        \"\"\"Validate configuration and initialize client.\"\"\"\n        # MCP name is used to compose model-facing tool names\n        # (mcp__{name}__{tool}), which must match ^[a-zA-Z0-9_-]+$.\n        if not re.fullmatch(r\"[a-zA-Z0-9_-]+\", self.name):\n            raise ValueError(\n                f\"MCPClient name '{self.name}' contains characters not \"\n                f\"allowed by LLM providers (only [a-zA-Z0-9_-] are \"\n                f\"permitted). Please rename it.\",\n            )\n\n        # STDIO MCP must be stateful\n        if self.mcp_config.type == \"stdio_mcp\" and not self.is_stateful:\n            raise ValueError(\n                \"STDIO MCP must be stateful (is_stateful=True).\",\n            )\n\n        # Check arguments for self.enable_tools and disable_tools\n        if self.enable_tools is not None:\n            if not isinstance(self.enable_tools, list) or any(\n                not isinstance(_, str) for _ in self.enable_tools\n            ):\n                raise ValueError(\n                    \"Enable tools should be a list of strings, but got \"","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/mcp/_mcp_client.py#L105-L141","documentation":"MCPClient validates in model_post_init that its name matches ^[a-zA-Z0-9_-]+$ because the name is used to compose model-facing tool names (mcp__{name}__{tool}) that LLM providers restrict to that character set. Any space, dot, slash, unicode, or other symbol in the MCP server name triggers this ValueError at construction time.","triggerScenarios":"MCPClient(name=\"my mcp.server\", mcp_config=...) or any name containing spaces, dots, colons, or non-ASCII characters; also names loaded from config files (YAML/JSON) where naming rules were not enforced.","commonSituations":"Naming an MCP server after a URL or path (e.g. \"github.com/api\"), using display names with spaces, copying server names from other tools (Claude Desktop config uses arbitrary names) into agentscope.","solutions":["Rename the MCP client using only letters, digits, underscore, and hyphen (e.g. \"github-api\" instead of \"github.com/api\")","If the name comes from a config file, sanitize it before constructing MCPClient","Keep the display/description separate from the internal name if human-friendly naming is needed"],"exampleFix":"// before\nclient = MCPClient(name=\"my search server\", mcp_config=config)\n\n// after\nclient = MCPClient(name=\"my-search-server\", mcp_config=config)","handlingStrategy":"validation","validationCode":"import re\nassert re.fullmatch(r\"[a-zA-Z0-9_-]+\", name), f\"invalid MCP name: {name!r}\"\nclient = MCPClient(name=name, mcp_config=cfg)","typeGuard":null,"tryCatchPattern":"try:\n    MCPClient(name=name, mcp_config=cfg)\nexcept ValueError as e:\n    if \"not allowed by LLM providers\" in str(e):\n        name = re.sub(r\"[^a-zA-Z0-9_-]\", \"-\", name)","preventionTips":["Derive MCP names with a sanitizer: re.sub(r'[^a-zA-Z0-9_-]', '-', raw_name)","Keep a naming convention (kebab-case) for MCP servers in config files","Add a config lint step before constructing clients"],"tags":["mcp","validation","naming","pydantic"],"backgroundTag":"invalid-identifier-characters","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}