BerriAI/litellm · error · HTTPException
tool_server_mismatch
tool_server_mismatch
Error message
Tool '{name}' belongs to MCP server '{mcp_server.name}' but request specified server_id for '{requested_server.name}'. What it means
In the MCP session tool-call path, LiteLLM resolves which server owns the requested tool name (accepting server-prefixed spellings). If the request also carries a server_id and that ID belongs to a different server than the resolved owner, the call is rejected with HTTP 403 and error code tool_server_mismatch — a guard against ambiguous or stale routing.
Source
Thrown at litellm/proxy/_experimental/mcp_server/server.py:2712
original_tool_name = name
else:
# Resolve from tool name (MCP JSON-RPC or prefixed REST tool names).
mcp_server = global_mcp_server_manager._get_mcp_server_from_tool_name(name)
if mcp_server is None and requested_server is not None:
for known_prefix in iter_known_server_prefixes(requested_server):
candidate = global_mcp_server_manager._get_mcp_server_from_tool_name(
add_server_prefix_to_name(name, known_prefix)
)
if candidate is not None:
mcp_server = candidate
break
if mcp_server is not None:
server_name = mcp_server.name
original_tool_name = strip_known_server_prefix(name, mcp_server)
if requested_server is not None:
if mcp_server is not None and mcp_server.server_id != requested_server.server_id:
raise HTTPException(
status_code=403,
detail={
"error": "tool_server_mismatch",
"message": (
f"Tool '{name}' belongs to MCP server "
f"'{mcp_server.name}' but request specified "
f"server_id for '{requested_server.name}'."
),
},
)
if mcp_server is None:
mcp_server = requested_server
server_name = requested_server.name
original_tool_name = strip_known_server_prefix(name, requested_server)
# Only enforce server-level permissions when we can resolve a server
if server_name:
if not MCPRequestHandler.is_tool_allowed(View on GitHub (pinned to 77b7c6c40c)
Solutions
- Drop the server_id from the request and let name resolution route the call.
- Or pass the server_id of the server that actually owns the tool — confirm with GET /mcp-rest/tools/list per server.
- After deleting and re-adding a server, refresh any cached server_id in client code.
Example fix
# before — server_id points at the wrong server
{"server_id": "srv-B", "name": "github__create_issue"}
# after — either use the owning server's id
{"server_id": "srv-A", "name": "github__create_issue"}
# or omit server_id entirely
{"name": "github__create_issue"} Defensive patterns
Strategy: validation
Validate before calling
async def owning_server_id(client: httpx.AsyncClient, tool_name: str) -> str | None:
tools = (await client.get(f"{base}/mcp-rest/tools/list")).json().get("tools", [])
for t in tools:
if t.get("name") == tool_name or t.get("name", "").endswith(f"__{tool_name}"):
return t.get("server_id") or t.get("spec", {}).get("server_id")
return None
sid = await owning_server_id(client, name)
payload = {"name": name, "arguments": args} if sid is None else {"server_id": sid, "name": name, "arguments": args} Try / catch
except httpx.HTTPStatusError as e:
d = e.response.json().get("detail", {})
if e.response.status_code == 403 and isinstance(d, dict) and d.get("error") == "tool_server_mismatch":
# drop server_id and retry on name alone, or resend with the owning server's id
payload.pop("server_id", None)
return await client.post(f"{base}/mcp-rest/tools/call", json=payload)
raise Prevention
- Never cache server_id across server re-registrations; re-resolve after any registry change.
- When both server_id and a prefixed tool name are sent, verify the prefix matches that server.
- Prefer the server-prefixed tool name alone — it carries the routing and avoids stale IDs.
When it happens
Trigger: The request passes server_id of server B while the (prefixed) tool name belongs to server A; a stale server_id captured before the server was deleted and re-registered under a new ID; the same tool name exposed by two servers.
Common situations: Clients caching server_id across re-registrations; copy-pasted payloads mixing values from different servers; duplicate tool names after merging MCP configs.
Related errors
- forbidden
- ip_filtering
- access_denied
- User does not have permission to test MCP server connections
- User does not have permission to test MCP server tools. Only
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/56d381b190908882.
Report an issue: GitHub.