mcp-generator / app.py
visproj's picture
Update app.py
14d54ee verified
raw
history blame
12.6 kB
"""
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
)