unslothai/unsloth · error · HTTPException

stdio MCP servers are disabled on this host

Error message

stdio MCP servers are disabled on this host

What it means

Raised as a 400 by the refresh endpoint when the stored server URL is a stdio:// target and stdio_mcp_enabled() reports stdio MCP disabled on the current host. Refresh reuses the stored address, so the endpoint re-applies the stdio gate: a stdio row that came from a desktop database must not be allowed to spawn a local process on a hosted/network host.

Source

Thrown at studio/backend/routes/mcp_servers.py:285

    invalidate_tool_cache(server_id)
    await asyncio.to_thread(close_stdio_sessions, old["url"], parse_server_headers(old))


@router.post("/{server_id}/refresh", response_model = McpServerProbeResult)
async def refresh_mcp_server_tools(
    server_id: str,
    current_subject: str = Depends(get_current_subject),
    via_api_key: ViaApiKey = False,
):
    server = mcp_servers_db.get_server(server_id)
    if not server:
        raise HTTPException(status_code = 404, detail = "MCP server not found")
    # Refresh uses the stored address, so re-check the stdio gate here too: a
    # stdio row from a desktop DB must not spawn on a hosted/network host.
    if is_stdio(server["url"]):
        require_ui_session_for_local_commands(via_api_key)
        if not stdio_mcp_enabled():
            raise HTTPException(
                status_code = 400, detail = "stdio MCP servers are disabled on this host"
            )

    use_oauth = bool(server.get("use_oauth"))
    try:
        tools = await list_tools_async(
            url = server["url"],
            headers = parse_server_headers(server),
            timeout = probe_timeout(server["url"], use_oauth),
            use_oauth = use_oauth,
        )
    except Exception as exc:  # noqa: BLE001 — surface transport+timeout errors to UI
        logger.error(
            "mcp_servers.refresh_failed",
            server_id = server_id,
            error = str(exc),
            exc_info = True,
        )

View on GitHub (pinned to 203007d190)

Solutions

  1. Check the host's stdio MCP enablement setting/env flag and enable it if local process spawning is intended and safe on this host.
  2. If you are on a hosted/network host, delete the stdio server entry and re-add it as an HTTP/SSE MCP server instead.
  3. Refresh from a real UI session (not an API key) if the gate also requires an interactive session for local commands.
  4. Confirm is_stdio(url) classification — the URL scheme must genuinely be stdio for this branch to fire.
Defensive patterns

Strategy: validation

Validate before calling

const servers = await (await fetch('/api/mcp-servers')).json();
const srv = servers.find(s => s.id === id);
if (srv && srv.url.startsWith('stdio://')) {
  const caps = await (await fetch('/api/mcp-servers/capabilities')).json(); // or host config flag
  if (!caps.stdioEnabled) throw new Error('stdio MCP disabled on this host — use an HTTP MCP server');
}

Try / catch

try {
  await refreshMcpServer(id);
} catch (e) {
  if (e.status === 400 && /stdio.*disabled/.test(e.detail)) markServerUnavailable(id);
  else throw e;
}

Prevention

When it happens

Trigger: POST /api/mcp-servers/{id}/refresh for a server whose url starts with stdio:// while the host has stdio MCP disabled (hosted deployment, or the enabling env/config flag off), or the request arrives via an API key (via_api_key) without a UI session — require_ui_session_for_local_commands is checked first, but the 400 here is the disabled-flag branch.

Common situations: Copying a desktop studio DB (with stdio servers configured) to a hosted environment; disabling stdio servers via configuration after they were added; environment flag controlling stdio support not set on a server deployment.

Related errors


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