{"record":{"id":"7b6744c6f7c47516","repo":"PrefectHQ/fastmcp","slug":"protocol-mode-for-server-name-r-must-be-a-string","errorCode":null,"errorMessage":"Protocol mode for server {name!r} must be a string","messagePattern":"Protocol mode for server (.+?) must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/client/group.py","lineNumber":82,"sourceCode":"        cls,\n        config: MCPConfig | dict[str, Any],\n        *,\n        default_mode: ConnectMode = \"auto\",\n    ) -> ClientGroup:\n        \"\"\"Create one independent client for each configured server.\n\n        A server entry may include a FastMCP-specific ``mode`` field. It applies\n        only to that server; entries without one use ``default_mode``.\n        \"\"\"\n        parsed = (\n            config if isinstance(config, MCPConfig) else MCPConfig.from_dict(config)\n        )\n        clients: dict[str, Client[Any]] = {}\n\n        for name, server in parsed.mcpServers.items():\n            configured_mode = (server.model_extra or {}).get(\"mode\", default_mode)\n            if not isinstance(configured_mode, str):\n                raise TypeError(f\"Protocol mode for server {name!r} must be a string\")\n            clients[name] = Client(server.to_transport(), mode=configured_mode)\n\n        return cls(clients)\n\n    @property\n    def protocol_versions(self) -> dict[str, str | None]:\n        return {name: client.protocol_version for name, client in self._clients.items()}\n\n    async def __aenter__(self) -> ClientGroup:\n        if self._exit_stack is not None:\n            raise RuntimeError(\"ClientGroup is already connected\")\n\n        # Claim the stack before the first await so a concurrent entry hits the\n        # guard above instead of racing past it and overwriting this one.\n        stack = contextlib.AsyncExitStack()\n        self._exit_stack = stack\n        await stack.__aenter__()\n","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/client/group.py#L64-L100","documentation":"ClientGroup.from_config reads each server's optional `mode` key (falling back to the group default). Because the value comes from untyped config (model_extra), it is validated: if a server's effective mode is not a string, this TypeError is raised naming the offending server. Mode selects the protocol era (legacy vs modern) for that client.","triggerScenarios":"A config where a server entry has `\"mode\": 2`, `\"mode\": null` with no string default, or any non-string value; a non-string default_mode combined with servers lacking a mode key.","commonSituations":"YAML/JSON typo like `mode: [legacy]` or an accidentally numeric mode; programmatically generated config inserting a wrong-typed mode; misunderstanding that mode must be a string like 'legacy' or 'server'.","solutions":["Fix the config so each server's mode is a string, e.g. \"mode\": \"legacy\"","Remove the mode key to inherit the (string) default_mode","Validate the parsed config before calling from_config"],"exampleFix":"// before\n{\"mcpServers\": {\"local\": {\"command\": \"python\", \"args\": [\"srv.py\"], \"mode\": 1}}}\n\n// after\n{\"mcpServers\": {\"local\": {\"command\": \"python\", \"args\": [\"srv.py\"], \"mode\": \"legacy\"}}}","handlingStrategy":"validation","validationCode":"mode = (server.model_extra or {}).get(\"mode\", default_mode)\nif not isinstance(mode, str):\n    raise TypeError(f\"Server {name!r}: mode must be a string, got {type(mode).__name__}\")","typeGuard":"def is_valid_mode(v: object) -> TypeGuard[str]:\n    return isinstance(v, str)","tryCatchPattern":"try:\n    group = ClientGroup.from_config(path)\nexcept TypeError as e:\n    if \"must be a string\" in str(e):\n        logger.error(\"bad config: %s\", e)\n    raise","preventionTips":["Quote mode values in YAML/JSON so they parse as strings","Add schema validation on the config before from_config","Keep mode values limited to documented strings ('legacy', 'server')"],"tags":["python","type-error","configuration","validation"],"backgroundTag":"config-type-mismatch","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}