diff --git a/src/fastmcp/server/openapi.py b/src/fastmcp/server/openapi.py index 88539ff65..a2dd3e21e 100644 --- a/src/fastmcp/server/openapi.py +++ b/src/fastmcp/server/openapi.py @@ -286,12 +286,14 @@ class OpenAPIResource(Resource): # Find the path parameter names from the route path param_matches = re.findall(r"\{([^}]+)\}", path) if param_matches: + # Reverse sorting from creation order (traversal is backwards) + param_matches.sort(reverse=True) # Number of sent parameters is number of parts -1 (assuming first part is resource identifier) expected_param_count = len(parts) -1 # Map parameters from the end of the URI to the parameters in the path # Last parameter in URI (parts[-1]) maps to last parameter in path, and so on for i, param_name in enumerate(param_matches): - if i < len(expected_param_count): # Ensure we don't use resource identifier as parameter + if i < expected_param_count: # Ensure we don't use resource identifier as parameter param_value = parts[-1-i] # Get values from the end of parts path_params[param_name] = param_value diff --git a/tests/server/test_openapi.py b/tests/server/test_openapi.py index 5d0506648..091b158db 100644 --- a/tests/server/test_openapi.py +++ b/tests/server/test_openapi.py @@ -53,7 +53,7 @@ def fastapi_app(users_db: dict[int, User]) -> FastAPI: @app.get("/users/{user_id}/{is_active}", tags=["users", "detail"]) async def get_user_active_state(user_id: int, is_active: bool) -> User | None: - """Get a user by ID.""" + """Get a user by ID and filter by active state.""" user = users_db.get(user_id) if user is not None and user.active == is_active: return user @@ -359,7 +359,7 @@ class TestResourceTemplates: is_active = True async with Client(fastmcp_openapi_server) as client: resource_response = await client.read_resource( - f"resource://openapi/get_user_users__user_type__user_id__get/{user_id}/{is_active}" + f"resource://openapi/get_user_active_state_users__user_id___is_active__get/{is_active}/{user_id}" ) assert isinstance(resource_response[0], TextResourceContents) response_text = resource_response[0].text