|
25 | 25 | import sys |
26 | 26 | import types as builtin_types |
27 | 27 | import typing |
28 | | -from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Union, _UnionGenericAlias # type: ignore |
| 28 | +from typing import Annotated, Any, Callable, Dict, List, Literal, Optional, Sequence, Union, _UnionGenericAlias # type: ignore |
29 | 29 | import pydantic |
30 | | -from pydantic import ConfigDict, Field, PrivateAttr, model_validator |
| 30 | +from pydantic import ConfigDict, Field, PrivateAttr, WrapValidator, model_validator |
31 | 31 | from typing_extensions import Self, TypedDict |
32 | 32 | from . import _common |
33 | 33 | from ._operations_converters import ( |
|
63 | 63 | except ImportError: |
64 | 64 | PIL_Image = None |
65 | 65 |
|
66 | | -_is_mcp_imported = False |
67 | 66 | if typing.TYPE_CHECKING: |
68 | 67 | from mcp import types as mcp_types |
69 | 68 | from mcp import ClientSession as McpClientSession |
70 | 69 | from mcp.types import CallToolResult as McpCallToolResult |
71 | | - |
72 | | - _is_mcp_imported = True |
73 | 70 | else: |
74 | 71 | McpClientSession: typing.Type = Any |
75 | 72 | McpCallToolResult: typing.Type = Any |
76 | | - try: |
77 | | - from mcp import types as mcp_types |
78 | | - from mcp import ClientSession as McpClientSession |
79 | | - from mcp.types import CallToolResult as McpCallToolResult |
80 | 73 |
|
81 | | - _is_mcp_imported = True |
82 | | - except ImportError: |
83 | | - McpClientSession = None |
84 | | - McpCallToolResult = None |
| 74 | + |
| 75 | +def _is_mcp_imported() -> bool: |
| 76 | + return 'mcp' in sys.modules |
| 77 | + |
85 | 78 |
|
86 | 79 | if typing.TYPE_CHECKING: |
87 | 80 | import yaml |
@@ -1789,7 +1782,7 @@ class FunctionResponse(_common.BaseModel): |
1789 | 1782 | def from_mcp_response( |
1790 | 1783 | cls, *, name: str, response: McpCallToolResult |
1791 | 1784 | ) -> 'FunctionResponse': |
1792 | | - if not _is_mcp_imported: |
| 1785 | + if not _is_mcp_imported(): |
1793 | 1786 | raise ValueError( |
1794 | 1787 | 'MCP response is not supported. Please ensure that the MCP library is' |
1795 | 1788 | ' imported.' |
@@ -4888,17 +4881,41 @@ class ToolDict(TypedDict, total=False): |
4888 | 4881 |
|
4889 | 4882 |
|
4890 | 4883 | ToolOrDict = Union[Tool, ToolDict] |
4891 | | -if _is_mcp_imported: |
| 4884 | + |
| 4885 | + |
| 4886 | +def _validate_tool_list(v: Any, handler: Any) -> Any: |
| 4887 | + """Pass MCP tool/session objects through Pydantic validation untouched. |
| 4888 | + |
| 4889 | + `Tool` has all-optional fields, so without this wrapper Pydantic's default |
| 4890 | + Union resolution would coerce any non-Tool item (e.g. an `mcp.ClientSession`) |
| 4891 | + into an empty `Tool()`. This wrapper dispatches dict -> Tool and leaves |
| 4892 | + every other type identity-preserved so MCP routing downstream still works. |
| 4893 | + """ |
| 4894 | + if v is None: |
| 4895 | + return None |
| 4896 | + if not isinstance(v, list): |
| 4897 | + return handler(v) |
| 4898 | + out = [] |
| 4899 | + for item in v: |
| 4900 | + if isinstance(item, dict): |
| 4901 | + out.append(Tool.model_validate(item)) |
| 4902 | + else: |
| 4903 | + out.append(item) |
| 4904 | + return out |
| 4905 | + |
| 4906 | + |
| 4907 | +if typing.TYPE_CHECKING: |
4892 | 4908 | ToolUnion = Union[Tool, Callable[..., Any], mcp_types.Tool, McpClientSession] |
4893 | 4909 | ToolUnionDict = Union[ |
4894 | 4910 | ToolDict, Callable[..., Any], mcp_types.Tool, McpClientSession |
4895 | 4911 | ] |
| 4912 | + ToolListUnion = list[ToolUnion] |
| 4913 | + ToolListUnionDict = list[ToolUnionDict] |
4896 | 4914 | else: |
4897 | 4915 | ToolUnion = Union[Tool, Callable[..., Any]] # type: ignore[misc] |
4898 | 4916 | ToolUnionDict = Union[ToolDict, Callable[..., Any]] # type: ignore[misc] |
4899 | | - |
4900 | | -ToolListUnion = list[ToolUnion] |
4901 | | -ToolListUnionDict = list[ToolUnionDict] |
| 4917 | + ToolListUnion = Annotated[list, WrapValidator(_validate_tool_list)] |
| 4918 | + ToolListUnionDict = list[ToolUnionDict] |
4902 | 4919 |
|
4903 | 4920 | SchemaUnion = Union[ |
4904 | 4921 | dict[Any, Any], type, Schema, builtin_types.GenericAlias, VersionedUnionType # type: ignore[valid-type] |
|
0 commit comments