22from typing import Any
33
44import pytest
5+ from inline_snapshot import snapshot
56from mcp .types import Tool as MCPTool
6- from pydantic import BaseModel
7+ from pydantic import BaseModel , TypeAdapter
78
8- from agents import FunctionTool , RunContextWrapper
9+ from agents import Agent , FunctionTool , RunContextWrapper
910from agents .exceptions import AgentsException , ModelBehaviorError
1011from agents .mcp import MCPServer , MCPUtil
1112
@@ -18,7 +19,16 @@ class Foo(BaseModel):
1819
1920
2021class Bar (BaseModel ):
21- qux : str
22+ qux : dict [str , str ]
23+
24+
25+ Baz = TypeAdapter (dict [str , str ])
26+
27+
28+ def _convertible_schema () -> dict [str , Any ]:
29+ schema = Foo .model_json_schema ()
30+ schema ["additionalProperties" ] = False
31+ return schema
2232
2333
2434@pytest .mark .asyncio
@@ -47,7 +57,7 @@ async def test_get_all_function_tools():
4757 server3 .add_tool (names [4 ], schemas [4 ])
4858
4959 servers : list [MCPServer ] = [server1 , server2 , server3 ]
50- tools = await MCPUtil .get_all_function_tools (servers )
60+ tools = await MCPUtil .get_all_function_tools (servers , convert_schemas_to_strict = False )
5161 assert len (tools ) == 5
5262 assert all (tool .name in names for tool in tools )
5363
@@ -56,6 +66,11 @@ async def test_get_all_function_tools():
5666 assert tool .params_json_schema == schemas [idx ]
5767 assert tool .name == names [idx ]
5868
69+ # Also make sure it works with strict schemas
70+ tools = await MCPUtil .get_all_function_tools (servers , convert_schemas_to_strict = True )
71+ assert len (tools ) == 5
72+ assert all (tool .name in names for tool in tools )
73+
5974
6075@pytest .mark .asyncio
6176async def test_invoke_mcp_tool ():
@@ -107,3 +122,141 @@ async def test_mcp_invocation_crash_causes_error(caplog: pytest.LogCaptureFixtur
107122 await MCPUtil .invoke_mcp_tool (server , tool , ctx , "" )
108123
109124 assert "Error invoking MCP tool test_tool_1" in caplog .text
125+
126+
127+ @pytest .mark .asyncio
128+ async def test_agent_convert_schemas_true ():
129+ """Test that setting convert_schemas_to_strict to True converts non-strict schemas to strict.
130+ - 'foo' tool is already strict and remains strict.
131+ - 'bar' tool is non-strict and becomes strict (additionalProperties set to False, etc).
132+ """
133+ strict_schema = Foo .model_json_schema ()
134+ non_strict_schema = Baz .json_schema ()
135+ possible_to_convert_schema = _convertible_schema ()
136+
137+ server = FakeMCPServer ()
138+ server .add_tool ("foo" , strict_schema )
139+ server .add_tool ("bar" , non_strict_schema )
140+ server .add_tool ("baz" , possible_to_convert_schema )
141+ agent = Agent (
142+ name = "test_agent" , mcp_servers = [server ], mcp_config = {"convert_schemas_to_strict" : True }
143+ )
144+ tools = await agent .get_mcp_tools ()
145+
146+ foo_tool = next (tool for tool in tools if tool .name == "foo" )
147+ assert isinstance (foo_tool , FunctionTool )
148+ bar_tool = next (tool for tool in tools if tool .name == "bar" )
149+ assert isinstance (bar_tool , FunctionTool )
150+ baz_tool = next (tool for tool in tools if tool .name == "baz" )
151+ assert isinstance (baz_tool , FunctionTool )
152+
153+ # Checks that additionalProperties is set to False
154+ assert foo_tool .params_json_schema == snapshot (
155+ {
156+ "properties" : {
157+ "bar" : {"title" : "Bar" , "type" : "string" },
158+ "baz" : {"title" : "Baz" , "type" : "integer" },
159+ },
160+ "required" : ["bar" , "baz" ],
161+ "title" : "Foo" ,
162+ "type" : "object" ,
163+ "additionalProperties" : False ,
164+ }
165+ )
166+ assert foo_tool .strict_json_schema is True , "foo_tool should be strict"
167+
168+ # Checks that additionalProperties is set to False
169+ assert bar_tool .params_json_schema == snapshot (
170+ {
171+ "type" : "object" ,
172+ "additionalProperties" : {"type" : "string" },
173+ }
174+ )
175+ assert bar_tool .strict_json_schema is False , "bar_tool should not be strict"
176+
177+ # Checks that additionalProperties is set to False
178+ assert baz_tool .params_json_schema == snapshot (
179+ {
180+ "properties" : {
181+ "bar" : {"title" : "Bar" , "type" : "string" },
182+ "baz" : {"title" : "Baz" , "type" : "integer" },
183+ },
184+ "required" : ["bar" , "baz" ],
185+ "title" : "Foo" ,
186+ "type" : "object" ,
187+ "additionalProperties" : False ,
188+ }
189+ )
190+ assert baz_tool .strict_json_schema is True , "baz_tool should be strict"
191+
192+
193+ @pytest .mark .asyncio
194+ async def test_agent_convert_schemas_false ():
195+ """Test that setting convert_schemas_to_strict to False leaves tool schemas as non-strict.
196+ - 'foo' tool remains strict.
197+ - 'bar' tool remains non-strict (additionalProperties remains True).
198+ """
199+ strict_schema = Foo .model_json_schema ()
200+ non_strict_schema = Baz .json_schema ()
201+ possible_to_convert_schema = _convertible_schema ()
202+
203+ server = FakeMCPServer ()
204+ server .add_tool ("foo" , strict_schema )
205+ server .add_tool ("bar" , non_strict_schema )
206+ server .add_tool ("baz" , possible_to_convert_schema )
207+
208+ agent = Agent (
209+ name = "test_agent" , mcp_servers = [server ], mcp_config = {"convert_schemas_to_strict" : False }
210+ )
211+ tools = await agent .get_mcp_tools ()
212+
213+ foo_tool = next (tool for tool in tools if tool .name == "foo" )
214+ assert isinstance (foo_tool , FunctionTool )
215+ bar_tool = next (tool for tool in tools if tool .name == "bar" )
216+ assert isinstance (bar_tool , FunctionTool )
217+ baz_tool = next (tool for tool in tools if tool .name == "baz" )
218+ assert isinstance (baz_tool , FunctionTool )
219+
220+ assert foo_tool .params_json_schema == strict_schema
221+ assert foo_tool .strict_json_schema is False , "Shouldn't be converted unless specified"
222+
223+ assert bar_tool .params_json_schema == non_strict_schema
224+ assert bar_tool .strict_json_schema is False
225+
226+ assert baz_tool .params_json_schema == possible_to_convert_schema
227+ assert baz_tool .strict_json_schema is False , "Shouldn't be converted unless specified"
228+
229+
230+ @pytest .mark .asyncio
231+ async def test_agent_convert_schemas_unset ():
232+ """Test that leaving convert_schemas_to_strict unset (defaulting to False) leaves tool schemas
233+ as non-strict.
234+ - 'foo' tool remains strict.
235+ - 'bar' tool remains non-strict.
236+ """
237+ strict_schema = Foo .model_json_schema ()
238+ non_strict_schema = Baz .json_schema ()
239+ possible_to_convert_schema = _convertible_schema ()
240+
241+ server = FakeMCPServer ()
242+ server .add_tool ("foo" , strict_schema )
243+ server .add_tool ("bar" , non_strict_schema )
244+ server .add_tool ("baz" , possible_to_convert_schema )
245+ agent = Agent (name = "test_agent" , mcp_servers = [server ])
246+ tools = await agent .get_mcp_tools ()
247+
248+ foo_tool = next (tool for tool in tools if tool .name == "foo" )
249+ assert isinstance (foo_tool , FunctionTool )
250+ bar_tool = next (tool for tool in tools if tool .name == "bar" )
251+ assert isinstance (bar_tool , FunctionTool )
252+ baz_tool = next (tool for tool in tools if tool .name == "baz" )
253+ assert isinstance (baz_tool , FunctionTool )
254+
255+ assert foo_tool .params_json_schema == strict_schema
256+ assert foo_tool .strict_json_schema is False , "Shouldn't be converted unless specified"
257+
258+ assert bar_tool .params_json_schema == non_strict_schema
259+ assert bar_tool .strict_json_schema is False
260+
261+ assert baz_tool .params_json_schema == possible_to_convert_schema
262+ assert baz_tool .strict_json_schema is False , "Shouldn't be converted unless specified"
0 commit comments