unslothai/unsloth · error · HTTPException

MCP server not found

Error message

MCP server not found

What it means

404 from the PUT update endpoint when mcp_servers_db.get_server(server_id) returns no row. The id is the 16-hex-char identifier assigned at create time (uuid4().hex[:16]); an unknown, mistyped, or deleted id yields this 404 before any payload validation side effects.

Source

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

        if payload.use_oauth is None:
            raise HTTPException(status_code = 400, detail = "use_oauth must be true or false")
        changes["use_oauth"] = payload.use_oauth
    # stdio is OAuth-less: drop a stale OAuth flag when switching to a command.
    if "url" in changes and is_stdio(changes["url"]):
        changes["use_oauth"] = False
    return changes


@router.put("/{server_id}", response_model = McpServerResponse)
async def update_mcp_server(
    server_id: str,
    payload: McpServerUpdate,
    current_subject: str = Depends(get_current_subject),
    via_api_key: ViaApiKey = False,
):
    old = mcp_servers_db.get_server(server_id)
    if not old:
        raise HTTPException(status_code = 404, detail = "MCP server not found")
    changes = _changes_from_payload(payload)
    if not changes:
        raise HTTPException(status_code = 400, detail = "No fields to update")
    # Both directions, so an API key can neither repoint an http row at a command
    # nor edit a stdio row's env/name/enabled flag. Before every side effect, so a
    # refusal leaves the row, its OAuth tokens, cache and sessions untouched.
    if is_stdio(old["url"]) or is_stdio(changes.get("url", old["url"])):
        require_ui_session_for_local_commands(via_api_key)
    # headers == HTTP headers (remote) or env vars (stdio). On a transport-type
    # switch with no new headers, drop the old ones so env secrets aren't
    # re-sent as HTTP headers (or vice versa).
    if (
        "url" in changes
        and is_stdio(changes["url"]) != is_stdio(old["url"])
        and "headers_json" not in changes
    ):
        changes["headers_json"] = None
    # Clear persisted OAuth tokens when the URL changes or OAuth is disabled;

View on GitHub (pinned to 203007d190)

Solutions

  1. List servers (GET /api/mcp-servers/) and use the current id from the response.
  2. Handle 404 by refreshing the local list and re-selecting the row instead of retrying blindly.
  3. If the row must exist, re-create it with POST and store the returned id.

Example fix

// before
await api.put(`/api/mcp-servers/${cachedId}`, payload); // 404 after row deleted elsewhere

// after
const servers = await api.get('/api/mcp-servers/').then(r => r.json());
const row = servers.find(s => s.display_name === name);
if (!row) throw new Error('Server row gone — recreate it');
await api.put(`/api/mcp-servers/${row.id}`, payload);
Defensive patterns

Strategy: validation

Validate before calling

const servers = await api.get('/api/mcp-servers/').then(r => r.json());
if (!servers.some(s => s.id === serverId)) throw new Error('Unknown MCP server id');

Type guard

async function serverExists(id) { const r = await api.get(`/api/mcp-servers/${id}`); return r.status === 200; }

Try / catch

catch (e) { if (e.status === 404) { await refreshServerList(); return null; } throw e; }

Prevention

When it happens

Trigger: PUT /api/mcp-servers/{server_id} with an id that was deleted, truncated/typo'd, or invented; also stale UI sessions holding ids from a reset database.

Common situations: Editing an MCP server in one tab after deleting it in another; a recreated database losing rows while the client caches old ids; scripts re-using hard-coded ids after a re-install.

Related errors


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