""" MCP Generator - Gradio Frontend Turn any API into an MCP server in seconds! """ import asyncio import gradio as gr from pathlib import Path import zipfile import io import os from src.agents.factory import AgentFactory from src.mcp_host import mcp_host from src.mcp_registry import mcp_registry from src.config import LLM_PROVIDER, ANTHROPIC_API_KEY, OPENAI_API_KEY, HOSTED_MCPS_DIR # Initialize agent factory try: agent_factory = AgentFactory() print(f"✅ Using {LLM_PROVIDER.upper()} for code generation") except ValueError as e: print(f"❌ Error: {e}") print(f"Please set {'ANTHROPIC_API_KEY' if LLM_PROVIDER == 'anthropic' else 'OPENAI_API_KEY'} in .env file") agent_factory = None async def generate_and_host_mcp(api_url: str, api_key: str = None, force_regenerate: bool = False, progress=gr.Progress()): """Generate and host an MCP server Args: api_url: The API URL to analyze api_key: Optional API key for the target API force_regenerate: If True, regenerate even if exists progress: Gradio progress tracker Returns: Tuple of (status_text, code, download_file, readme, connection_config) """ if not agent_factory: api_key_name = "ANTHROPIC_API_KEY" if LLM_PROVIDER == "anthropic" else "OPENAI_API_KEY" return ( f"❌ Error: {api_key_name} not configured. Please set it in your .env file.", "", None, "", "" ) try: # Check if MCP already exists for this URL existing_mcp = mcp_registry.find_by_url(api_url) if existing_mcp and not force_regenerate: progress(0.5, desc="Found existing MCP, reusing...") # Reuse existing MCP mcp_id = existing_mcp['mcp_id'] mcp_path = HOSTED_MCPS_DIR / mcp_id # Update last used timestamp mcp_registry.update_last_used(api_url) # Load existing files server_code = (mcp_path / "server.py").read_text() readme = (mcp_path / "README.md").read_text() status_text = f"""♻️ **Reusing Existing MCP!** **MCP ID:** `{mcp_id}` **Originally Created:** {existing_mcp['created_at']} **Last Used:** {existing_mcp['last_used']} This MCP was already generated for this API URL. Using existing version to save time and API calls! 💡 **Tip:** To regenerate from scratch, check "Force Regenerate" below. """ # Create ZIP file zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf: for file_path in mcp_path.rglob('*'): if file_path.is_file(): arcname = file_path.relative_to(mcp_path.parent) zipf.write(file_path, arcname) zip_buffer.seek(0) temp_zip = f"/tmp/{mcp_id}.zip" with open(temp_zip, 'wb') as f: f.write(zip_buffer.read()) connection_config = f"""{{ "mcpServers": {{ "{mcp_id}": {{ "command": "python", "args": ["server.py"] }} }} }}""" return ( status_text, server_code, temp_zip, readme, connection_config ) # Generate new MCP if existing_mcp: progress(0.1, desc="Regenerating MCP (forced)...") else: progress(0.1, desc="Analyzing API...") # Generate the MCP result = await agent_factory.generate_mcp(api_url, api_key) if result["status"] == "error": return ( f"❌ Error: {result['error']}", "", None, "", "" ) progress(0.6, desc="Generating code...") # Get the generated files mcp_id = result["mcp_id"] server_code = result["server_code"] readme = result["readme_content"] progress(0.8, desc="Starting MCP server...") # Start the MCP server start_result = await mcp_host.start_mcp(mcp_id) if not start_result["success"]: status_text = f"⚠️ MCP generated but failed to start: {start_result.get('error')}" else: status_text = f"""✅ **MCP Server Running!** **MCP ID:** `{mcp_id}` **Status:** {start_result['status']} **Connection:** stdio (local) Your MCP server is generated and ready to use! """ progress(0.9, desc="Creating download package...") # Create ZIP file for download zip_buffer = io.BytesIO() mcp_path = Path(result["download_path"]) with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf: for file_path in mcp_path.rglob('*'): if file_path.is_file(): arcname = file_path.relative_to(mcp_path.parent) zipf.write(file_path, arcname) zip_buffer.seek(0) # Save to temp file for Gradio temp_zip = f"/tmp/{mcp_id}.zip" with open(temp_zip, 'wb') as f: f.write(zip_buffer.read()) # Create connection config connection_config = f"""{{ "mcpServers": {{ "{mcp_id}": {{ "command": "python", "args": ["server.py"] }} }} }}""" progress(1.0, desc="Done!") return ( status_text, server_code, temp_zip, readme, connection_config ) except Exception as e: return ( f"❌ Unexpected error: {str(e)}", "", None, "", "" ) # Build Gradio Interface with gr.Blocks( title="MCP Generator" ) as app: gr.Markdown(""" # 🤖 MCP Generator ## Turn Any API into an MCP Server in Seconds! Simply enter an API URL and we'll generate a complete, working MCP server with: - ✅ Automatically analyzed endpoints - ✅ Generated MCP tools - ✅ Complete documentation - ✅ Ready to use immediately! **Built for the MCP 1st Birthday Hackathon** 🎉 """) with gr.Accordion("ℹ️ API URL Guidelines & Tips", open=False): gr.Markdown(""" ### ✅ What Works Best: - **Base API URLs** (e.g., `https://api.example.com`) - **REST APIs** with clear endpoints - **OpenAPI/Swagger** documented APIs - **Public APIs** (no complex auth) ### 🎯 Try These Free APIs: **No Auth Required (Perfect for Testing!):** - `https://jsonplaceholder.typicode.com` - Fake REST API (best for testing!) - `https://api.github.com` - GitHub public API - `https://dog.ceo/api` - Random dog images - `https://catfact.ninja` - Random cat facts - `https://api.coindesk.com/v1/bpi` - Bitcoin prices - `https://api.ipify.org` - Get IP address **With API Key (Free Tier):** - `https://api.openweathermap.org/data/2.5` - Weather data - `https://newsapi.org/v2` - News articles - `https://api.stripe.com` - Payment processing (test mode) ### 💡 Tips: - **Start with jsonplaceholder.typicode.com** - always works! - Paste the **base URL** (not a specific endpoint) - If API needs a key, add it in the "API Key" field below - Cached URLs generate instantly (try the same URL twice!) ### ⚠️ May Not Work Well: - GraphQL APIs (REST only for now) - APIs requiring OAuth flows - WebSocket-only APIs - APIs with very complex authentication """) with gr.Row(): with gr.Column(scale=2): gr.Markdown("### 📝 Input") api_url = gr.Textbox( label="API URL or Documentation URL", placeholder="https://api.example.com", info="Enter the base URL or documentation URL of the API" ) api_key = gr.Textbox( label="API Key (Optional)", placeholder="sk-...", type="password", info="If the API requires authentication" ) force_regenerate = gr.Checkbox( label="Force Regenerate", value=False, info="Regenerate even if MCP already exists for this URL (saves API calls when unchecked)" ) generate_btn = gr.Button( "🚀 Generate & Host MCP Server", variant="primary", size="lg" ) with gr.Accordion("🚀 Quick Start Examples", open=True): gr.Markdown(""" **Click to copy and paste:** ``` https://jsonplaceholder.typicode.com ``` ⭐ **Recommended first try!** Always works, no API key needed. --- **More examples:** - `https://api.github.com` - GitHub API (no auth) - `https://dog.ceo/api` - Dog images (fun!) - `https://catfact.ninja` - Cat facts (simple) 💡 **Tip:** MCPs are cached - try the same URL twice to see instant results! """) gr.Markdown("---") with gr.Row(): with gr.Column(): gr.Markdown("### 📊 Results") status_output = gr.Markdown(label="Status") with gr.Tab("Generated Code"): code_output = gr.Code( label="server.py", language="python", lines=20 ) with gr.Tab("README"): readme_output = gr.Markdown() with gr.Tab("Connection Config"): connection_output = gr.Code( label="Claude Desktop Config", language="json" ) download_output = gr.File( label="📦 Download Complete Package (ZIP)" ) # Wire up the button generate_btn.click( fn=generate_and_host_mcp, inputs=[api_url, api_key, force_regenerate], outputs=[ status_output, code_output, download_output, readme_output, connection_output ] ) with gr.Accordion("📋 Previously Generated MCPs", open=False): def get_existing_mcps(): """Get list of existing MCPs for display""" mcps = mcp_registry.list_all() if not mcps: return "No MCPs generated yet. Generate your first one above! 👆" output = "| API Name | URL | Created | Last Used |\n" output += "|----------|-----|---------|----------|\n" for mcp in mcps[:10]: # Show last 10 api_name = mcp['api_name'] api_url = mcp['api_url'][:40] + "..." if len(mcp['api_url']) > 40 else mcp['api_url'] created = mcp['created_at'].split('T')[0] last_used = mcp['last_used'].split('T')[0] output += f"| {api_name} | {api_url} | {created} | {last_used} |\n" return output existing_mcps_display = gr.Markdown(get_existing_mcps()) refresh_btn = gr.Button("🔄 Refresh List", size="sm") refresh_btn.click(fn=get_existing_mcps, outputs=existing_mcps_display) gr.Markdown(""" --- ### 🎯 How to Use Your Generated MCP 1. **Download** the ZIP file above 2. **Extract** it to a folder 3. **Add** the connection config to your Claude Desktop settings 4. **Restart** Claude Desktop Your MCP server is ready to use! 🎉 ### 🚀 About This Project This is a meta-MCP: an MCP server that generates other MCP servers! - Built with [Gradio](https://gradio.app) - Powered by [LangGraph](https://github.com/langchain-ai/langgraph) agents - Uses [Anthropic's Claude](https://anthropic.com) for code generation - Integrates with [MCP Fetch Server](https://github.com/modelcontextprotocol/servers) **For MCP 1st Birthday Hackathon - Track 2: MCP in Action** 🎂 """) if __name__ == "__main__": # Check for API key if not ANTHROPIC_API_KEY: print("⚠️ WARNING: ANTHROPIC_API_KEY not set!") print("Please create a .env file with your API key") print("Example: echo 'ANTHROPIC_API_KEY=your_key_here' > .env") app.launch( server_name="0.0.0.0", server_port=7860, share=False )