owtaylor commited on
Commit
d958edd
·
1 Parent(s): 5d9b56c

openapi: Rewrite recursive #/components/schemas/ references

Browse files

When a schema referenced another schema as #/components/schemas/...
that wasn't properly rewritten into #/$defs/..

src/fastmcp/utilities/openapi.py CHANGED
@@ -262,16 +262,18 @@ class OpenAPIParser(
262
 
263
  if isinstance(resolved_schema, (self.schema_cls)):
264
  # Convert schema to dictionary
265
- return resolved_schema.model_dump(
266
  mode="json", by_alias=True, exclude_none=True
267
  )
268
  elif isinstance(resolved_schema, dict):
269
- return resolved_schema
270
  else:
271
  logger.warning(
272
  f"Expected Schema after resolving, got {type(resolved_schema)}. Returning empty dict."
273
  )
274
- return {}
 
 
275
  except Exception as e:
276
  logger.error(f"Failed to extract schema as dict: {e}", exc_info=False)
277
  return {}
 
262
 
263
  if isinstance(resolved_schema, (self.schema_cls)):
264
  # Convert schema to dictionary
265
+ result = resolved_schema.model_dump(
266
  mode="json", by_alias=True, exclude_none=True
267
  )
268
  elif isinstance(resolved_schema, dict):
269
+ result = resolved_schema
270
  else:
271
  logger.warning(
272
  f"Expected Schema after resolving, got {type(resolved_schema)}. Returning empty dict."
273
  )
274
+ result = {}
275
+
276
+ return _replace_ref_with_defs(result)
277
  except Exception as e:
278
  logger.error(f"Failed to extract schema as dict: {e}", exc_info=False)
279
  return {}
tests/utilities/openapi/test_openapi_advanced.py CHANGED
@@ -294,6 +294,28 @@ def test_complex_schema_route_count(parsed_complex_routes):
294
  assert len(parsed_complex_routes) == 3
295
 
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  def test_complex_schema_list_users_query_param_limit(complex_route_map):
298
  """Test that a reference to a limit query parameter is correctly resolved."""
299
  list_users = complex_route_map["listUsers"]
 
294
  assert len(parsed_complex_routes) == 3
295
 
296
 
297
+ def test_complex_schema_ref_rewriting(parsed_complex_routes):
298
+ """Test that all #/components references have been rewritten."""
299
+
300
+ def no_components(value):
301
+ if isinstance(value, dict):
302
+ for k, v in value.items():
303
+ if k == "$ref":
304
+ assert not v.startswith("#/components/"), (
305
+ f"reference '{v}' was not rewritten"
306
+ )
307
+ else:
308
+ no_components(v)
309
+ elif isinstance(value, list):
310
+ for v in value:
311
+ no_components(v)
312
+
313
+ for route in parsed_complex_routes:
314
+ no_components(route.schema_definitions)
315
+ for param in route.parameters:
316
+ no_components(param.schema_)
317
+
318
+
319
  def test_complex_schema_list_users_query_param_limit(complex_route_map):
320
  """Test that a reference to a limit query parameter is correctly resolved."""
321
  list_users = complex_route_map["listUsers"]