BerriAI/litellm · error · HTTPException

MCP Security: request references unregistered MCP server(s):

Error message

MCP Security: request references unregistered MCP server(s): {', '.join(sorted(unregistered))}. Only servers registered on this gateway are allowed.

What it means

The MCP security guardrail inspects request tools of type mcp whose server_url uses the litellm_proxy:// prefix, extracts the server name, and compares it against the gateway's MCP server registry (global_mcp_server_manager, fed by the mcp_servers config). Unregistered names trigger HTTPException 400 with the sorted list when on_violation is 'block', or just a warning log when 'alert'.

Source

Thrown at litellm/proxy/guardrails/guardrail_hooks/mcp_security/mcp_security_guardrail.py:60

        cache: Any,
        data: dict,
        call_type: str,
    ) -> Exception | str | dict | None:
        if self.should_run_guardrail(data=data, event_type=GuardrailEventHooks.pre_call) is not True:
            return data

        unregistered: Final = self._find_unregistered_mcp_servers(data)
        if not unregistered:
            return data

        message: Final = (
            f"MCP Security: request references unregistered MCP server(s): "
            f"{', '.join(sorted(unregistered))}. "
            f"Only servers registered on this gateway are allowed."
        )

        if self.on_violation == "block":
            raise HTTPException(
                status_code=400,
                detail={
                    "error": "Violated guardrail policy",
                    "guardrail": "mcp_security",
                    "unregistered_servers": sorted(unregistered),
                    "detection_message": message,
                },
            )
        else:
            verbose_proxy_logger.warning(message)

        return data

    @staticmethod
    def _extract_mcp_server_names_from_tools(tools: list[dict]) -> set[str]:
        """Extract MCP server names from tools with type=mcp and litellm_proxy server_url."""
        server_names: Final[set[str]] = set()
        for tool in tools:

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Register the referenced server under mcp_servers in the gateway config with the exact name clients use
  2. Fix the client's tool definition so server_url uses the registered name (check spelling/case)
  3. If evaluation without blocking is wanted, set on_violation: 'alert' so violations only log
  4. Remove stale tool references from clients after deleting a server from the registry

Example fix

# before - request references a server the gateway never registered
tools = [{"type": "mcp", "server_url": "litellm_proxy://filesystem"}]

# after - name matches the mcp_servers registry entry (config: mcp_servers: {fs: {...}})
tools = [{"type": "mcp", "server_url": "litellm_proxy://fs"}]
Defensive patterns

Strategy: validation

Validate before calling

def validate_mcp_tool_servers(tools: list[dict], registered: set[str]) -> None:  
    prefix = "litellm_proxy://"  
    referenced = {  
        t["server_url"][len(prefix):]  
        for t in tools  
        if isinstance(t, dict) and t.get("type") == "mcp"  
        and isinstance(t.get("server_url"), str)  
        and t["server_url"].startswith(prefix)  
    }  
    unknown = referenced - registered  
    assert not unknown, f"tools reference unregistered MCP servers: {sorted(unknown)}; registered: {sorted(registered)}"  
  
# registered = set of keys under mcp_servers in the gateway config  
validate_mcp_tool_servers(request_tools, registered_server_names)

Type guard

def references_registered_servers_only(tools: object, registered: set[str]) -> bool:  
    if not isinstance(tools, list):  
        return True  
    prefix = "litellm_proxy://"  
    for t in tools:  
        if isinstance(t, dict) and t.get("type") == "mcp" and isinstance(t.get("server_url"), str):  
            url = t["server_url"]  
            if url.startswith(prefix) and url[len(prefix):] not in registered:  
                return False  
    return True

Prevention

When it happens

Trigger: A request whose tools include {type: mcp, server_url: litellm_proxy://<name>} where <name> is not defined under mcp_servers in the proxy config - typo'd name, server removed but clients still send it, or requests crafted against servers this gateway never hosted.

Common situations: Renaming/removing an MCP server in config while client SDKs keep the old name; per-team gateways where a request assumes another gateway's servers; typo between config and client tool definitions; multi-environment drift (server exists in staging, not prod).

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/586d52324728fc6d. Report an issue: GitHub.