From a3bb75f3a618668cdc154a76778429f0b6bb7db4 Mon Sep 17 00:00:00 2001 From: Razvan Radulescu <43811028+h3xxit@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:48:22 +0200 Subject: [PATCH] fix(openapi): make unsupported-method warning reachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The convert() loop pre-filtered operations to SUPPORTED_HTTP_METHODS, so the validation guard in _create_tool could never run and its warning never fired — dead code. Split the method sets: the loop now filters on the full set of OpenAPI operation methods (so it still skips non-operation path-item keys like parameters/summary/$ref), and _create_tool remains the single place that validates against SUPPORTED_HTTP_METHODS, warning and skipping options/head/ trace operations. Strengthens the skip test to assert the warning is emitted. Bumps utcp-http 1.1.8 -> 1.1.9. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../communication_protocols/http/pyproject.toml | 2 +- .../http/src/utcp_http/openapi_converter.py | 14 ++++++++++---- .../http/tests/test_openapi_converter.py | 9 ++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/communication_protocols/http/pyproject.toml b/plugins/communication_protocols/http/pyproject.toml index 10bb447..ef2cdda 100644 --- a/plugins/communication_protocols/http/pyproject.toml +++ b/plugins/communication_protocols/http/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "utcp-http" -version = "1.1.8" +version = "1.1.9" authors = [ { name = "UTCP Contributors" }, ] diff --git a/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py b/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py index 0054b29..dc930f0 100644 --- a/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py +++ b/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py @@ -29,9 +29,15 @@ from utcp_http.http_call_template import HttpCallTemplate from utcp_http._security import ensure_secure_url, is_loopback_url -# HTTP methods that HttpCallTemplate.http_method accepts. Kept as the single -# source of truth for both the operation loop filter and per-operation -# validation so the two can never drift apart. +# All HTTP methods that OpenAPI defines as operation fields on a Path Item +# Object. The conversion loop uses this to tell operations apart from the other +# path-item keys (parameters, summary, $ref, servers, ...), so that genuinely +# unsupported operations still reach _create_tool and get a warning rather than +# being silently dropped by the loop. +OPENAPI_OPERATION_METHODS: Tuple[str, ...] = ("get", "put", "post", "delete", "options", "head", "patch", "trace") + +# The subset of HTTP methods that HttpCallTemplate.http_method accepts. +# _create_tool validates against this and skips anything else with a warning. SUPPORTED_HTTP_METHODS: Tuple[str, ...] = ("GET", "POST", "PUT", "DELETE", "PATCH") class OpenApiConverter: @@ -190,7 +196,7 @@ def convert(self) -> UtcpManual: for path, path_item in self.spec.get("paths", {}).items(): for method, operation in path_item.items(): - if method.upper() in SUPPORTED_HTTP_METHODS: + if method.lower() in OPENAPI_OPERATION_METHODS: tool = self._create_tool(path, method, operation, base_url) if tool: tools.append(tool) diff --git a/plugins/communication_protocols/http/tests/test_openapi_converter.py b/plugins/communication_protocols/http/tests/test_openapi_converter.py index 1cb3ac6..9cf7709 100644 --- a/plugins/communication_protocols/http/tests/test_openapi_converter.py +++ b/plugins/communication_protocols/http/tests/test_openapi_converter.py @@ -220,7 +220,7 @@ def test_openapi_converter_parameter_examples(): assert example_value["email"] == "jane@example.com" -def test_openapi_converter_skips_unsupported_methods(): +def test_openapi_converter_skips_unsupported_methods(capsys): """Operations with HTTP methods HttpCallTemplate cannot represent are skipped, not crashed on.""" openapi_spec = { "openapi": "3.0.0", @@ -254,6 +254,13 @@ def test_openapi_converter_skips_unsupported_methods(): tool_names = {tool.name for tool in manual.tools} assert tool_names == {"listThings"} + # Unsupported operations must reach _create_tool and emit a skip warning, + # not be silently dropped by the loop filter. + stderr = capsys.readouterr().err + assert "optionsThings" in stderr + assert "headThings" in stderr + assert "traceThings" in stderr + def test_openapi_converter_schema_level_examples_normalized(): """Examples declared at the schema level (not the media type) are normalized into 'examples'."""