veten/OpenAPI2MCP
0
1import gradio as gr2import httpx3import json4from huggingface_hub import HfApi5import random6import re7 8def find_endpoints(openapi_spec_url, api_base_url, paths, methods):9 print(f"Finding endpoints for {openapi_spec_url} with methods {methods}")10 if openapi_spec_url.startswith(("http://", "https://")):11 response = httpx.get(openapi_spec_url)12 response.raise_for_status()13 content = response.text14 else:15 raise gr.Error("Invalid URL for OpenAPI spec")16 17 try:18 spec = json.loads(content)19 except json.JSONDecodeError as e:20 raise gr.Error("Invalid JSON for OpenAPI spec")21 22 api_paths = spec.get("paths", {})23 if not api_paths:24 raise gr.Error("No valid paths found in the OpenAPI specification")25 26 valid_api_paths = []27 for path, path_item in api_paths.items():28 for method, operation in path_item.items():29 if methods and method.lower() not in [m.lower() for m in methods]:30 continue31 if not paths:32 valid_api_paths.append({33 "path": path,34 "method": method.upper(),35 })36 else:37 for path_regex in paths.split(","):38 if re.match(path_regex, path):39 valid_api_paths.append({40 "path": path,41 "method": method.upper(),42 })43 break44 45 return gr.JSON(valid_api_paths, label=f"๐ {len(valid_api_paths)} endpoints found")46 47def update_bottom(oauth_token: gr.OAuthToken | None):48 if oauth_token:49 return "Click the ๐ Create button to create a new MCP Space", gr.Button(interactive=True)50 else:51 return gr.skip()52 53gradio_app_code = """54import os55import gradio as gr56 57gr.load_openapi(58 openapi_spec=\"{}\",59 base_url=\"{}\",60 paths={},61 methods={},62 auth_token=os.getenv("OPENAPI_AUTH_TOKEN")63).launch(mcp_server=True)64"""65 66readme_code = """67---68title: OpenAPI MCP Server69sdk: gradio70sdk_version: 5.38.271app_file: app.py72pinned: false73tags:74 - openapi2mcp75---76 77Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference78"""79 80requirements_code = """81https://gradio-pypi-previews.s3.amazonaws.com/86331cf36187d17a6d3ce26b01f28ea8dd5cbe35/gradio-5.38.2-py3-none-any.whl82"""83 84def create_hf_space(token, space_name, space_public, app_code, auth_token):85 """86 Create a new Hugging Face Space with optional app.py file87 88 Args:89 token (str): Your Hugging Face API token90 space_name (str): The name of the space to create91 space_public (bool): Whether the space should be public92 app_code (str): String content for the app.py file93 auth_token (str | None): The auth token to include in the API requests94 95 Returns:96 SpaceInfo: Information about the created space97 """98 99 api = HfApi(token=token)100 user_info = api.whoami()101 username = user_info["name"]102 space_name = space_name or f"my-mcp-space-{random.randint(100000, 999999)}" 103 space_id = f"{username}/{space_name}"104 105 try:106 gr.Info(f"Creating space {space_id}...", duration=20)107 space_info = api.create_repo(108 repo_id=space_id,109 repo_type="space",110 private=not space_public,111 space_sdk="gradio"112 )113 api.upload_file(114 path_or_fileobj=app_code.encode('utf-8'),115 path_in_repo="app.py",116 repo_id=space_id,117 repo_type="space",118 commit_message="Add app.py"119 )120 api.upload_file(121 path_or_fileobj=readme_code.encode('utf-8'),122 path_in_repo="README.md",123 repo_id=space_id,124 repo_type="space",125 commit_message="Add README.md"126 )127 api.upload_file(128 path_or_fileobj=requirements_code.encode('utf-8'),129 path_in_repo="requirements.txt",130 repo_id=space_id,131 repo_type="space",132 commit_message="Add requirements.txt"133 )134 if auth_token:135 api.add_space_secret(136 repo_id=space_id,137 key="OPENAPI_AUTH_TOKEN",138 value=auth_token139 )140 141 space_url = f"https://huggingface.co/spaces/{space_id}"142 gr.Success(f"๐ Your space will be available at: <a href='{space_url}' target='_blank'>{space_url} โคด</a>", duration=None) 143 return space_info144 145 except Exception as e:146 gr.Error(f"โ Error creating space: {str(e)}")147 148 149def launch_mcp_server(openapi_spec_url, api_base_url, paths, methods, auth_token, space_name_box, space_public_box, oauth_token: gr.OAuthToken | None):150 if oauth_token:151 if not paths:152 paths = None153 else:154 paths = f"[\"{paths}\"]"155 create_hf_space(156 oauth_token.token, 157 space_name_box,158 space_public_box,159 gradio_app_code.format(160 openapi_spec_url,161 api_base_url,162 paths,163 methods,164 ),165 auth_token166 )167 else:168 pass169 170with gr.Blocks(theme="ocean") as demo:171 gr.Markdown("## OpenAPI โช MCP")172 gr.Markdown("""173 This is a tool that converts an OpenAPI spec to a MCP server that you can launch as a Space and then use with any MCP Client (e.g. ChatGPT, Claude, Cursor, Cline).174 """)175 with gr.Row():176 with gr.Column():177 openapi_spec_url = gr.Textbox(label="OpenAPI Spec URL", value="https://petstore3.swagger.io/api/v3/openapi.json")178 api_base_url = gr.Textbox(label="API Base URL", value="https://petstore3.swagger.io/api/v3/")179 methods = gr.CheckboxGroup(label="Methods", choices=["GET", "POST", "PUT", "DELETE"], value=["GET", "POST", "PUT", "DELETE"])180 with gr.Accordion("Optional Settings", open=False):181 paths = gr.Textbox(label="Regex to filter paths by", placeholder=".*user.*", info="Only include API endpoints that match this regex")182 auth_token = gr.Textbox(label="Auth token to include in API requests", info="This will be sent to the API endpoints as a Bearer token", type="password")183 find_endpoints_button = gr.Button("๐ Find Endpoints")184 with gr.Column():185 endpoints = gr.JSON(label="๐ endpoints found", value=[], max_height=400)186 message = gr.Markdown("_Note:_ you must be signed in through your Hugging Face account to create the MCP Space")187 space_name_box = gr.Textbox(show_label=False, placeholder="Optional space name (e.g. my-mcp-space)")188 space_public_box = gr.Checkbox(label="Make Space public", value=True)189 with gr.Row():190 login_button = gr.LoginButton()191 launch_button = gr.Button("๐ Create MCP Space", variant="primary", interactive=False)192 193 gr.on(194 [demo.load, find_endpoints_button.click],195 find_endpoints,196 inputs=[openapi_spec_url, api_base_url, paths, methods],197 outputs=endpoints,198 )199 200 gr.on(201 [demo.load],202 update_bottom,203 inputs=None,204 outputs=[message, launch_button]205 )206 207 gr.on(208 [launch_button.click],209 launch_mcp_server,210 inputs=[openapi_spec_url, api_base_url, paths, methods, auth_token, space_name_box, space_public_box],211 outputs=None212 )213 214demo.launch()