diff --git a/src/fastmcp/utilities/openapi.py b/src/fastmcp/utilities/openapi.py index c29b20661..fd0cbed5b 100644 --- a/src/fastmcp/utilities/openapi.py +++ b/src/fastmcp/utilities/openapi.py @@ -84,6 +84,7 @@ class HTTPRoute(FastMCPBaseModel): schema_definitions: dict[str, JsonSchema] = Field( default_factory=dict ) # Store component schemas + extensions: dict[str, Any] = Field(default_factory=dict) # Export public symbols @@ -591,6 +592,14 @@ class OpenAPIParser( getattr(operation, "responses", None) ) + extensions = {} + if hasattr(operation, "model_extra") and operation.model_extra: + extensions = { + k: v + for k, v in operation.model_extra.items() + if k.startswith("x-") + } + route = HTTPRoute( path=path_str, method=method_upper, # type: ignore[arg-type] # Known valid HTTP method @@ -602,6 +611,7 @@ class OpenAPIParser( request_body=request_body_info, responses=responses, schema_definitions=schema_definitions, + extensions=extensions, ) routes.append(route) logger.info( diff --git a/tests/utilities/openapi/test_openapi.py b/tests/utilities/openapi/test_openapi.py index a1f3bdca1..6cbad7cad 100644 --- a/tests/utilities/openapi/test_openapi.py +++ b/tests/utilities/openapi/test_openapi.py @@ -687,6 +687,29 @@ def test_multiple_tags_preserved(bookstore_schema): assert len(get_books.tags) == 3 +def test_openapi_extensions(petstore_schema): + """Test that OpenAPI extensions (x-*) are correctly parsed from operations.""" + # Add extensions to a route + petstore_schema["paths"]["/pets"]["get"]["x-rate-limit"] = 100 + petstore_schema["paths"]["/pets"]["get"]["x-custom-auth"] = "bearer" + petstore_schema["paths"]["/pets"]["get"]["x-internal"] = True + + # Parse the modified schema + routes = parse_openapi_to_http_routes(petstore_schema) + + # Find the GET /pets route + get_pets = next( + (r for r in routes if r.method == "GET" and r.path == "/pets"), None + ) + assert get_pets is not None + + # Should have extensions + assert get_pets.extensions["x-rate-limit"] == 100 + assert get_pets.extensions["x-custom-auth"] == "bearer" + assert get_pets.extensions["x-internal"] is True + assert len(get_pets.extensions) == 3 + + # --- Tests for BookStore schema --- #