Skip to content

Commit 486e1fc

Browse files
authored
fix(mcp): business agent and catalog tools take their path ids (#54)
The MCP generator kept only PageParam among referenced parameters and dropped the rest, so the business agent and product catalog tools had no account, catalog, connector, tool or product id and could not be called. Both generators now share one resolver, and the tools are regenerated. Feedback: feedback_6ad87b06bf2c6d56c1f4fd7c
1 parent ecc55cb commit 486e1fc

5 files changed

Lines changed: 633 additions & 189 deletions

File tree

‎scripts/generate_mcp_tools.py‎

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from typing import Any
2222

2323
import yaml
24-
from openapi_body import flatten_request_body_schema
24+
from openapi_body import flatten_request_body_schema, resolve_parameter_ref
2525

2626
# Map OpenAPI tags to SDK resource names
2727
TAG_TO_RESOURCE: dict[str, str] = {
@@ -372,7 +372,8 @@ def extract_parameters(
372372
"""Extract parameters from operation.
373373
374374
`spec` is threaded through so `get_python_type` can dereference
375-
`$ref`/`allOf` schemas to read the underlying type.
375+
`$ref`/`allOf` schemas to read the underlying type. A `$ref` parameter
376+
without `spec` raises ValueError.
376377
377378
Dedupes by Python name: when the same parameter appears in both the path
378379
and the request body (e.g. `accountId` as a path param and also in the
@@ -390,27 +391,7 @@ def add_param(entry: dict[str, Any]) -> None:
390391
params.append(entry)
391392

392393
for param in operation.get("parameters", []):
393-
if "$ref" in param:
394-
ref = param["$ref"]
395-
if "PageParam" in ref:
396-
add_param({
397-
"name": "page",
398-
"type": "int",
399-
"required": False,
400-
"default": "1",
401-
"description": "Page number",
402-
"sdk_name": "page",
403-
})
404-
elif "LimitParam" in ref:
405-
add_param({
406-
"name": "limit",
407-
"type": "int",
408-
"required": False,
409-
"default": "10",
410-
"description": "Results per page",
411-
"sdk_name": "limit",
412-
})
413-
continue
394+
param = resolve_parameter_ref(param, spec)
414395

415396
if "name" not in param:
416397
continue

‎scripts/generate_resources.py‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from typing import Any
2323

2424
import yaml
25-
from openapi_body import flatten_request_body_schema
25+
from openapi_body import flatten_request_body_schema, resolve_parameter_ref
2626

2727
# Map OpenAPI tags to resource class names
2828
TAG_TO_RESOURCE: dict[str, str] = {
@@ -136,17 +136,7 @@ def extract_parameters(
136136

137137
# Query/path parameters
138138
for param in operation.get("parameters", []):
139-
# Resolve reusable parameters before extracting their name and location.
140-
# Silently dropping a path parameter produces broken URL templates.
141-
seen_refs: set[str] = set()
142-
while "$ref" in param:
143-
ref = param["$ref"]
144-
if not ref.startswith("#/") or ref in seen_refs:
145-
raise ValueError(f"Unsupported or cyclic parameter reference: {ref}")
146-
seen_refs.add(ref)
147-
param = spec
148-
for part in ref[2:].split("/"):
149-
param = param[part.replace("~1", "/").replace("~0", "~")]
139+
param = resolve_parameter_ref(param, spec)
150140

151141
if "name" not in param:
152142
continue

‎scripts/openapi_body.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ def resolve_local_ref(ref: str, spec: dict[str, Any]) -> dict[str, Any]:
1414
return node if isinstance(node, dict) else {}
1515

1616

17+
def resolve_parameter_ref(
18+
param: dict[str, Any], spec: dict[str, Any] | None
19+
) -> dict[str, Any]:
20+
# Resolve reusable parameters before extracting their name and location.
21+
# Silently dropping a path parameter produces broken URL templates.
22+
seen_refs: set[str] = set()
23+
while "$ref" in param:
24+
ref = param["$ref"]
25+
if spec is None or not ref.startswith("#/") or ref in seen_refs:
26+
raise ValueError(f"Unsupported or cyclic parameter reference: {ref}")
27+
seen_refs.add(ref)
28+
param = spec
29+
for part in ref[2:].split("/"):
30+
param = param[part.replace("~1", "/").replace("~0", "~")]
31+
return param
32+
33+
1734
def flatten_request_body_schema(
1835
schema: dict[str, Any], spec: dict[str, Any]
1936
) -> dict[str, Any] | None:

0 commit comments

Comments
 (0)