unslothai/unsloth · critical · RuntimeError

UNSLOTH_STUDIO_MCP_TOKEN is required when MCP is enabled

Error message

UNSLOTH_STUDIO_MCP_TOKEN is required when MCP is enabled

What it means

Raised at app assembly time when UNSLOTH_STUDIO_ENABLE_MCP=1 but UNSLOTH_STUDIO_MCP_TOKEN is unset or empty. The MCP surface can start GPU jobs and write model artifacts, so the backend refuses to mount it unauthenticated rather than shipping an open endpoint.

Source

Thrown at studio/backend/main.py:808

    # Swagger UI and ReDoc are re-registered below on these same paths, against vendored
    # assets instead of a CDN. FastAPI's built-ins point at cdn.jsdelivr.net, and this origin
    # holds the auth tokens, so nothing third-party may execute here.
    docs_url = None,
    redoc_url = None,
    swagger_ui_oauth2_redirect_url = None,
)

# The MCP surface is opt-in: it can start GPU jobs and write model artifacts.
if os.environ.get("UNSLOTH_STUDIO_ENABLE_MCP") == "1":
    from fastmcp.utilities.lifespan import combine_lifespans

    from mcp_server import BearerTokenMiddleware, create_studio_mcp

    _studio_mcp_app = create_studio_mcp().http_app(path = "/")
    _studio_mcp_lifespan = _studio_mcp_app.lifespan
    _mcp_token = os.environ.get("UNSLOTH_STUDIO_MCP_TOKEN")
    if not _mcp_token:
        raise RuntimeError("UNSLOTH_STUDIO_MCP_TOKEN is required when MCP is enabled")
    _studio_mcp_app = BearerTokenMiddleware(_studio_mcp_app, _mcp_token)
    app.router.lifespan_context = combine_lifespans(lifespan, _studio_mcp_lifespan)
    app.mount("/mcp", _studio_mcp_app)

from loggers.config import LogConfig
from loggers.handlers import LoggingMiddleware

logger = LogConfig.setup_logging(
    service_name = "unsloth-studio-backend",
    env = os.getenv("ENVIRONMENT_TYPE", "production"),
)

app.add_middleware(LoggingMiddleware)


class ResearchPortMiddleware:
    """Capture the bound port without replacing the ASGI receive channel."""

View on GitHub (pinned to 203007d190)

Solutions

  1. Generate a strong secret (e.g. openssl rand -hex 32) and export it as UNSLOTH_STUDIO_MCP_TOKEN before starting the backend.
  2. If MCP is not intended, unset UNSLOTH_STUDIO_ENABLE_MCP (anything other than '1' disables it) instead of supplying a token.
  3. If using a secret manager, verify the variable actually lands in the process env (printenv UNSLOTH_STUDIO_MCP_TOKEN in the same context that launches the app).
  4. Restart the backend after setting both variables.

Example fix

# before
export UNSLOTH_STUDIO_ENABLE_MCP=1
# backend exits: token required
# after
export UNSLOTH_STUDIO_ENABLE_MCP=1
export UNSLOTH_STUDIO_MCP_TOKEN="$(openssl rand -hex 32)"
Defensive patterns

Strategy: validation

Validate before calling

import os

def mcp_config_valid() -> bool:
    enabled = os.environ.get("UNSLOTH_STUDIO_ENABLE_MCP") == "1"
    token = os.environ.get("UNSLOTH_STUDIO_MCP_TOKEN", "")
    return not enabled or bool(token and token.strip() and token.isascii())

Prevention

When it happens

Trigger: Enabling the MCP server with UNSLOTH_STUDIO_ENABLE_MCP=1 while forgetting to set UNSLOTH_STUDIO_MCP_TOKEN, or setting it to an empty string ('UNSLOTH_STUDIO_MCP_TOKEN='), in the process environment before main.py builds the FastAPI app.

Common situations: Following MCP setup docs that mention the enable flag but not the token; deploying with a secrets file that failed to load so the env var is missing; CI pipelines enabling MCP for testing without credentials.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/c3f4cca1d2fdc76e. Report an issue: GitHub.