{"record":{"id":"aacae5953dc23611","repo":"rohitg00/ai-engineering-from-scratch","slug":"progresstoken-must-be-a-string-or-integer","errorCode":null,"errorMessage":"progressToken must be a string or integer","messagePattern":"progressToken must be a string or integer","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py","lineNumber":323,"sourceCode":"\n    def _call_tool(\n        self, params: dict[str, Any], metadata: dict[str, Any]\n    ) -> tuple[dict[str, Any], list[dict[str, Any]]]:\n        name = params[\"name\"]\n        if not isinstance(name, str) or not name:\n            raise ValueError(\"name must be a non-empty string\")\n        tool = self.tools.get(name)\n        if tool is None:\n            raise ValueError(\"unknown tool\")\n        arguments = tool.validate_arguments(params.get(\"arguments\", {}))\n        if name == \"prepare_review\":\n            return self._prepare_review(params, metadata, arguments), []\n\n        token = metadata.get(\"progressToken\")\n        notifications: list[dict[str, Any]] = []\n        if token is not None:\n            if not isinstance(token, (str, int)) or isinstance(token, bool):\n                raise ValueError(\"progressToken must be a string or integer\")\n            notifications = [\n                self._progress(token, 0, 1, \"starting\"),\n                self._progress(token, 1, 1, \"complete\"),\n            ]\n        value = tool.handler(arguments)\n        return self._complete(\n            content=[{\"type\": \"text\", \"text\": json.dumps(value)}], isError=False\n        ), notifications\n\n    def _prepare_review(\n        self,\n        params: dict[str, Any],\n        metadata: dict[str, Any],\n        arguments: dict[str, Any],\n    ) -> dict[str, Any]:\n        input_requests = {\n            \"workspace_scope\": {\"method\": \"roots/list\", \"params\": {}},\n            \"review_sample\": {","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py#L305-L341","documentation":"Before running the tool handler the server validates metadata['progressToken']: when present it must be str or int. bool is explicitly rejected because in Python bool is a subclass of int, so True would otherwise slip through the isinstance check — the guard exists to make that trap visible.","triggerScenarios":"tools/call with a progressToken set to true/false, a float, a list, or a dict in the request metadata (_meta).","commonSituations":"A JSON config or UI checkbox serialized as boolean into progressToken; client library defaulting the token to an options object; copying a whole request payload as the token.","solutions":["Send progressToken as a string or integer, e.g. \"tok-1\" or 42","Omit progressToken entirely when progress notifications are not wanted","Never send booleans — they pass isinstance(token, int) checks in looser code but are rejected here by design"],"exampleFix":"# before\nmeta = {\"progressToken\": True}\n\n# after\nmeta = {\"progressToken\": \"tok-1\"}","handlingStrategy":"type-guard","validationCode":"token = meta.get(\"progressToken\")\nif token is not None and (not isinstance(token, (str, int)) or isinstance(token, bool)):\n    raise ValueError(\"progressToken must be a string or integer\")\nserver.exchange(\"tools/call\", params, metadata=meta)","typeGuard":"def is_valid_progress_token(token: object) -> bool:\n    return token is None or (isinstance(token, (str, int)) and not isinstance(token, bool))","tryCatchPattern":null,"preventionTips":["Generate tokens as strings ('tok-<n>') or ints","Remember bool is an int subclass in Python — exclude it explicitly","Omit the token when you do not want progress notifications"],"tags":["mcp","validation","progress","type-check"],"backgroundTag":"invalid-progress-token","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}