From 17c6231a2068abd6c78394542a955b23c153ea84 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 22 Nov 2025 02:39:31 +0000
Subject: [PATCH 01/13] Initial plan
From 3b19b6b0e4874481e6080bfb3392cd4054226dcc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 22 Nov 2025 02:49:25 +0000
Subject: [PATCH 02/13] Add iframe support to canvas panel for MCP tools
- Added support for iframe type artifacts in CanvasPanel
- Updated DOMPurify configuration to allow iframes with proper sandboxing
- Added handling for display.canvas.type = "iframe" from v2 MCP spec
- Created demo MCP tool to test iframe functionality
- Configured proper security attributes (sandbox) for iframes
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
---
backend/mcp/ui-demo/main.py | 27 ++++++++++
frontend/src/components/CanvasPanel.jsx | 53 ++++++++++++++++---
.../src/handlers/chat/websocketHandlers.js | 19 ++++++-
3 files changed, 89 insertions(+), 10 deletions(-)
diff --git a/backend/mcp/ui-demo/main.py b/backend/mcp/ui-demo/main.py
index 77fba12..fab2d16 100644
--- a/backend/mcp/ui-demo/main.py
+++ b/backend/mcp/ui-demo/main.py
@@ -255,5 +255,32 @@ def get_image() -> Dict[str, Any]:
}
}
+@mcp.tool
+def create_iframe_demo() -> Dict[str, Any]:
+ """
+ Create a demo showing how to embed external content using iframes.
+
+ This demonstrates the v2 MCP iframe capability for embedding interactive
+ external content like dashboards, visualizations, or web applications.
+
+ Returns:
+ Dictionary with iframe display configuration
+ """
+ return {
+ "results": {
+ "content": "Iframe demo created! An external webpage will be displayed in the canvas panel.",
+ "iframe_url": "https://example.com"
+ },
+ "artifacts": [],
+ "display": {
+ "open_canvas": True,
+ "type": "iframe",
+ "url": "https://example.com",
+ "title": "Example Website",
+ "sandbox": "allow-scripts allow-same-origin",
+ "mode": "replace"
+ }
+ }
+
if __name__ == "__main__":
mcp.run()
diff --git a/frontend/src/components/CanvasPanel.jsx b/frontend/src/components/CanvasPanel.jsx
index 6ff5d86..a4b09d5 100644
--- a/frontend/src/components/CanvasPanel.jsx
+++ b/frontend/src/components/CanvasPanel.jsx
@@ -20,7 +20,7 @@ const processCanvasContent = (content) => {
// Fallback to JSON for other objects
try {
return JSON.stringify(content, null, 2)
- } catch (e) {
+ } catch {
return String(content || '')
}
}
@@ -148,6 +148,18 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
return;
}
+ // Handle iframe artifacts (from display config with URL)
+ if (currentFile.type === 'iframe' && currentFile.url) {
+ setCurrentFileContent({
+ type: 'iframe',
+ url: currentFile.url,
+ file: currentFile,
+ sandbox: currentFile.sandbox || 'allow-scripts allow-same-origin allow-forms'
+ });
+ setIsLoadingFile(false);
+ return;
+ }
+
// Fetch file content from the backend
const response = await fetch(`/api/files/download/${currentFile.s3_key}`, {
method: 'GET',
@@ -201,8 +213,8 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
const handleDownload = () => {
const currentFile = canvasFiles[currentCanvasFileIndex];
- // Inline-only files are not downloadable via backend
- if (currentFile && !currentFile.isInline && downloadFile) {
+ // Inline-only files and iframes are not downloadable via backend
+ if (currentFile && !currentFile.isInline && currentFile.type !== 'iframe' && downloadFile) {
downloadFile(currentFile.filename);
}
};
@@ -212,6 +224,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
case 'image': return ;
case 'pdf': return ;
case 'html': return ;
+ case 'iframe': return ;
default: return ;
}
};
@@ -271,12 +284,27 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
);
+ case 'iframe':
+ return (
+
+
+
+ );
+
case 'html':
return (
);
@@ -285,7 +313,10 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
try {
// Try to parse as markdown first
const markdownHtml = marked.parse(currentFileContent.content);
- const sanitizedHtml = DOMPurify.sanitize(markdownHtml);
+ const sanitizedHtml = DOMPurify.sanitize(markdownHtml, {
+ ADD_TAGS: ['iframe'],
+ ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling', 'sandbox', 'src']
+ });
return (
{
dangerouslySetInnerHTML={{ __html: sanitizedHtml }}
/>
);
- } catch (error) {
+ } catch {
// Fallback to plain text with syntax highlighting for code files
const fileExt = currentFileContent.file.filename.split('.').pop().toLowerCase();
const isCodeFile = ['js', 'py', 'java', 'cpp', 'ts', 'jsx', 'tsx', 'css', 'html', 'json', 'sql'].includes(fileExt);
@@ -318,7 +349,10 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
)
@@ -340,7 +374,10 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
try {
const markdownHtml = marked.parse(content)
- const sanitizedHtml = DOMPurify.sanitize(markdownHtml)
+ const sanitizedHtml = DOMPurify.sanitize(markdownHtml, {
+ ADD_TAGS: ['iframe'],
+ ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling', 'sandbox', 'src']
+ })
return (
f.filename === updateData.display.primary_file)
+ const idx = canvasFiles.findIndex(f => f.filename === updateData.display.primary_file)
setCurrentCanvasFileIndex(idx >= 0 ? idx : 0)
} else {
setCurrentCanvasFileIndex(0)
From 7d2269b279c1465c461f0ccdae3c1914f01810bb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 22 Nov 2025 02:51:32 +0000
Subject: [PATCH 03/13] Add test for iframe canvas file creation from display
config
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
---
.../handlers/chat/websocketHandlers.test.js | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/frontend/src/handlers/chat/websocketHandlers.test.js b/frontend/src/handlers/chat/websocketHandlers.test.js
index fabdd28..e41e4d8 100644
--- a/frontend/src/handlers/chat/websocketHandlers.test.js
+++ b/frontend/src/handlers/chat/websocketHandlers.test.js
@@ -105,4 +105,44 @@ describe('createWebSocketHandler – intermediate updates', () => {
expect(deps.setCanvasContent).toHaveBeenCalledWith('')
expect(deps.setCustomUIContent).toHaveBeenCalledWith(null)
})
+
+ it('creates iframe canvas file from display config with type=iframe', () => {
+ const deps = makeDeps()
+ const handler = createWebSocketHandler(deps)
+
+ const payload = {
+ type: 'intermediate_update',
+ update_type: 'canvas_files',
+ data: {
+ files: [],
+ display: {
+ type: 'iframe',
+ url: 'https://example.com/dashboard',
+ title: 'Analytics Dashboard',
+ sandbox: 'allow-scripts allow-same-origin',
+ open_canvas: true
+ }
+ }
+ }
+
+ handler(payload)
+
+ // Should create a virtual iframe canvas file
+ expect(deps.setCanvasFiles).toHaveBeenCalledTimes(1)
+ const canvasFiles = deps.setCanvasFiles.mock.calls[0][0]
+ expect(canvasFiles).toEqual([
+ {
+ filename: 'Analytics Dashboard',
+ type: 'iframe',
+ url: 'https://example.com/dashboard',
+ sandbox: 'allow-scripts allow-same-origin',
+ isInline: true
+ }
+ ])
+
+ // Should select the iframe file
+ expect(deps.setCurrentCanvasFileIndex).toHaveBeenCalledWith(0)
+ expect(deps.setCanvasContent).toHaveBeenCalledWith('')
+ expect(deps.setCustomUIContent).toHaveBeenCalledWith(null)
+ })
})
From c0f697a4d8c298afba7d5e420038eeb40ccde600 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 22 Nov 2025 02:52:49 +0000
Subject: [PATCH 04/13] Add HTML artifact demo with embedded iframe
Added create_html_with_iframe tool to demonstrate HTML artifacts
that include embedded iframes within the HTML content
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
---
backend/mcp/ui-demo/main.py | 79 +++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/backend/mcp/ui-demo/main.py b/backend/mcp/ui-demo/main.py
index fab2d16..0299b65 100644
--- a/backend/mcp/ui-demo/main.py
+++ b/backend/mcp/ui-demo/main.py
@@ -282,5 +282,84 @@ def create_iframe_demo() -> Dict[str, Any]:
}
}
+@mcp.tool
+def create_html_with_iframe() -> Dict[str, Any]:
+ """
+ Create an HTML artifact that includes an embedded iframe.
+
+ This demonstrates how MCP tools can return HTML content with embedded
+ iframes that will be properly rendered in the canvas panel.
+
+ Returns:
+ Dictionary with HTML artifact containing an iframe
+ """
+ html_content = """
+
+
+
+
+
+
Embedded Content Demo
+
This HTML artifact includes an embedded iframe showing external content:
+
+
+
+
+"""
+
+ import base64
+ html_base64 = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
+
+ return {
+ "results": {
+ "content": "HTML with embedded iframe created! Check the canvas panel."
+ },
+ "artifacts": [
+ {
+ "name": "iframe_demo.html",
+ "b64": html_base64,
+ "mime": "text/html",
+ "size": len(html_content.encode('utf-8')),
+ "description": "HTML page with embedded iframe",
+ "viewer": "html"
+ }
+ ],
+ "display": {
+ "open_canvas": True,
+ "primary_file": "iframe_demo.html",
+ "mode": "replace",
+ "viewer_hint": "html"
+ }
+ }
+
if __name__ == "__main__":
mcp.run()
From 74bbf6bb788b67d8dd3410a682eb86f8df388f96 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 22 Nov 2025 02:54:11 +0000
Subject: [PATCH 05/13] Add iframe support documentation to developer guide
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
---
docs/03_developer_guide.md | 75 +++++++++++++++++++++++++++++++++++++-
1 file changed, 74 insertions(+), 1 deletion(-)
diff --git a/docs/03_developer_guide.md b/docs/03_developer_guide.md
index 2dabd22..dbcbf02 100644
--- a/docs/03_developer_guide.md
+++ b/docs/03_developer_guide.md
@@ -82,6 +82,7 @@ The returned JSON object has the following structure:
* `name`: The filename (e.g., `report.html`).
* `b64`: The base64-encoded content of the file.
* `mime`: The MIME type (e.g., `text/html`, `image/png`).
+ * `viewer`: The preferred viewer type (e.g., `html`, `image`, `pdf`, `iframe`).
* **`display`** (optional): A JSON object that provides hints to the UI on how to display the artifacts, such as whether to open the canvas automatically.
**Example tool returning an artifact:**
@@ -99,7 +100,8 @@ def create_html_report(title: str) -> Dict[str, Any]:
"artifacts": [{
"name": "report.html",
"b64": b64_content,
- "mime": "text/html"
+ "mime": "text/html",
+ "viewer": "html"
}],
"display": {
"open_canvas": True,
@@ -108,6 +110,77 @@ def create_html_report(title: str) -> Dict[str, Any]:
}
```
+#### Iframe Support for External Content
+
+MCP tools can embed external content using iframes in two ways:
+
+**Approach 1: Direct Iframe via Display Config**
+
+Use this when you want to display an external URL directly without wrapping it in HTML:
+
+```python
+@mcp.tool
+def show_dashboard() -> Dict[str, Any]:
+ """Display an external dashboard in the canvas."""
+ return {
+ "results": {"summary": "Dashboard loaded"},
+ "artifacts": [],
+ "display": {
+ "type": "iframe",
+ "url": "https://example.com/dashboard",
+ "title": "Analytics Dashboard",
+ "sandbox": "allow-scripts allow-same-origin",
+ "open_canvas": True
+ }
+ }
+```
+
+**Approach 2: HTML Artifact with Embedded Iframe**
+
+Use this when you want to create a custom HTML page that includes one or more iframes:
+
+```python
+@mcp.tool
+def create_page_with_iframe() -> Dict[str, Any]:
+ """Create an HTML page with embedded iframe."""
+ html_content = """
+
+
+
External Content
+
+
+
+ """
+ b64_content = base64.b64encode(html_content.encode()).decode()
+
+ return {
+ "results": {"summary": "Page created"},
+ "artifacts": [{
+ "name": "page.html",
+ "b64": b64_content,
+ "mime": "text/html",
+ "viewer": "html"
+ }],
+ "display": {
+ "open_canvas": True,
+ "primary_file": "page.html"
+ }
+ }
+```
+
+**Security Considerations for Iframes:**
+
+* Iframes are automatically sandboxed for security
+* Default sandbox permissions: `"allow-scripts allow-same-origin allow-forms"`
+* Tools can specify custom sandbox attributes via the `sandbox` field
+* Only whitelisted iframe attributes are preserved: `src`, `sandbox`, `allow`, `allowfullscreen`, `frameborder`, `scrolling`
+* All HTML content is sanitized with DOMPurify before rendering
+
### 3. Registering the Server
After creating your server, you must register it in `config/overrides/mcp.json`.
From 2329e055a71b9a0b5a5a30a5889019ed94a29745 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 22 Nov 2025 02:57:06 +0000
Subject: [PATCH 06/13] Address code review feedback
- Extract DOMPurify config to constant to avoid duplication
- Simplify download check logic with helper variable
- Make default sandbox more restrictive (removed allow-forms)
- Update documentation with more secure defaults
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
---
docs/03_developer_guide.md | 4 +--
frontend/src/components/CanvasPanel.jsx | 32 ++++++++-----------
.../src/handlers/chat/websocketHandlers.js | 2 +-
3 files changed, 16 insertions(+), 22 deletions(-)
diff --git a/docs/03_developer_guide.md b/docs/03_developer_guide.md
index dbcbf02..f80f1d8 100644
--- a/docs/03_developer_guide.md
+++ b/docs/03_developer_guide.md
@@ -176,8 +176,8 @@ def create_page_with_iframe() -> Dict[str, Any]:
**Security Considerations for Iframes:**
* Iframes are automatically sandboxed for security
-* Default sandbox permissions: `"allow-scripts allow-same-origin allow-forms"`
-* Tools can specify custom sandbox attributes via the `sandbox` field
+* Default sandbox permissions: `"allow-scripts allow-same-origin"` (restrictive by default)
+* Tools can specify custom sandbox attributes via the `sandbox` field to add permissions like `"allow-forms"` if needed
* Only whitelisted iframe attributes are preserved: `src`, `sandbox`, `allow`, `allowfullscreen`, `frameborder`, `scrolling`
* All HTML content is sanitized with DOMPurify before rendering
diff --git a/frontend/src/components/CanvasPanel.jsx b/frontend/src/components/CanvasPanel.jsx
index a4b09d5..8ff4f9d 100644
--- a/frontend/src/components/CanvasPanel.jsx
+++ b/frontend/src/components/CanvasPanel.jsx
@@ -4,6 +4,12 @@ import { marked } from 'marked'
import DOMPurify from 'dompurify'
import { useState, useEffect } from 'react'
+// DOMPurify configuration for allowing iframes in HTML content
+const IFRAME_SANITIZE_CONFIG = {
+ ADD_TAGS: ['iframe'],
+ ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling', 'sandbox', 'src']
+};
+
// Helper function to process canvas content (strings and structured objects)
const processCanvasContent = (content) => {
if (typeof content === 'string') {
@@ -154,7 +160,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
type: 'iframe',
url: currentFile.url,
file: currentFile,
- sandbox: currentFile.sandbox || 'allow-scripts allow-same-origin allow-forms'
+ sandbox: currentFile.sandbox || 'allow-scripts allow-same-origin'
});
setIsLoadingFile(false);
return;
@@ -213,8 +219,8 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
const handleDownload = () => {
const currentFile = canvasFiles[currentCanvasFileIndex];
- // Inline-only files and iframes are not downloadable via backend
- if (currentFile && !currentFile.isInline && currentFile.type !== 'iframe' && downloadFile) {
+ const canDownload = currentFile && !currentFile.isInline && currentFile.type !== 'iframe';
+ if (canDownload && downloadFile) {
downloadFile(currentFile.filename);
}
};
@@ -301,10 +307,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
);
@@ -313,10 +316,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
try {
// Try to parse as markdown first
const markdownHtml = marked.parse(currentFileContent.content);
- const sanitizedHtml = DOMPurify.sanitize(markdownHtml, {
- ADD_TAGS: ['iframe'],
- ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling', 'sandbox', 'src']
- });
+ const sanitizedHtml = DOMPurify.sanitize(markdownHtml, IFRAME_SANITIZE_CONFIG);
return (
{
)
@@ -374,10 +371,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
try {
const markdownHtml = marked.parse(content)
- const sanitizedHtml = DOMPurify.sanitize(markdownHtml, {
- ADD_TAGS: ['iframe'],
- ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling', 'sandbox', 'src']
- })
+ const sanitizedHtml = DOMPurify.sanitize(markdownHtml, IFRAME_SANITIZE_CONFIG)
return (
Date: Sat, 22 Nov 2025 02:59:12 +0000
Subject: [PATCH 07/13] Extract constants and helper function for better
maintainability
- Add DEFAULT_IFRAME_SANDBOX constant for consistency
- Extract canDownloadFile helper function for readability
- Use constants in both CanvasPanel and websocketHandlers
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
---
frontend/src/components/CanvasPanel.jsx | 16 +++++++++++++---
frontend/src/handlers/chat/websocketHandlers.js | 5 ++++-
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/frontend/src/components/CanvasPanel.jsx b/frontend/src/components/CanvasPanel.jsx
index 8ff4f9d..53e4673 100644
--- a/frontend/src/components/CanvasPanel.jsx
+++ b/frontend/src/components/CanvasPanel.jsx
@@ -10,6 +10,17 @@ const IFRAME_SANITIZE_CONFIG = {
ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling', 'sandbox', 'src']
};
+// Default sandbox permissions for iframes (restrictive by default)
+const DEFAULT_IFRAME_SANDBOX = 'allow-scripts allow-same-origin';
+
+// Helper function to check if a file can be downloaded
+const canDownloadFile = (file) => {
+ if (!file) return false;
+ if (file.isInline) return false; // Inline files don't have backend storage
+ if (file.type === 'iframe') return false; // Iframes are not downloadable
+ return true;
+};
+
// Helper function to process canvas content (strings and structured objects)
const processCanvasContent = (content) => {
if (typeof content === 'string') {
@@ -160,7 +171,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
type: 'iframe',
url: currentFile.url,
file: currentFile,
- sandbox: currentFile.sandbox || 'allow-scripts allow-same-origin'
+ sandbox: currentFile.sandbox || DEFAULT_IFRAME_SANDBOX
});
setIsLoadingFile(false);
return;
@@ -219,8 +230,7 @@ const CanvasPanel = ({ isOpen, onClose, onWidthChange }) => {
const handleDownload = () => {
const currentFile = canvasFiles[currentCanvasFileIndex];
- const canDownload = currentFile && !currentFile.isInline && currentFile.type !== 'iframe';
- if (canDownload && downloadFile) {
+ if (canDownloadFile(currentFile) && downloadFile) {
downloadFile(currentFile.filename);
}
};
diff --git a/frontend/src/handlers/chat/websocketHandlers.js b/frontend/src/handlers/chat/websocketHandlers.js
index ae12318..b7e409e 100644
--- a/frontend/src/handlers/chat/websocketHandlers.js
+++ b/frontend/src/handlers/chat/websocketHandlers.js
@@ -1,5 +1,8 @@
// Handlers extracted from original ChatContext to keep provider lean
+// Default sandbox permissions for iframes (restrictive by default)
+const DEFAULT_IFRAME_SANDBOX = 'allow-scripts allow-same-origin';
+
export function createWebSocketHandler(deps) {
const {
addMessage,
@@ -174,7 +177,7 @@ export function createWebSocketHandler(deps) {
filename: updateData.display.title || 'Embedded Content',
type: 'iframe',
url: updateData.display.url,
- sandbox: updateData.display.sandbox || 'allow-scripts allow-same-origin',
+ sandbox: updateData.display.sandbox || DEFAULT_IFRAME_SANDBOX,
isInline: true
}
canvasFiles = [iframeFile, ...canvasFiles]
From 4e0d132483284a79045bce37d30547b655660d95 Mon Sep 17 00:00:00 2001
From: Anthony
Date: Sat, 22 Nov 2025 04:24:01 +0000
Subject: [PATCH 08/13] docs(ui-demo): enhance create_button_demo docstring
with detailed sections
Improve documentation for the create_button_demo function to provide clearer
descriptions of its capabilities, use cases, technical implementation, and
integration features. This update helps developers and users better understand
the tool's features and benefits for UI prototyping and demonstrations.
---
backend/mcp/ui-demo/main.py | 128 ++++++++++++-------------
docs/developer/canvas-renderers.md | 26 ++++-
docs/developer/creating-mcp-servers.md | 2 +
3 files changed, 87 insertions(+), 69 deletions(-)
diff --git a/backend/mcp/ui-demo/main.py b/backend/mcp/ui-demo/main.py
index 0299b65..c6249df 100644
--- a/backend/mcp/ui-demo/main.py
+++ b/backend/mcp/ui-demo/main.py
@@ -31,64 +31,64 @@ def load_template(template_name: str) -> str:
with open(template_path, "r") as f:
return f.read()
-@mcp.tool
-def create_button_demo() -> Dict[str, Any]:
- """
- Generate interactive HTML demonstrations showcasing advanced UI customization and dynamic interface capabilities.
-
- This UI prototyping tool creates sophisticated interactive demonstrations:
-
- **Interactive UI Components:**
- - Custom HTML button interfaces with advanced styling
- - Dynamic interaction patterns and user feedback systems
- - Professional design templates with modern aesthetics
- - Responsive layouts optimized for different screen sizes
-
- **UI Customization Features:**
- - Advanced CSS styling with modern design patterns
- - Interactive JavaScript functionality for user engagement
- - Professional color schemes and typography
- - Accessibility-compliant interface elements
-
- **Demonstration Capabilities:**
- - Real-time UI modification examples
- - Interactive component behavior showcases
- - Design pattern implementation demonstrations
- - User experience optimization examples
-
- **Technical Implementation:**
- - Clean HTML5 structure with semantic elements
- - Modern CSS3 styling with flexbox and grid layouts
- - Vanilla JavaScript for cross-browser compatibility
- - Base64 encoding for seamless artifact delivery
-
- **Use Cases:**
- - UI design prototyping and concept validation
- - Client demonstration and stakeholder presentations
- - Design system documentation and examples
- - Interactive tutorial and training materials
- - A/B testing interface variations
- - User experience research and testing
-
- **Professional Features:**
- - Production-ready code quality and structure
- - Cross-browser compatibility and standards compliance
- - Performance-optimized implementation
- - Maintainable and extensible code architecture
-
- **Integration Capabilities:**
- - Canvas viewer integration for immediate preview
- - Downloadable HTML for offline use and sharing
- - Framework-agnostic implementation
- - Easy customization and extension
-
- Returns:
- Dictionary containing:
- - results: Demo creation summary and success confirmation
- - artifacts: Interactive HTML demonstration as downloadable content
- - display: Optimized canvas viewer configuration for immediate preview
- - Interactive elements ready for user testing and evaluation
- Or error message if HTML generation or template loading fails
+@mcp.tool
+def create_button_demo() -> Dict[str, Any]:
+ """
+ Generate interactive HTML demonstrations showcasing advanced UI customization and dynamic interface capabilities.
+
+ This UI prototyping tool creates sophisticated interactive demonstrations:
+
+ **Interactive UI Components:**
+ - Custom HTML button interfaces with advanced styling
+ - Dynamic interaction patterns and user feedback systems
+ - Professional design templates with modern aesthetics
+ - Responsive layouts optimized for different screen sizes
+
+ **UI Customization Features:**
+ - Advanced CSS styling with modern design patterns
+ - Interactive JavaScript functionality for user engagement
+ - Professional color schemes and typography
+ - Accessibility-compliant interface elements
+
+ **Demonstration Capabilities:**
+ - Real-time UI modification examples
+ - Interactive component behavior showcases
+ - Design pattern implementation demonstrations
+ - User experience optimization examples
+
+ **Technical Implementation:**
+ - Clean HTML5 structure with semantic elements
+ - Modern CSS3 styling with flexbox and grid layouts
+ - Vanilla JavaScript for cross-browser compatibility
+ - Base64 encoding for seamless artifact delivery
+
+ **Use Cases:**
+ - UI design prototyping and concept validation
+ - Client demonstration and stakeholder presentations
+ - Design system documentation and examples
+ - Interactive tutorial and training materials
+ - A/B testing interface variations
+ - User experience research and testing
+
+ **Professional Features:**
+ - Production-ready code quality and structure
+ - Cross-browser compatibility and standards compliance
+ - Performance-optimized implementation
+ - Maintainable and extensible code architecture
+
+ **Integration Capabilities:**
+ - Canvas viewer integration for immediate preview
+ - Downloadable HTML for offline use and sharing
+ - Framework-agnostic implementation
+ - Easy customization and extension
+
+ Returns:
+ Dictionary containing:
+ - results: Demo creation summary and success confirmation
+ - artifacts: Interactive HTML demonstration as downloadable content
+ - display: Optimized canvas viewer configuration for immediate preview
+ - Interactive elements ready for user testing and evaluation
+ Or error message if HTML generation or template loading fails
"""
# Load the HTML template
html_content = load_template("button_demo.html")
@@ -269,13 +269,13 @@ def create_iframe_demo() -> Dict[str, Any]:
return {
"results": {
"content": "Iframe demo created! An external webpage will be displayed in the canvas panel.",
- "iframe_url": "https://example.com"
+ "iframe_url": "https://www.sandia.gov/"
},
"artifacts": [],
"display": {
"open_canvas": True,
"type": "iframe",
- "url": "https://example.com",
+ "url": "https://www.sandia.gov/",
"title": "Example Website",
"sandbox": "allow-scripts allow-same-origin",
"mode": "replace"
@@ -325,10 +325,10 @@ def create_html_with_iframe() -> Dict[str, Any]:
Embedded Content Demo
This HTML artifact includes an embedded iframe showing external content:
-
diff --git a/docs/developer/canvas-renderers.md b/docs/developer/canvas-renderers.md
index 842b69e..352e3f8 100644
--- a/docs/developer/canvas-renderers.md
+++ b/docs/developer/canvas-renderers.md
@@ -1,13 +1,29 @@
# Adding Custom Canvas Renderers
-The canvas panel displays tool-generated files (PDFs, images, HTML). To add support for new file types (e.g., `.stl`, `.obj`, `.ipynb`):
+The canvas panel displays tool-generated files (PDFs, images, HTML, iframes). To add support for new file types (e.g., `.stl`, `.obj`, `.ipynb`):
## Canvas Architecture Flow
-1. Backend tool returns artifacts → stored in S3 → sends `canvas_files` WebSocket message
-2. Frontend receives file metadata (filename, s3_key, type)
-3. Frontend fetches file content from `/api/files/download/{s3_key}`
-4. `CanvasPanel` renders based on file type
+1. Backend tool returns artifacts and optional `display` hints → stored in S3 → sends `canvas_files` WebSocket message
+2. Frontend receives file metadata (filename, s3_key, type, viewer_hint/display type)
+3. Frontend fetches file content from `/api/files/download/{s3_key}` when needed
+4. `CanvasPanel` renders based on file type and viewer configuration
+
+## Built-in Viewers and Iframe Support
+
+The canvas supports several built-in viewer types, selected via the artifact `viewer` field or display configuration:
+
+- `html`: Render HTML content in an isolated, sanitized frame
+- `image`: Display images such as PNG/JPEG
+- `pdf`: Render PDF documents
+- `iframe`: Embed external content from a URL
+
+For iframe-based content, there are two primary patterns:
+
+1. **Direct iframe via `display`** – the tool sets `display.type = "iframe"` and provides a `url`, `title`, and optional `sandbox` attributes.
+2. **HTML artifact with embedded `