@@ -198,3 +198,121 @@ def hello_world():
198
198
result = tool ()
199
199
assert result == "hello world"
200
200
201
+
202
+ def test_backwards_compatibility ():
203
+ """Tests that the same tools work consistently with structured_output=False and True.
204
+
205
+ This ensures existing code doesn't break when upgrading and that tools
206
+ behave predictably regardless of the structured_output setting.
207
+ """
208
+ server_script = dedent (
209
+ """
210
+ from mcp.server.fastmcp import FastMCP
211
+ from typing import Any
212
+
213
+ mcp = FastMCP("Compatibility Server")
214
+
215
+ @mcp.tool()
216
+ def simple_text_tool(message: str) -> str:
217
+ '''Returns a simple text message'''
218
+ return f"Echo: {message}"
219
+
220
+ @mcp.tool()
221
+ def structured_data_tool(location: str) -> dict[str, Any]:
222
+ '''Returns structured weather data'''
223
+ return {
224
+ "location": location,
225
+ "weather": "sunny",
226
+ "temperature": 22,
227
+ "humidity": 65
228
+ }
229
+
230
+ mcp.run()
231
+ """
232
+ )
233
+
234
+ # Test with legacy behavior (structured_output=False)
235
+ with MCPAdapt (
236
+ StdioServerParameters (
237
+ command = "uv" , args = ["run" , "python" , "-c" , server_script ]
238
+ ),
239
+ SmolAgentsAdapter (structured_output = False ),
240
+ ) as legacy_tools :
241
+ # We know the tools are created in order, so we can use simple indexing
242
+ simple_tool_legacy = legacy_tools [0 ] # simple_text_tool
243
+ structured_tool_legacy = legacy_tools [1 ] # structured_data_tool
244
+
245
+ # Verify we got the right tools
246
+ assert simple_tool_legacy .name == "simple_text_tool"
247
+ assert structured_tool_legacy .name == "structured_data_tool"
248
+
249
+ # Both tools should have string output_type in legacy mode
250
+ assert simple_tool_legacy .output_type == "string"
251
+ assert structured_tool_legacy .output_type == "string"
252
+ assert simple_tool_legacy .output_schema is None
253
+ assert structured_tool_legacy .output_schema is None
254
+ assert simple_tool_legacy .use_structured_features is False
255
+ assert structured_tool_legacy .use_structured_features is False
256
+
257
+ # Call the tools in legacy mode
258
+ simple_result_legacy = simple_tool_legacy (message = "test" )
259
+ structured_result_legacy = structured_tool_legacy (location = "London" )
260
+
261
+ assert simple_result_legacy == "Echo: test"
262
+ # structured_tool result should be JSON string in legacy mode
263
+ assert isinstance (structured_result_legacy , str )
264
+ assert "London" in structured_result_legacy
265
+ assert "sunny" in structured_result_legacy
266
+
267
+ # Test with enhanced behavior (structured_output=True)
268
+ with MCPAdapt (
269
+ StdioServerParameters (
270
+ command = "uv" , args = ["run" , "python" , "-c" , server_script ]
271
+ ),
272
+ SmolAgentsAdapter (structured_output = True ),
273
+ ) as enhanced_tools :
274
+ # Same tools, same order - much clearer than complex searching
275
+ simple_tool_enhanced = enhanced_tools [0 ] # simple_text_tool
276
+ structured_tool_enhanced = enhanced_tools [1 ] # structured_data_tool
277
+
278
+ # Verify we got the right tools
279
+ assert simple_tool_enhanced .name == "simple_text_tool"
280
+ assert structured_tool_enhanced .name == "structured_data_tool"
281
+
282
+ # Both tools get enhanced features and schemas
283
+ assert simple_tool_enhanced .output_type == "object" # FastMCP auto-generates schema even for str
284
+ assert simple_tool_enhanced .output_schema is not None # Schema exists for str return type
285
+ assert simple_tool_enhanced .use_structured_features is True
286
+
287
+ assert structured_tool_enhanced .output_type == "object"
288
+ assert structured_tool_enhanced .output_schema is not None
289
+ assert structured_tool_enhanced .use_structured_features is True
290
+
291
+ # Call the tools in enhanced mode
292
+ simple_result_enhanced = simple_tool_enhanced (message = "test" )
293
+ structured_result_enhanced = structured_tool_enhanced (location = "London" )
294
+
295
+ # Key behavior differences discovered by this test:
296
+
297
+ # Simple text tools - enhanced mode wraps results
298
+ assert isinstance (simple_result_enhanced , dict )
299
+ assert simple_result_enhanced ["result" ] == "Echo: test" # Wrapped in result object
300
+ # vs legacy mode returns plain string: "Echo: test"
301
+
302
+ # Structured tools show the format difference:
303
+ assert isinstance (structured_result_enhanced , dict ) # Dict in enhanced mode
304
+ assert structured_result_enhanced ["location" ] == "London"
305
+ assert structured_result_enhanced ["weather" ] == "sunny"
306
+
307
+ # Legacy mode returns JSON string, enhanced mode returns dict
308
+ # But both contain the same underlying data
309
+ import json
310
+ parsed_legacy_result = json .loads (structured_result_legacy )
311
+ assert parsed_legacy_result == structured_result_enhanced # Same data, different format
312
+
313
+ # Backwards compatibility verified:
314
+ # - Legacy mode: Returns raw data (strings as strings, dicts as JSON strings)
315
+ # - Enhanced mode: Returns structured objects (strings wrapped, dicts native)
316
+ # - Both contain the same information, just in different formats
317
+ # - Existing code will keep working, new code gets enhanced features
318
+
0 commit comments