{"record":{"id":"47f12661b644ffd8","repo":"unslothai/unsloth","slug":"unsloth-mcp-bearer-token-must-be-a-non-empty-value","errorCode":null,"errorMessage":"Unsloth MCP bearer token must be a non-empty value","messagePattern":"Unsloth MCP bearer token must be a non-empty value","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"studio/backend/mcp_server.py","lineNumber":25,"sourceCode":"duplicating training or export logic. It is opt-in because several tools can\nstart GPU work or write model artifacts.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport hmac\nimport asyncio\nfrom typing import Any\n\nfrom fastmcp import FastMCP\n\n\nclass BearerTokenMiddleware:\n    \"\"\"Require an exact bearer token when Unsloth MCP is exposed remotely.\"\"\"\n\n    def __init__(self, app: Any, token: str) -> None:\n        if not token or not token.strip():\n            raise ValueError(\"Unsloth MCP bearer token must be a non-empty value\")\n        if not token.isascii():\n            # A non-ASCII token cannot be sent in an HTTP header; reject it here.\n            raise ValueError(\"Unsloth MCP bearer token must contain ASCII characters only\")\n        self.app = app\n        # Compare on raw header bytes: str hmac.compare_digest raises on non-ASCII\n        # input, which would surface as a 500 instead of a clean 401.\n        self.expected = token.encode(\"utf-8\")\n\n    async def __call__(self, scope: dict[str, Any], receive: Any, send: Any) -> None:\n        scope_type = scope.get(\"type\")\n        if scope_type not in (\"http\", \"websocket\"):\n            await self.app(scope, receive, send)\n            return\n\n        headers = dict(scope.get(\"headers\", []))\n        raw_auth = headers.get(b\"authorization\", b\"\")\n        scheme, _, supplied = raw_auth.partition(b\" \")\n        if scheme.lower() != b\"bearer\" or not hmac.compare_digest(supplied, self.expected):","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/mcp_server.py#L7-L43","documentation":"ValueError from BearerTokenMiddleware.__init__ when the token passed for the Unsloth MCP app is empty or whitespace-only. The middleware performs exact bearer-token authentication on every HTTP/websocket request, so an empty token would either match nothing or (worse) trivially guessable values; construction fails fast instead.","triggerScenarios":"Constructing BearerTokenMiddleware(app, token='') or BearerTokenMiddleware(app, token='   '), typically indirectly because main.py read an empty UNSLOTH_STUDIO_MCP_TOKEN from the environment while UNSLOTH_STUDIO_ENABLE_MCP=1.","commonSituations":"Secrets file that defines the key with an empty value; YAML/env templating that strips the token; CI enabling MCP without provisioning a token.","solutions":["Set UNSLOTH_STUDIO_MCP_TOKEN to a non-empty, non-whitespace ASCII secret before starting the backend.","Generate one with 'openssl rand -hex 32' and inject it via your secret manager.","If MCP was enabled accidentally, unset UNSLOTH_STUDIO_ENABLE_MCP."],"exampleFix":"# before\nexport UNSLOTH_STUDIO_MCP_TOKEN=\"\"\n# after\nexport UNSLOTH_STUDIO_MCP_TOKEN=\"$(openssl rand -hex 32)\"","handlingStrategy":"validation","validationCode":"def valid_mcp_token(token: str | None) -> bool:\n    return bool(token) and bool(token.strip())","typeGuard":"def is_nonempty_token(t: str | None) -> bool:\n    return isinstance(t, str) and t.strip() != \"\"","tryCatchPattern":null,"preventionTips":["Validate the token env var in a startup preflight before enabling MCP.","Provision tokens via a secret manager; never hand-edit them into manifests.","Pair this check with the ASCII check (error 886) — one validator covers both."],"tags":["mcp","auth","config","security"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}