korakot commited on
Commit
48a7d73
Β·
verified Β·
1 Parent(s): d090f04

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +32 -14
app.py CHANGED
@@ -1,22 +1,23 @@
1
  """render-html MCP Apps server β€” renders arbitrary HTML inline in AI chat.
2
 
3
  Architecture (matching PDF Viewer pattern):
4
- 1. Resource at ui://render-html/viewer.html β†’ the viewer app (text/html;profile=mcp-app)
5
- 2. Tools return structuredContent with _meta.ui.resourceUri pointing to viewer
6
- 3. Viewer receives data via postMessage (ui/notifications/tool-result)
 
7
  """
8
 
9
  from mcp.server.fastmcp import FastMCP
10
  from mcp.server.transport_security import TransportSecuritySettings
11
  from mcp.types import CallToolResult, TextContent
 
 
12
 
13
  mcp = FastMCP("render-html", transport_security=TransportSecuritySettings(
14
  enable_dns_rebinding_protection=False,
15
  ))
16
 
17
  # ── The viewer HTML app ──────────────────────────────────────────────
18
- # This is served as an MCP resource. The host renders it in a sandboxed
19
- # iframe and sends tool results to it via postMessage.
20
 
21
  VIEWER_HTML = """<!DOCTYPE html>
22
  <html lang="en">
@@ -73,6 +74,12 @@ VIEWER_HTML = """<!DOCTYPE html>
73
 
74
  VIEWER_URI = "ui://render-html/viewer.html"
75
 
 
 
 
 
 
 
76
 
77
  # ── Register the viewer as an MCP resource ───────────────────────────
78
  @mcp.resource(
@@ -85,6 +92,17 @@ def viewer_resource() -> str:
85
  return VIEWER_HTML
86
 
87
 
 
 
 
 
 
 
 
 
 
 
 
88
  # ── Tools ────────────────────────────────────────────────────────────
89
 
90
  @mcp.tool(
@@ -94,9 +112,9 @@ def viewer_resource() -> str:
94
  "readOnlyHint": True,
95
  "openWorldHint": True,
96
  },
97
- meta={"ui": {"resourceUri": VIEWER_URI}},
98
  )
99
- def render_html(html: str, title: str = "Rendered Content") -> CallToolResult:
100
  """Render arbitrary HTML content inline in the conversation.
101
 
102
  Use this to display images, SVG diagrams, charts, styled content,
@@ -109,7 +127,7 @@ def render_html(html: str, title: str = "Rendered Content") -> CallToolResult:
109
  return CallToolResult(
110
  content=[TextContent(type="text", text=f"Rendered: {title}")],
111
  structuredContent={"html": html, "title": title},
112
- _meta={"ui": {"resourceUri": VIEWER_URI}},
113
  )
114
 
115
 
@@ -120,9 +138,9 @@ def render_html(html: str, title: str = "Rendered Content") -> CallToolResult:
120
  "readOnlyHint": True,
121
  "openWorldHint": True,
122
  },
123
- meta={"ui": {"resourceUri": VIEWER_URI}},
124
  )
125
- def render_image(url: str, alt: str = "", width: int = 0) -> CallToolResult:
126
  """Display an image from a URL inline in the conversation.
127
 
128
  Args:
@@ -143,7 +161,7 @@ def render_image(url: str, alt: str = "", width: int = 0) -> CallToolResult:
143
  return CallToolResult(
144
  content=[TextContent(type="text", text=f"Image: {alt or url}")],
145
  structuredContent={"html": img_html, "title": alt or "Image"},
146
- _meta={"ui": {"resourceUri": VIEWER_URI}},
147
  )
148
 
149
 
@@ -154,9 +172,9 @@ def render_image(url: str, alt: str = "", width: int = 0) -> CallToolResult:
154
  "readOnlyHint": True,
155
  "openWorldHint": True,
156
  },
157
- meta={"ui": {"resourceUri": VIEWER_URI}},
158
  )
