InstantMCP / ui_components /code_editor.py
areeb1501
add design changes
2201434
"""
Code Editor UI Component
Interactive code editor for MCP tool development.
FIXES APPLIED:
- Added try/except error handling in load_code()
- Using .get() with defaults for ALL fields to prevent KeyError
- Better error messages and fallbacks
- Handles missing url, description, and other fields gracefully
"""
import gradio as gr
from mcp_tools.deployment_tools import (
get_deployment_code,
list_deployments,
update_deployment_code,
)
def create_code_editor():
"""
Create the code editor UI component.
Returns:
gr.Blocks: Code editor interface
"""
with gr.Blocks() as editor:
gr.Markdown("## 💻 Code Editor")
gr.Markdown("Edit and view your deployment code inline")
# Main 4-column layout
with gr.Row():
# Column 1: Deployment Selection & Info
with gr.Column(scale=1):
gr.Markdown("### 📂 Select Deployment")
deployment_selector = gr.Dropdown(
label="Deployment",
choices=[],
interactive=True
)
with gr.Row():
load_btn = gr.Button("📥 Load", size="sm", scale=2)
refresh_deployments_btn = gr.Button("🔄", size="sm", scale=1)
deployment_info = gr.Markdown("*Select a deployment to view details*")
# Column 2: Code Editor
with gr.Column(scale=2):
gr.Markdown("### 📝 MCP Tools Code")
code_editor = gr.Code(
language="python",
label="",
lines=18,
interactive=True,
value="# Load a deployment to view and edit code"
)
# Action Buttons
with gr.Row():
save_btn = gr.Button("💾 Save & Redeploy", variant="primary", interactive=True, scale=2)
preview_btn = gr.Button("👁️ Preview", variant="secondary", scale=1)
deploy_btn = gr.Button("🚀 Deploy as New (Coming Soon)", interactive=False, scale=1)
# Column 3: Packages & Tools Preview
with gr.Column(scale=1):
gr.Markdown("### 📦 Packages & Tools")
packages_display = gr.Textbox(
label="Current Packages",
interactive=True,
placeholder="No deployment loaded",
lines=2
)
tools_preview = gr.JSON(
label="📋 Detected Tools",
value={}
)
# Output/Result Display
output = gr.JSON(label="Result")
# Functions
def load_deployment_list():
"""Load list of deployments for dropdown"""
try:
result = list_deployments()
if result.get("success"):
choices = [
(dep.get("server_name", "Unknown"), dep.get("deployment_id", ""))
for dep in result.get("deployments", [])
if dep.get("deployment_id") # Only include if we have an ID
]
return gr.update(choices=choices)
return gr.update(choices=[])
except Exception as e:
print(f"Error loading deployment list: {e}")
return gr.update(choices=[])
def load_code(deployment_id):
"""
Load code for selected deployment.
✅ FIXED: Now uses .get() with defaults for ALL fields
✅ FIXED: Wrapped in try/except for error handling
"""
# Handle empty selection
if not deployment_id:
return (
"# Select a deployment first",
"*No deployment selected*",
"",
{},
{"success": False, "error": "No deployment selected"}
)
try:
result = get_deployment_code(deployment_id)
if result.get("success"):
# ✅ FIX: Use .get() with defaults for ALL fields to prevent KeyError
deployment_id_val = result.get('deployment_id', 'N/A')
server_name = result.get('server_name', 'Unknown')
description = result.get('description', 'No description')
url = result.get('url', '')
mcp_endpoint = result.get('mcp_endpoint', '')
status = result.get('status', 'unknown')
category = result.get('category', 'Uncategorized')
author = result.get('author', 'Anonymous')
version = result.get('version', '1.0.0')
tools = result.get('tools', [])
packages = result.get('packages', [])
code = result.get('code', '# No code available')
created_at = result.get('created_at', 'Unknown')
# Format deployment info markdown
# ✅ FIX: Handle empty/None URL gracefully
if url:
url_display = f"[{url}]({url})"
else:
url_display = "*Not available*"
if mcp_endpoint:
mcp_display = f"`{mcp_endpoint}`"
else:
mcp_display = "*Not available*"
info_md = f"""
### 📋 Deployment Details
| Field | Value |
|-------|-------|
| **Deployment ID** | `{deployment_id_val}` |
| **Server Name** | {server_name} |
| **Description** | {description or 'N/A'} |
| **Status** | {status} |
| **Category** | {category} |
| **Author** | {author} |
| **Version** | {version} |
| **Created** | {created_at} |
| **Tools Count** | {len(tools)} detected |
**🔗 URL:** {url_display}
**📡 MCP Endpoint:** {mcp_display}
"""
# Format packages string
packages_str = ", ".join(packages) if packages else "No extra packages"
return (
code if code else "# No code available",
info_md,
packages_str,
tools if tools else [],
result
)
else:
# Handle API error response
error_msg = result.get('error', 'Unknown error occurred')
return (
f"# Error loading code\n# {error_msg}",
f"### ❌ Error\n\n{error_msg}",
"",
{},
result
)
except Exception as e:
# ✅ FIX: Catch any unexpected exceptions
error_msg = str(e)
return (
f"# Exception occurred while loading code\n# {error_msg}",
f"### ❌ Exception\n\n```\n{error_msg}\n```\n\nPlease try refreshing the deployment list.",
"",
{},
{"success": False, "error": error_msg, "exception": True}
)
def preview_code(code):
"""Preview code analysis"""
if not code or code.startswith("#"):
return {"info": "Enter code to preview"}
try:
# Basic code analysis
lines = code.split('\n')
tool_count = sum(1 for line in lines if '@mcp.tool()' in line)
import_count = sum(1 for line in lines if line.strip().startswith(('import ', 'from ')))
# Check for required components
has_fastmcp_import = 'from fastmcp import FastMCP' in code or 'import FastMCP' in code
has_mcp_instance = 'mcp = FastMCP(' in code
has_tools = tool_count > 0
# Validation status
is_valid = has_fastmcp_import and has_mcp_instance and has_tools
validation_issues = []
if not has_fastmcp_import:
validation_issues.append("Missing: from fastmcp import FastMCP")
if not has_mcp_instance:
validation_issues.append("Missing: mcp = FastMCP('server-name')")
if not has_tools:
validation_issues.append("Missing: @mcp.tool() decorated functions")
return {
"total_lines": len(lines),
"detected_tools": tool_count,
"imports": import_count,
"is_valid": is_valid,
"validation_issues": validation_issues if validation_issues else ["✅ All checks passed"],
"preview": "Code analysis complete"
}
except Exception as e:
return {
"error": str(e),
"preview": "Code analysis failed"
}
def save_and_redeploy(deployment_id, edited_code, packages_str):
"""Save edited code and redeploy to Modal"""
# Validate deployment selection
if not deployment_id:
return {"success": False, "error": "No deployment selected"}
# Validate code
if not edited_code:
return {"success": False, "error": "No code provided"}
if edited_code.startswith("# Load") or edited_code.startswith("# Error") or edited_code.startswith("# Select"):
return {"success": False, "error": "No valid code to save. Please load a deployment first."}
if edited_code.startswith("# Exception"):
return {"success": False, "error": "Cannot save error placeholder. Please load a valid deployment."}
try:
# Convert packages string to list
package_list = []
if packages_str and not packages_str.startswith("No"):
package_list = [p.strip() for p in packages_str.split(",") if p.strip()]
# Call update function
result = update_deployment_code(
deployment_id=deployment_id,
mcp_tools_code=edited_code,
extra_pip_packages=package_list
)
return result
except Exception as e:
return {
"success": False,
"error": f"Exception during save: {str(e)}",
"exception": True
}
# Wire up events
refresh_deployments_btn.click(
fn=load_deployment_list,
outputs=deployment_selector,
api_visibility="private" # Don't expose UI handler as MCP tool
)
load_btn.click(
fn=load_code,
inputs=[deployment_selector],
outputs=[code_editor, deployment_info, packages_display, tools_preview, output],
api_visibility="private" # Don't expose UI handler as MCP tool
)
save_btn.click(
fn=save_and_redeploy,
inputs=[deployment_selector, code_editor, packages_display],
outputs=output,
api_visibility="private" # Don't expose UI handler as MCP tool
)
preview_btn.click(
fn=preview_code,
inputs=[code_editor],
outputs=output,
api_visibility="private" # Don't expose UI handler as MCP tool
)
# Load deployments on editor load
editor.load(
fn=load_deployment_list,
outputs=deployment_selector,
api_visibility="private" # Don't expose UI handler as MCP tool
)
return editor