wrapped tools using annotations fail

用户在 MCP Python SDK 中使用 @logfire.instrument 等装饰器包装一个工具函数,且该函数所在模块开启了 from __future__ import annotations 。当通过 Tool.from_function() 注册该工具时,触发 InvalidSign

wrapped tools using annotations fail

wrapped tools using annotations fail

快速结论:当你使用装饰器(如 @logfire.instrument)包装一个函数,并且该函数所在模块使用了 from __future__ import annotations 时,MCP FastMCP 在注册工具时可能抛出 InvalidSignature 异常。优先排查你的 MCP Python SDK 版本是否已更新至包含修复的版本(PR #1496 已修复此问题)。

问题场景

用户在 MCP Python SDK 中使用 @logfire.instrument 等装饰器包装一个工具函数,且该函数所在模块开启了 from __future__ import annotations。当通过 Tool.from_function() 注册该工具时,触发 InvalidSignature 异常。

报错原文

mcp.server.fastmcp.exceptions.InvalidSignature: Unable to evaluate type annotation ForwardRef("Literal['literal'] | None")

完整 Traceback 中包含以下关键路径:

File ".../mcp/server/fastmcp/utilities/func_metadata.py", line 466, in _get_typed_annotation
    raise InvalidSignature(f"Unable to evaluate type annotation {annotation}")

原因分析

当工具函数被包装(如使用 @logfire.instrument)且模块使用了 from __future__ import annotations 时,函数注解会以字符串形式(ForwardRef)存储。MCP 的 func_metadata 在解析这些字符串注解时,错误地使用了包装器(wrapper)的 __globals__ 命名空间,而非原始函数的命名空间。由于包装器命名空间中不包含 Literal 等类型定义,导致注解无法被正确求值,从而抛出 InvalidSignature

环境排查

  • MCP Python SDK 版本:确认是否已更新至包含修复的最新版本。Issue 中提及 PR #1496 已修复此问题,该 PR 将 func_metadata 切换为使用 inspect.signature(func, eval_str=True)
  • Python 版本:Issue 中测试环境为 Python 3.11 和 3.13,理论上该问题与 Python 版本无直接关联。
  • 相关依赖版本:确认 logfire 或其他装饰器库的版本,但问题根源在于 MCP SDK 对包装函数的注解解析逻辑。

解决步骤

  1. 更新 MCP Python SDK:将 MCP Python SDK 更新到包含 PR #1496 修复的版本(该 PR 通过 inspect.signature(func, eval_str=True) 正确解析字符串注解,并感知 __wrapped__ 属性)。
  2. 验证修复:如果无法立即更新,可优先尝试将 from __future__ import annotations 移除,或手动在包装器命名空间中注入所需类型(不推荐长期方案)。
  3. 检查装饰器兼容性:确认你的装饰器(如 @logfire.instrument)是否正确设置了 __wrapped__ 属性,以支持 inspect.signature 的回溯。MCP SDK 修复依赖于该属性。

验证方法

运行以下测试代码(基于 Issue 中的最小复现示例):

from __future__ import annotations
from typing import Literal

from mcp.server.fastmcp.tools import Tool

import logfire

@logfire.instrument
def test_tool(location_type: Literal["literal"] | None = None) -> str:
    """Test tool."""
    return f"a is {location_type}"

Tool.from_function(test_tool)

如果不再抛出 InvalidSignature 异常,说明问题已修复。你也可以通过 func_metadata(test_tool) 检查返回的签名中 location_type 的类型是否正确解析为 Optional[Literal['literal']]

参考来源

modelcontextprotocol/python-sdk #1391 — Issue 讨论链包含完整的复现代码、原因分析和修复确认。修复 PR #1496 的链接:#1496

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

celebrityanime
celebrityanime
文章: 8759

发表回复

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