159
- def render_svg(svg: str, title: str = "SVG Diagram") -> CallToolResult:
160
  """Render an SVG diagram inline in the conversation.
161
 
162
  Args:
@@ -171,7 +189,7 @@ def render_svg(svg: str, title: str = "SVG Diagram") -> CallToolResult:
171
  return CallToolResult(
172
  content=[TextContent(type="text", text=f"SVG: {title}")],
173
  structuredContent={"svg": wrapped, "title": title},
174
- _meta={"ui": {"resourceUri": VIEWER_URI}},
175
  )
176
 
177
 
 
1
  """render-html MCP Apps server β€” renders arbitrary HTML inline in AI chat.
2
 
3
  Architecture (matching PDF Viewer pattern):
4
+ 1. Resource at ui://render-html/viewer.html (text/html;profile=mcp-app)
5
+ 2. Tools have _meta.ui.resourceUri AND _meta["ui/resourceUri"] pointing to viewer
6
+ 3. Tools return structuredContent + outputSchema
7
+ 4. Viewer receives data via postMessage (ui/notifications/tool-result)
8
  """
9
 
10
  from mcp.server.fastmcp import FastMCP
11
  from mcp.server.transport_security import TransportSecuritySettings
12
  from mcp.types import CallToolResult, TextContent
13
+ from pydantic import BaseModel, Field
14
+ from typing import Annotated
15
 
16
  mcp = FastMCP("render-html", transport_security=TransportSecuritySettings(
17
  enable_dns_rebinding_protection=False,
18
  ))
19
 
20
  # ── The viewer HTML app ──────────────────────────────────────────────
 
 
21
 
22
  VIEWER_HTML = """<!DOCTYPE html>
23
  <html lang="en">
 
74
 
75
  VIEWER_URI = "ui://render-html/viewer.html"
76
 
77
+ # Both forms of the meta key (PDF Viewer uses both)
78
+ TOOL_META = {
79
+ "ui": {"resourceUri": VIEWER_URI},
80
+ "ui/resourceUri": VIEWER_URI,
81
+ }
82
+
83
 
84
  # ── Register the viewer as an MCP resource ───────────────────────────
85
  @mcp.resource(
 
92
  return VIEWER_HTML
93
 
94
 
95
+ # ── Output schemas ───────────────────────────────────────────────────
96
+
97
+ class HtmlOutput(BaseModel):
98
+ html: str
99
+ title: str
100
+
101
+ class SvgOutput(BaseModel):
102
+ svg: str
103
+ title: str
104
+
105
+
106
  # ── Tools ────────────────────────────────────────────────────────────
107
 
108
  @mcp.tool(
 
112
  "readOnlyHint": True,
113
  "openWorldHint": True,
114
  },
115
+ meta=TOOL_META,
116
  )
117
+ def render_html(html: str, title: str = "Rendered Content") -> Annotated[CallToolResult, HtmlOutput]:
118
  """Render arbitrary HTML content inline in the conversation.
119
 
120
  Use this to display images, SVG diagrams, charts, styled content,
 
127
  return CallToolResult(
128
  content=[TextContent(type="text", text=f"Rendered: {title}")],
129
  structuredContent={"html": html, "title": title},
130
+ _meta=TOOL_META,
131
  )
132
 
133
 
 
138
  "readOnlyHint": True,
139
  "openWorldHint": True,
140
  },
141
+ meta=TOOL_META,
142
  )
143
+ def render_image(url: str, alt: str = "", width: int = 0) -> Annotated[CallToolResult, HtmlOutput]:
144
  """Display an image from a URL inline in the conversation.
145
 
146
  Args:
 
161
  return CallToolResult(
162
  content=[TextContent(type="text", text=f"Image: {alt or url}")],
163
  structuredContent={"html": img_html, "title": alt or "Image"},
164
+ _meta=TOOL_META,
165
  )
166
 
167
 
 
172
  "readOnlyHint": True,
173
  "openWorldHint": True,
174
  },
175
+ meta=TOOL_META,
176
  )
177
+ def render_svg(svg: str, title: str = "SVG Diagram") -> Annotated[CallToolResult, SvgOutput]:
178
  """Render an SVG diagram inline in the conversation.
179
 
180
  Args:
 
189
  return CallToolResult(
190
  content=[TextContent(type="text", text=f"SVG: {title}")],
191
  structuredContent={"svg": wrapped, "title": title},
192
+ _meta=TOOL_META,
193
  )
194
 
195