{"record":{"id":"d8fc21c4ab3652ed","repo":"langchain-ai/deepagents","slug":"invalid-mcp-server-name-server-name-r-token-sto","errorCode":null,"errorMessage":"Invalid MCP server name {server_name!r}: token storage names must match [A-Za-z0-9_-]+ to keep the on-disk path inside {tokens_dir}.","messagePattern":"Invalid MCP server name (.+?): token storage names must match \\[A-Za-z0-9_-\\]\\+ to keep the on-disk path inside (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/mcp_auth.py","lineNumber":324,"sourceCode":"class FileTokenStorage(TokenStorage):\n    \"\"\"File-backed `TokenStorage` under the selected profile's state directory.\"\"\"\n\n    def __init__(self, server_name: str, *, server_url: str | None = None) -> None:\n        \"\"\"Bind this storage to a configured MCP server identity.\n\n        Raises:\n            ValueError: If `server_name` contains characters that would let\n                it escape the MCP token-store directory when used as the\n                token-file basename.\n        \"\"\"\n        if not _SAFE_SERVER_NAME_RE.fullmatch(server_name):\n            tokens_dir = PATHS.display(token_store_dir())\n            msg = (\n                f\"Invalid MCP server name {server_name!r}: token storage \"\n                \"names must match [A-Za-z0-9_-]+ to keep the on-disk path \"\n                f\"inside {tokens_dir}.\"\n            )\n            raise ValueError(msg)\n        self._server_name = server_name\n        self._server_url = server_url\n\n    @property\n    def path(self) -> Path:\n        \"\"\"On-disk token file path for this server.\"\"\"\n        stem = _token_file_stem(self._server_name, self._server_url)\n        return token_store_dir() / f\"{stem}.json\"\n\n    @property\n    def refresh_lock_path(self) -> Path:\n        \"\"\"Sibling lock file that serializes token refreshes across processes.\n\n        A dedicated `.lock` file (never the token file itself) lets `filelock`\n        coordinate refreshes between dcode processes and provider instances\n        without ever holding an exclusive lock on the credential file. It holds\n        no token material.\n        \"\"\"","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/mcp_auth.py#L306-L342","documentation":"The MCP token-store wrapper validates the server name at construction because the server name becomes a file name under the tokens directory. Names containing path separators or other unsafe characters could escape the token directory, so only [A-Za-z0-9_-]+ is allowed. This is a fail-fast guard in TokenStore.__init__ (mcp_auth.py:324).","triggerScenarios":"Constructing the token store (directly or via `mcp login`/auth flows) with a server name that is empty or contains characters outside [A-Za-z0-9_-], e.g. slashes, spaces, dots, or unicode.","commonSituations":"MCP server entries in config with URLs or paths used as names (e.g. 'https://mcp.example.com/v1'), names copied from docs containing spaces or slashes, or a typo'd/whitespace-padded server key in .mcp.json.","solutions":["Rename the MCP server entry to a simple identifier using only letters, digits, underscores, or hyphens (e.g. 'github-mcp').","Strip whitespace and transliterate/replace special characters in the name before constructing the store.","If generating names programmatically, pass them through re.sub(r'[^A-Za-z0-9_-]', '-', name) first."],"exampleFix":"// before\nstore = McpTokenStore(server_name=\"https://mcp.example.com/sse\", url=...)\n// after\nimport re\nname = re.sub(r\"[^A-Za-z0-9_-]\", \"-\", \"https://mcp.example.com/sse\")  # e.g. 'https---mcp-example-com-sse'\nstore = McpTokenStore(server_name=name, url=...)","handlingStrategy":"validation","validationCode":"import re\ndef valid_server_name(name: str) -> bool:\n    return bool(re.fullmatch(r\"[A-Za-z0-9_-]+\", name))\n\nif not valid_server_name(server_name):\n    server_name = re.sub(r\"[^A-Za-z0-9_-]\", \"-\", server_name)","typeGuard":"import re\ndef is_safe_token_name(name: object) -> TypeGuard[str]:\n    return isinstance(name, str) and re.fullmatch(r\"[A-Za-z0-9_-]+\", name) is not None","tryCatchPattern":null,"preventionTips":["Use short kebab-case identifiers for MCP server names in .mcp.json.","Never reuse a URL or file path as the server name.","Sanitize programmatically derived names with re.sub(r'[^A-Za-z0-9_-]', '-', name) before constructing the store."],"tags":["mcp","validation","configuration","path-traversal"],"backgroundTag":"invalid-identifier-name","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}