Spaces:
Running
Running
Upload app.py with huggingface_hub
Browse files
app.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""render-html MCP Apps server — renders arbitrary HTML inline in AI chat.
|
| 2 |
+
|
| 3 |
+
Deploy as HF Space, connect as MCP connector in Claude/ChatGPT.
|
| 4 |
+
Endpoint: https://korakot-render-html.hf.space/mcp
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from fastmcp import FastMCP
|
| 8 |
+
from mcp_ui_server import create_ui_resource
|
| 9 |
+
from mcp_ui_server.core import UIResource
|
| 10 |
+
from fastapi import FastAPI
|
| 11 |
+
from fastapi.responses import HTMLResponse
|
| 12 |
+
import uvicorn
|
| 13 |
+
|
| 14 |
+
# FastMCP server
|
| 15 |
+
mcp = FastMCP("render-html")
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@mcp.tool()
|
| 19 |
+
def render_html(html: str, title: str = "Rendered Content") -> list[UIResource]:
|
| 20 |
+
"""Render arbitrary HTML content inline in the conversation.
|
| 21 |
+
|
| 22 |
+
Use this to display images, SVG diagrams, charts, styled content,
|
| 23 |
+
or any HTML/CSS/JS directly in the chat. The HTML is rendered in
|
| 24 |
+
a sandboxed iframe.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
html: Complete HTML content to render. Can include style, script,
|
| 28 |
+
inline styles, SVG, images via img src, etc.
|
| 29 |
+
title: Optional title for the rendered content.
|
| 30 |
+
"""
|
| 31 |
+
return [create_ui_resource({
|
| 32 |
+
"uri": f"ui://render-html/{title.lower().replace(' ', '-')}",
|
| 33 |
+
"content": {
|
| 34 |
+
"type": "rawHtml",
|
| 35 |
+
"htmlString": html,
|
| 36 |
+
},
|
| 37 |
+
"encoding": "text",
|
| 38 |
+
})]
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
@mcp.tool()
|
| 42 |
+
def render_image(url: str, alt: str = "", width: int = 0) -> list[UIResource]:
|
| 43 |
+
"""Display an image from a URL inline in the conversation.
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
url: The image URL to display.
|
| 47 |
+
alt: Alt text for the image.
|
| 48 |
+
width: Optional width in pixels (0 = auto).
|
| 49 |
+
"""
|
| 50 |
+
style = 'max-width:100%;height:auto;'
|
| 51 |
+
if width > 0:
|
| 52 |
+
style = f'width:{width}px;max-width:100%;height:auto;'
|
| 53 |
+
|
| 54 |
+
html = f'''<!DOCTYPE html>
|
| 55 |
+
<html><body style="margin:0;display:flex;justify-content:center;align-items:center;min-height:100vh;">
|
| 56 |
+
<img src="{url}" alt="{alt}" style="{style}" />
|
| 57 |
+
</body></html>'''
|
| 58 |
+
|
| 59 |
+
return [create_ui_resource({
|
| 60 |
+
"uri": "ui://render-html/image",
|
| 61 |
+
"content": {"type": "rawHtml", "htmlString": html},
|
| 62 |
+
"encoding": "text",
|
| 63 |
+
})]
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
@mcp.tool()
|
| 67 |
+
def render_svg(svg: str, title: str = "SVG Diagram") -> list[UIResource]:
|
| 68 |
+
"""Render an SVG diagram inline in the conversation.
|
| 69 |
+
|
| 70 |
+
Args:
|
| 71 |
+
svg: SVG markup (the svg element).
|
| 72 |
+
title: Optional title.
|
| 73 |
+
"""
|
| 74 |
+
html = f'''<!DOCTYPE html>
|
| 75 |
+
<html><body style="margin:0;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;">
|
| 76 |
+
{svg}
|
| 77 |
+
</body></html>'''
|
| 78 |
+
|
| 79 |
+
return [create_ui_resource({
|
| 80 |
+
"uri": "ui://render-html/svg",
|
| 81 |
+
"content": {"type": "rawHtml", "htmlString": html},
|
| 82 |
+
"encoding": "text",
|
| 83 |
+
})]
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
# FastAPI wrapper for landing page + MCP mount
|
| 87 |
+
app = FastAPI(title="render-html MCP")
|
| 88 |
+
|
| 89 |
+
# Mount MCP at /mcp
|
| 90 |
+
mcp_app = mcp.http_app()
|
| 91 |
+
app.mount("/mcp", mcp_app)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
@app.get("/", response_class=HTMLResponse)
|
| 95 |
+
async def index():
|
| 96 |
+
return """<!DOCTYPE html>
|
| 97 |
+
<html><body style="font-family:system-ui;max-width:600px;margin:40px auto;padding:0 20px;">
|
| 98 |
+
<h1>render-html MCP Server</h1>
|
| 99 |
+
<p>An MCP Apps server that renders HTML inline in AI conversations.</p>
|
| 100 |
+
<h2>Tools</h2>
|
| 101 |
+
<ul>
|
| 102 |
+
<li><b>render_html</b> - render arbitrary HTML/CSS/JS</li>
|
| 103 |
+
<li><b>render_image</b> - display an image from URL</li>
|
| 104 |
+
<li><b>render_svg</b> - render SVG diagrams</li>
|
| 105 |
+
</ul>
|
| 106 |
+
<h2>Connect</h2>
|
| 107 |
+
<p>Add as custom MCP connector:<br>
|
| 108 |
+
<code>https://korakot-render-html.hf.space/mcp</code></p>
|
| 109 |
+
</body></html>"""
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
if __name__ == "__main__":
|
| 113 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|