Repository: google/adk-python

Issue:
Open_api_spec_parser.py not parsing path level parameters
**Describe the bug**
OpenAPI specification has path level parameters that defines the list of API parameters that commonly applies to all methods. 
Ref: https://swagger.io/docs/specification/v3_0/describing-parameters/#common-parameters

The current implementation of OpenApiSpecParser._collect_operations() loads only method level parameters. Common parameters are discarded in the whole process. 

**To Reproduce**
Steps to reproduce the behavior:
1. Create a unittest for parsing an OpenAPI spec v3.0 with path-level parameters.
    ```python def test_parse_spec_with_path_level_parameters(openapi_spec_generator):
    openapi_spec = {
        "openapi": "3.1.0",
        "info": {"title": "Combine Parameters API", "version": "1.0.0"},
        "paths": {
            "/test": {
                "parameters": [
                    {"name": "global_param", "in": "query", "schema": {"type": "string"}}
                ],
                "get": {
                    "parameters": [
                        {"name": "local_param", "in": "header", "schema": {"type": "integer"}}
                    ],
                    "operationId": "testGet",
                    "responses": {
                        "200": {
                            "description": "Successful response",
                            "content": {
                                "application/json": {"schema": {"type": "string"}}
                            },
                        }
                    },
                },
            }
        },
    }

    parsed_operations = openapi_spec_generator.parse(openapi_spec)
    assert len(parsed_operations) == 1

    operation = parsed_operations[0]
    assert len(operation.parameters) == 2

    # Verify the combined parameters
    global_param = next((p for p in operation.parameters if p.original_name == "global_param"), None)
    local_param = next((p for p in operation.parameters if p.original_name == "local_param"), None)

    assert global_param is not None
    assert global_param.param_location == "query"
    assert global_param.type_value is str

    assert local_param is not None
    assert local_param.param_location == "header"
    assert local_param.type_value is int

    ```
2. Run this unittest
3. See error
<img width="1151" alt="Image" src="https://github.com/user-attachments/assets/03e2d32f-7984-4a58-a964-99b17b43c61f" />

**Expected behavior**
In the unittest, 
```python
parsed_operations = openapi_spec_generator.parse(openapi_spec)
```
The returned parsed_operations should contains an operation with both local_param and global_param. The unittest should passed as shown below:

<img width="752" alt="Image" src="https://github.com/user-attachments/assets/cab74beb-1934-401d-821f-18e75b7119f6" />

**Root Cause Analysis**
As shown in the current implementation, the _collect_operations method which loads parameters for every HTTP methods but the common parameters block at path level is skipped and not being processed in subsequent statements too.

<img width="596" alt="Image" src="https://github.com/user-attachments/assets/a0ef822c-7415-4618-9470-200bf3d6807e" />

**Desktop (please complete the following information):**
 - OS: MacOS
 - Python version(python -V): 3.13.3
 - ADK version(pip show google-adk): 

```shell
(adk-python) ➜  adk-python (fix/missing-path-level-parameters) pip show google-adk                                                                                                                                                               ✭ ✱
Name: google-adk
Version: 0.4.0
Summary: Agent Development Kit
Home-page: https://google.github.io/adk-docs/
Author:
Author-email: Google LLC <googleapis-packages@google.com>
License:
Location: /Users/kmak/Documents/Github/adk-python/.venv/lib/python3.13/site-packages
Requires: authlib, click, fastapi, google-api-python-client, google-cloud-aiplatform, google-cloud-secret-manager, google-cloud-speech, google-cloud-storage, google-genai, graphviz, mcp, opentelemetry-api, opentelemetry-exporter-gcp-trace, opentelemetry-sdk, pydantic, python-dotenv, PyYAML, sqlalchemy, tzlocal, uvicorn
Required-by:
```
**Additional context**
The problem was found while parsing the OpenAPI spec of Kubernetes API Server located in the following path:
https://raw.githubusercontent.com/kubernetes/kubernetes/refs/heads/master/api/openapi-spec/v3/api__v1_openapi.json




Discussion:

Patch:
diff --git a/src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py b/src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py
index ac86cd057..64eb204fb 100644
--- a/src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py
+++ b/src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py
@@ -111,6 +111,11 @@ def _collect_operations(
         if operation_dict is None:
           continue
 
+        # Append path-level parameters
+        operation_dict["parameters"] = operation_dict.get(
+            "parameters", []
+        ) + path_item.get("parameters", [])
+
         # If operation ID is missing, assign an operation id based on path
         # and method
         if "operationId" not in operation_dict:


Test patch:
diff --git a/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py b/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py
index 8fbee55fc..053da7598 100644
--- a/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py
+++ b/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py
@@ -624,3 +624,60 @@ def test_parse_spec_with_duplicate_parameter_names(openapi_spec_generator):
   assert body_param is not None
   assert body_param.original_name == "name"
   assert body_param.py_name == "name_0"
+
+
+def test_parse_spec_with_path_level_parameters(openapi_spec_generator):
+  """Test that operation parameters are correctly combined with path-level parameters."""
+  openapi_spec = {
+      "openapi": "3.1.0",
+      "info": {"title": "Combine Parameters API", "version": "1.0.0"},
+      "paths": {
+          "/test": {
+              "parameters": [{
+                  "name": "global_param",
+                  "in": "query",
+                  "schema": {"type": "string"},
+              }],
+              "get": {
+                  "parameters": [{
+                      "name": "local_param",
+                      "in": "header",
+                      "schema": {"type": "integer"},
+                  }],
+                  "operationId": "testGet",
+                  "responses": {
+                      "200": {
+                          "description": "Successful response",
+                          "content": {
+                              "application/json": {"schema": {"type": "string"}}
+                          },
+                      }
+                  },
+              },
+          }
+      },
+  }
+
+  parsed_operations = openapi_spec_generator.parse(openapi_spec)
+  assert len(parsed_operations) == 1
+
+  operation = parsed_operations[0]
+  assert len(operation.parameters) == 2
+
+  # Verify the combined parameters
+  global_param = next(
+      (p for p in operation.parameters if p.original_name == "global_param"),
+      None,
+  )
+  local_param = next(
+      (p for p in operation.parameters if p.original_name == "local_param"),
+      None,
+  )
+
+  assert global_param is not None
+  assert global_param.param_location == "query"
+  assert global_param.type_value is str
+
+  assert local_param is not None
+  assert local_param.param_location == "header"
+  assert local_param.type_value is int
diff --git a/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py b/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py
index fdb6d201a..2b95e4614 100644
--- a/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py
+++ b/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py
@@ -95,7 +95,7 @@ def test_openapi_toolset_tool_existing(openapi_spec: Dict):
   assert tool.is_long_running is False
   assert tool.operation.operationId == "calendar.calendars.get"
   assert tool.operation.description == "Returns metadata for a calendar."
-  assert len(tool.operation.parameters) == 1
+  assert len(tool.operation.parameters) == 8
   assert tool.operation.parameters[0].name == "calendarId"
   assert tool.operation.parameters[0].in_ == ParameterInType.path
   assert tool.operation.parameters[0].required is True