[Bug]: `mcp_semantic_tool_filter` silently drops all non-MCP (“native”) tools

用户在运行 LiteLLM 代理(proxy)时,启用了 MCP 语义工具过滤功能。当客户端向 /chat/completions 或 /responses 端点发送请求,并携带原生(非 MCP)工具定义时,该功能会错误地将这些工具移除。无论客户端是否引用了 MCP 服务器,该问题都会出现。

[Bug]: `mcp_semantic_tool_filter` silently drops all non-MCP ("native") tools

[Bug]: `mcp_semantic_tool_filter` silently drops all non-MCP (“native”) tools

快速结论:当 LiteLLM 代理配置了 mcp_semantic_tool_filter.enabled: true 时,客户端发送的请求中包含的所有非 MCP 工具(即客户端本地定义的 OpenAI 格式工具)都会在转发前被静默移除,导致工具调用失败或引发 BadRequestError。优先排查是否所有工具都被语义过滤器过滤为空。

问题场景

用户在运行 LiteLLM 代理(proxy)时,启用了 MCP 语义工具过滤功能。当客户端向 /chat/completions/responses 端点发送请求,并携带原生(非 MCP)工具定义时,该功能会错误地将这些工具移除。无论客户端是否引用了 MCP 服务器,该问题都会出现。

报错原文

# 场景1: 工具调用被静默忽略,模型回复纯文本
Model answers in prose or apologizes that it cannot call the tool

# 场景2: 当同时设置 tool_choice 且非 "none" 时
BadRequestError: Invalid value for 'tool_choice': 'tool_choice' is only allowed when 'tools' are specified.

# 调试日志显示
Response header: x-litellm-semantic-filter: 1->0  (one tool in, zero out)

原因分析

问题根源在于 LiteLLM 的 semantic_tool_filter 实现中,没有区分 MCP 工具和原生工具。具体来说:

  • SemanticToolFilterHook.async_pre_call_hook 将请求中所有的 data["tools"](包括 MCP 工具和原生工具)都传入 filter_tools() 方法。
  • SemanticMCPToolFilter.filter_tools 内部调用了 self.tool_router,而该路由器仅在启动时由 global_mcp_server_manager 构建,只包含 MCP 工具的名称映射
  • _get_tools_by_names() 方法通过严格名称匹配来保留工具,原生工具因为无法匹配到任何 MCP 名称而被静默丢弃。
  • 最终,未经检查的 data["tools"] = filtered_tools 覆盖了原始工具列表,导致原生工具全部丢失。

环境排查

  • 确认 LiteLLM 版本:该问题存在于 mcp_semantic_tool_filter 功能引入后的版本。
  • 检查代理配置文件:确认 litellm_settingsmcp_semantic_tool_filter.enabled 是否为 true
  • 验证 MCP 服务器注册状态:即使未注册 MCP 服务器,该问题也可能出现(但注册了更明显)。
  • 检查响应头:查看 x-litellm-semantic-filter 头中的计数变化(如 n->0)。

解决步骤

  1. 可优先尝试:如果不需要 MCP 语义过滤,则将配置中的 mcp_semantic_tool_filter.enabled 设为 false
  2. 适用补丁:参考 Issue #26247 中提出的修复方案:
    • 在调用 filter_tools() 之前,将 data["tools"] 分区为两类:原生工具(不在 filter._tool_map 中的)和 MCP 工具
    • 语义过滤器仅应用于 MCP 工具部分。
    • 将经过过滤的 MCP 工具与未被过滤的原生工具无条件合并data["tools"]
    • 添加回归测试,覆盖混合 MCP+原生工具、以及全原生工具场景。
  3. 手动修改:如果你有权限修改代理代码,可以直接在 litellm/proxy/hooks/mcp_semantic_filter/hook.py 中实施上述分区逻辑。

验证方法

再次发送包含原生工具的请求(参考 Issue 中的 curl 示例),观察:

  • 响应头 x-litellm-semantic-filter 中计数应为 n->n(原生工具未被减少),或至少不为 n->0
  • 模型应能正常调用客户端提供的原生工具。
  • 如果使用了 tool_choice,不再出现 BadRequestError

参考来源

BerriAI/litellm #26212

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 8455

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注