Proxy class to generate client from OpenAPI schema.
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def __init__(self, servers: list[dict[str, Any]], title: str | None = None, **kwargs: Any) -> None:
"""Proxy class to generate client from OpenAPI schema."""
self._servers = servers
self._title = title or "MCP Proxy"
self._kwargs = kwargs
self._registered_funcs: list[Callable[..., Any]] = []
self._globals: dict[str, Any] = {}
self._security: dict[str, list[BaseSecurity]] = {}
self._security_params: dict[str | None, BaseSecurityParameters] = {}
self._tags: set[str] = set()
self._function_group: dict[str, list[str]] = {}
|
get_mcp
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def get_mcp(self, **settings: Any) -> "FastMCP":
mcp = FastMCP(name=self._title, **settings) # newer mcp
for func in self._registered_funcs:
try:
mcp.tool()(func)
except PydanticInvalidForJsonSchema as e:
logger.warning("Could not register function %s: %s", func.__name__, e)
return mcp
|
set_security_params
set_security_params(security_params, name=None)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def set_security_params(self, security_params: BaseSecurityParameters, name: str | None = None) -> None:
if name is not None:
security = self._security.get(name)
if security is None:
raise ValueError(f"Security is not set for '{name}'")
for match_security in security:
if match_security.accept(security_params):
break
else:
raise ValueError(f"Security parameters {security_params} do not match security {security}")
self._security_params[name] = security_params
|
put
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def put(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
return self._request("put", path, **kwargs)
|
get
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def get(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
return self._request("get", path, **kwargs)
|
post
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def post(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
return self._request("post", path, **kwargs)
|
delete
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def delete(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
return self._request("delete", path, **kwargs)
|
head
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def head(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
return self._request("head", path, **kwargs)
|
patch
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def patch(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
return self._request("patch", path, **kwargs)
|
generate_code classmethod
generate_code(input_text, output_dir, disable_timestamp=False, custom_visitors=None)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| @classmethod
@require_optional_import(["datamodel_code_generator", "fastapi_code_generator"], "mcp-proxy-gen")
def generate_code(
cls,
input_text: str,
output_dir: Path,
disable_timestamp: bool = False,
custom_visitors: list[Path] | None = None,
) -> str:
if custom_visitors is None:
custom_visitors = []
custom_visitors.append(Path(__file__).parent / "security_schema_visitor.py")
# with patch_get_parameter_type():
generate_code(
input_name="openapi.yaml",
input_text=input_text,
encoding="utf-8",
output_dir=output_dir,
template_dir=cls._get_template_dir() / "client_template",
disable_timestamp=disable_timestamp,
custom_visitors=custom_visitors,
output_model_type=DataModelType.PydanticV2BaseModel,
)
main_path = output_dir / "main.py"
with main_path.open("r") as f:
main_py_code = f.read()
# main_py_code = main_py_code.replace("from .models import", "from models import")
main_py_code = main_py_code.replace("from .models", "from models")
# Removing "from __future__ import annotations" to avoid ForwardRef issues, should be fixed in fastapi_code_generator
main_py_code = main_py_code.replace("from __future__ import annotations", "")
with main_path.open("w") as f:
f.write(main_py_code)
return main_path.stem
|
set_globals
set_globals(main, suffix)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def set_globals(self, main: ModuleType, suffix: str) -> None:
xs = {k: v for k, v in main.__dict__.items() if not k.startswith("__")}
self._globals = {
k: v for k, v in xs.items() if hasattr(v, "__module__") and v.__module__ in [f"models_{suffix}", "typing"]
}
|
create classmethod
create(*, openapi_specification=None, openapi_url=None, client_source_path=None, servers=None, rename_functions=False, group_functions=False, configuration_type='json')
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| @classmethod
@require_optional_import(["yaml"], "mcp-proxy-gen")
def create(
cls,
*,
openapi_specification: str | None = None,
openapi_url: str | None = None,
client_source_path: str | None = None,
servers: list[dict[str, Any]] | None = None,
rename_functions: bool = False,
group_functions: bool = False,
configuration_type: Literal["json", "yaml"] = "json",
) -> "MCPProxy":
if (openapi_specification is None) == (openapi_url is None):
raise ValueError("Either openapi_specification or openapi_url should be provided")
if openapi_specification is None and openapi_url is not None:
with requests.get(openapi_url, timeout=10) as response:
response.raise_for_status()
openapi_specification = response.text
openapi_parsed = (
json.loads(openapi_specification) if configuration_type == "json" else yaml.safe_load(openapi_specification)
) # type: ignore [arg-type]
if servers:
openapi_parsed["servers"] = servers
yaml_friendly = yaml.safe_dump(openapi_parsed)
with optional_temp_path(client_source_path) as td:
suffix = td.name # noqa F841
custom_visitors = []
if rename_functions:
custom_visitors.append(Path(__file__).parent / "operation_renaming.py")
if group_functions:
custom_visitors.append(Path(__file__).parent / "operation_grouping.py")
main_name = cls.generate_code( # noqa F841
input_text=yaml_friendly, # type: ignore [arg-type]
output_dir=td,
custom_visitors=custom_visitors,
)
# add td to sys.path
try:
sys.path.append(str(td))
main = importlib.import_module(main_name, package=td.name) # nosemgrep
finally:
sys.path.remove(str(td))
client: MCPProxy = main.app # type: ignore [attr-defined]
client.set_globals(main, suffix=suffix)
client.dump_configurations(output_dir=td)
return client
|
dump_configurations
dump_configurations(output_dir)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def dump_configurations(self, output_dir: Path) -> None:
for tag in self._function_group:
output_file = output_dir / f"mcp_config_{tag}.json"
functions = [
registered_function
for registered_function in self._registered_funcs
if registered_function.__name__ in self._function_group[tag]
]
self.dump_configuration(output_file, functions)
self.dump_configuration(output_dir / "mcp_config.json", self._registered_funcs)
|
dump_configuration
dump_configuration(output_file, functions=None)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def dump_configuration(self, output_file: Path, functions: list[Callable[..., Any]] = None) -> None:
# Define paths
template_dir = MCPProxy._get_template_dir() / "config_template"
template_file = "config.jinja2"
# Load Jinja environment
env = Environment(loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True)
# Load the template
template = env.get_template(template_file)
# Prepare context for rendering
context = {
"server_url": self._servers[0]["url"], # single or list depending on your structure
"authentications": self._get_authentications(), # list of auth blocks, we will also need to check _security_params
"operations": [
{
"name": op.__name__,
"description": op._description.replace("\n", " ").replace("\r", "").strip()
if op._description is not None
else "",
}
for op in functions
],
}
# Render the template
rendered_config = template.render(context)
# Save the output to a file
with open(output_file, "w") as f:
f.write(rendered_config)
|
load_configuration
load_configuration(config_file)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def load_configuration(self, config_file: str) -> None:
with Path(config_file).open("r") as f:
config_data_str = f.read()
self.load_configuration_from_string(config_data_str)
|
load_configuration_from_string
load_configuration_from_string(config_data_str)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def load_configuration_from_string(self, config_data_str: str) -> None:
config_data = json.loads(config_data_str)
# Load server URL
self._servers = [{"url": config_data["server"]["url"]}]
# Load authentication
for auth in config_data.get("authentication", []):
security = BaseSecurity.parse_security_parameters(auth)
self.set_security_params(security)
operation_names = [op["name"] for op in config_data.get("operations", [])]
self._registered_funcs = [func for func in self._registered_funcs if func.__name__ in operation_names]
|
get_functions
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def get_functions(self) -> list[str]:
raise DeprecationWarning("Use function_names property instead of get_functions method")
|
get_function
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def get_function(self, name: str) -> Callable[..., dict[str, Any]]:
for f in self._registered_funcs:
if f.__name__ == name:
return f
raise ValueError(f"Function {name} not found")
|
set_function
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def set_function(self, name: str, func: Callable[..., dict[str, Any]]) -> None:
for i, f in enumerate(self._registered_funcs):
if f.__name__ == name:
self._registered_funcs[i] = func
return
raise ValueError(f"Function {name} not found")
|
inject_parameters
inject_parameters(name, **kwargs)
Source code in autogen/mcp/mcp_proxy/mcp_proxy.py
| def inject_parameters(self, name: str, **kwargs: Any) -> None:
raise NotImplementedError("Injecting parameters is not implemented yet")
|