refactor: extract http request helper for mcp openapi tools

This commit is contained in:
Yuta Saito
2026-01-03 09:07:59 +09:00
parent 6168e500a8
commit 101fa6bdc6
@@ -42,6 +42,80 @@ def _sanitize_path_parameter_value(param_value: Any, param_name: str) -> str:
return quote(value_str, safe="")
async def _invoke_openapi_operation(
*,
method: str,
base_url: str,
path: str,
headers: Dict[str, str],
path_params: list,
query_params: list,
body_params: list,
provided_params: Dict[str, Any],
) -> str:
"""Execute the OpenAPI operation using provided parameters."""
url = base_url + path
# Replace path parameters with sanitized values
for param_name in path_params:
param_value = provided_params.get(param_name, "")
if param_value:
try:
safe_value = _sanitize_path_parameter_value(param_value, param_name)
except ValueError as exc:
return "Invalid path parameter: " + str(exc)
url = url.replace("{" + param_name + "}", safe_value)
# Build query params
params: Dict[str, Any] = {}
for param_name in query_params:
param_value = provided_params.get(param_name, "")
if param_value:
params[param_name] = param_value
# Build request body
json_body = None
if body_params:
body_value = provided_params.get("body", {})
if isinstance(body_value, dict):
json_body = body_value
elif body_value:
import json as json_module
try:
json_body = (
json_module.loads(body_value)
if isinstance(body_value, str)
else {"data": body_value}
)
except Exception:
json_body = {"data": body_value}
async with httpx.AsyncClient() as client:
method_lower = method.lower()
if method_lower == "get":
response = await client.get(url, params=params, headers=headers)
elif method_lower == "post":
response = await client.post(
url, params=params, json=json_body, headers=headers
)
elif method_lower == "put":
response = await client.put(
url, params=params, json=json_body, headers=headers
)
elif method_lower == "delete":
response = await client.delete(url, params=params, headers=headers)
elif method_lower == "patch":
response = await client.patch(
url, params=params, json=json_body, headers=headers
)
else:
return f"Unsupported HTTP method: {method}"
return response.text
def load_openapi_spec(filepath: str) -> Dict[str, Any]:
"""Load OpenAPI specification from JSON file."""
with open(filepath, "r") as f:
@@ -160,59 +234,19 @@ def create_tool_function(
func_code = f'''
async def tool_function({params_str}) -> str:
"""Dynamically generated tool function."""
url = base_url + path
# Replace path parameters
path_param_names = {path_params}
for param_name in path_param_names:
param_value = locals().get(param_name, "")
if param_value:
# url = url.replace("{{" + param_name + "}}", str(param_value))
try:
safe_value = _sanitize_path_parameter_value(param_value, param_name)
except ValueError as exc:
return "Invalid path parameter: " + str(exc)
url = url.replace("{{" + param_name + "}}", safe_value)
# Build query params
query_param_names = {query_params}
params = {{}}
for param_name in query_param_names:
param_value = locals().get(param_name, "")
if param_value:
params[param_name] = param_value
# Build request body
body_param_names = {body_params}
json_body = None
if body_param_names:
body_value = locals().get("body", {{}})
if isinstance(body_value, dict):
json_body = body_value
elif body_value:
# If it's a string, try to parse as JSON
import json as json_module
try:
json_body = json_module.loads(body_value) if isinstance(body_value, str) else {{"data": body_value}}
except:
json_body = {{"data": body_value}}
# Make HTTP request
async with httpx.AsyncClient() as client:
if "{method.lower()}" == "get":
response = await client.get(url, params=params, headers=headers)
elif "{method.lower()}" == "post":
response = await client.post(url, params=params, json=json_body, headers=headers)
elif "{method.lower()}" == "put":
response = await client.put(url, params=params, json=json_body, headers=headers)
elif "{method.lower()}" == "delete":
response = await client.delete(url, params=params, headers=headers)
elif "{method.lower()}" == "patch":
response = await client.patch(url, params=params, json=json_body, headers=headers)
else:
return "Unsupported HTTP method: {method}"
return response.text
provided_params = {{}}
for param_name in {all_params}:
provided_params[param_name] = locals().get(param_name, "")
return await _invoke_openapi_operation(
method="{method}",
base_url=base_url,
path=path,
headers=headers,
path_params={path_params},
query_params={query_params},
body_params={body_params},
provided_params=provided_params,
)
'''
# Execute the function code to create the actual function
@@ -222,7 +256,7 @@ async def tool_function({params_str}) -> str:
"base_url": base_url,
"path": path,
"method": method,
"_sanitize_path_parameter_value": _sanitize_path_parameter_value,
"_invoke_openapi_operation": _invoke_openapi_operation,
}
exec(func_code, local_vars)