fix: support MCP tools in Generic Guardrail API

The `tools` field in `GenericGuardrailAPIRequest`, `GenericGuardrailAPIResponse`,
and `GenericGuardrailAPIInputs` was typed as `List[ChatCompletionToolParam]`,
which requires a `function` field. MCP tools (`type: "mcp"`) don't have this
field, causing a Pydantic validation error:

  tools.0.function - Field required [type=missing]

Changed `tools` to `List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]]`,
aligned with the Responses API pattern (transformation.py). This supports both
function tools and MCP tools with proper type validation.

Fixes #23695
This commit is contained in:
Igal Boxerman 2026-03-15 19:53:22 +02:00
parent 548e7ebd60
commit c96576cb04
2 changed files with 7 additions and 5 deletions

View file

@ -1,12 +1,13 @@
from typing import Any, Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field
from typing_extensions import TYPE_CHECKING, TypedDict
from typing_extensions import TypedDict
from litellm.types.llms.openai import (
AllMessageValues,
ChatCompletionToolCallChunk,
ChatCompletionToolParam,
OpenAIMcpServerTool,
)
from litellm.types.proxy.guardrails.guardrail_hooks.base import GuardrailConfigModel
from litellm.types.utils import ChatCompletionMessageToolCall
@ -65,7 +66,7 @@ class GenericGuardrailAPIRequest(BaseModel):
] = None # the trace id of the LLM call - useful if there are multiple LLM calls for the same conversation
structured_messages: Optional[List[AllMessageValues]] = None
images: Optional[List[str]] = None
tools: Optional[List[ChatCompletionToolParam]] = None
tools: Optional[List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]]] = None
texts: Optional[List[str]] = None
request_data: GenericGuardrailAPIMetadata
request_headers: Optional[Dict[str, str]] = Field(
@ -88,7 +89,7 @@ class GenericGuardrailAPIResponse:
texts: Optional[List[str]]
images: Optional[List[str]]
tools: Optional[List[ChatCompletionToolParam]]
tools: Optional[List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]]]
action: str
blocked_reason: Optional[str]
@ -98,7 +99,7 @@ class GenericGuardrailAPIResponse:
texts: Optional[List[str]] = None,
blocked_reason: Optional[str] = None,
images: Optional[List[str]] = None,
tools: Optional[List[ChatCompletionToolParam]] = None,
tools: Optional[List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]]] = None,
):
self.action = action
self.blocked_reason = blocked_reason

View file

@ -69,6 +69,7 @@ from .llms.openai import (
OpenAIChatCompletionChunk,
OpenAIChatCompletionFinishReason,
OpenAIFileObject,
OpenAIMcpServerTool,
OpenAIRealtimeStreamList,
ResponsesAPIResponse,
WebSearchOptions,
@ -3564,7 +3565,7 @@ class PriorityReservationSettings(BaseModel):
class GenericGuardrailAPIInputs(TypedDict, total=False):
texts: List[str] # extracted text from the LLM response - for basic text guardrails
images: List[str] # extracted images from the LLM response - for image guardrails
tools: List[ChatCompletionToolParam] # tools sent to the LLM
tools: List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]] # tools sent to the LLM
tool_calls: Union[
List[ChatCompletionToolCallChunk], List[ChatCompletionMessageToolCall]
] # tool calls sent from the LLM