fix(mcp): resolve \$ref params in OpenAPI preview endpoint (test/tools/list)

The _preview_openapi_tools function (called by the UI add-server form to show
connection status and available tools) had the same bug as _register_openapi_tools:
it accessed param["name"] directly without resolving \$ref parameters or merging
path-level parameters from the path item.

This caused "Failed to load OpenAPI spec: 'name'" for any spec that uses
component-level parameter references (e.g. GitHub's official REST API spec).

Apply the same fix: resolve \$ref against components/parameters and merge
path-level params (with operation-level taking priority) before building schemas.
This commit is contained in:
Ishaan Jaffer 2026-03-05 18:55:09 -08:00
parent 50b24774b4
commit af45006111

View file

@ -671,16 +671,35 @@ if MCP_AVAILABLE:
try:
spec = await load_openapi_spec_async(spec_path)
paths = spec.get("paths", {})
components = spec.get("components", {})
tools: List[dict] = []
for path, path_item in paths.items():
for method in ("get", "post", "put", "patch", "delete"):
operation = path_item.get(method)
if operation is None:
continue
# Resolve $ref params and merge path-level params (same logic as
# _register_openapi_tools) so large specs like GitHub's work correctly.
def _resolve_ref(p: dict) -> dict:
ref = p.get("$ref", "")
if ref.startswith("#/components/parameters/"):
param_name = ref.split("/")[-1]
return components.get("parameters", {}).get(param_name, p)
return p
path_level = [_resolve_ref(p) for p in path_item.get("parameters", [])]
op_level = [_resolve_ref(p) for p in operation.get("parameters", [])]
op_keys = {(p.get("name"), p.get("in")) for p in op_level}
merged = [p for p in path_level if (p.get("name"), p.get("in")) not in op_keys] + op_level
resolved_op = dict(operation)
resolved_op["parameters"] = merged
op_id = operation.get("operationId", f"{method}_{path}")
summary = operation.get("summary", "")
description = operation.get("description", summary)
input_schema = build_input_schema(operation)
input_schema = build_input_schema(resolved_op)
tools.append(
{
"name": op_id,