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
- List servers (GET /api/mcp-servers/) and use the current id from the response.
- Handle 404 by refreshing the local list and re-selecting the row instead of retrying blindly.
- 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
- Refresh the id list before editing rows touched by other tabs/sessions.
- Store ids from the create response, never hard-code them.
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
- Local (stdio) MCP servers can only be configured from the Un
- Dataset not found in cache
- This dataset is no longer on disk. Add it again or pick anot
- dataset_local_cache_miss
- UNSLOTH_STUDIO_MCP_TOKEN is required when MCP is enabled
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/b4d4028406666776.
Report an issue: GitHub.