Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/communication_protocols/http/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "utcp-http"
version = "1.1.9"
version = "1.1.10"
authors = [
{ name = "UTCP Contributors" },
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,20 +360,30 @@ def _resolve_ref_obj(self, obj: Any, visited: Optional[set] = None) -> Any:

def _extract_examples(self, obj: Dict[str, Any]) -> Optional[List[Any]]:
"""
Extract examples from an OpenAPI parameter or Media Type Object (Parameter, Media Type, Schema).

Supports both 'example' (single value) and 'examples' (map of Example Objects).
Extract examples from an OpenAPI Parameter, Media Type, or Schema object.

Handles all three shapes the spec allows:
- 'example' (single value) - OpenAPI Parameter / Media Type / 3.0 Schema.
- 'examples' as a map of named Example Objects - OpenAPI Parameter /
Media Type Object (each entry carries an inline 'value').
- 'examples' as a list of literal values - JSON Schema / OpenAPI 3.1
Schema Object.

Returns a list of example values suitable for JSON Schema 'examples' keyword.
"""
examples = []

# Handle single 'example' field
if "example" in obj and obj["example"] is not None:
examples.append(obj["example"])

# Handle 'examples' map (OpenAPI 3.0+)
if "examples" in obj and isinstance(obj["examples"], dict):
for example_obj in obj["examples"].values():

examples_obj = obj.get("examples")
if isinstance(examples_obj, list):
# JSON Schema / OpenAPI 3.1 Schema form: a plain list of example values.
examples.extend(examples_obj)
elif isinstance(examples_obj, dict):
# OpenAPI 3.0 form: a map of named Example Objects.
for example_obj in examples_obj.values():
if isinstance(example_obj, dict) and "$ref" in example_obj:
example_obj = self._resolve_ref_obj(example_obj, set()) or {}
if isinstance(example_obj, dict):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,57 @@ def test_openapi_converter_schema_level_examples_normalized():
assert "example" not in body_param.model_dump(by_alias=True)

assert tool.outputs.examples == [{"id": "w_1"}]


def test_openapi_converter_array_form_schema_examples():
"""Array-form (JSON Schema / OpenAPI 3.1) schema 'examples' are preserved, not dropped."""
openapi_spec = {
"openapi": "3.1.0",
"info": {"title": "Test API", "version": "1.0.0"},
"paths": {
"/gadgets": {
"post": {
"operationId": "createGadget",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {"name": {"type": "string"}},
# JSON Schema 'examples' keyword: a list of values
"examples": [{"name": "Gadget A"}, {"name": "Gadget B"}],
}
}
}
},
"responses": {
"200": {
"description": "ok",
"content": {
"application/json": {
"schema": {
"type": "string",
"examples": ["ok", "done"],
}
}
},
}
},
}
}
},
}

converter = OpenApiConverter(openapi_spec)
manual = converter.convert()

tool = next((t for t in manual.tools if t.name == "createGadget"), None)
assert tool is not None

body_param = tool.inputs.properties.get("body")
assert body_param is not None
assert body_param.examples == [{"name": "Gadget A"}, {"name": "Gadget B"}]
# examples surface in the normalized field on serialization
assert body_param.model_dump(by_alias=True).get("examples") == [{"name": "Gadget A"}, {"name": "Gadget B"}]

assert tool.outputs.examples == ["ok", "done"]
Loading