Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 22, 2025

MCP tools can now display external content via iframes in the canvas panel, following the v2 MCP contract spec for dashboard/visualization embedding.

Implementation

Frontend (CanvasPanel.jsx, websocketHandlers.js)

  • Added iframe file type rendering with configurable sandbox attributes
  • Configured DOMPurify to allow <iframe> tags with whitelisted attributes (src, sandbox, allow, allowfullscreen, frameborder, scrolling)
  • Handle display.type = "iframe" from v2 MCP spec to create virtual canvas files
  • Extracted IFRAME_SANITIZE_CONFIG and DEFAULT_IFRAME_SANDBOX constants

Backend (mcp/ui-demo/main.py)

  • Demo tools for both iframe approaches (direct URL and HTML with embedded iframe)

Security

  • Default sandbox: allow-scripts allow-same-origin (restrictive by default)
  • All HTML sanitized with DOMPurify before rendering
  • Tools specify custom sandbox via display.sandbox field if needed

Usage

Approach 1: Direct iframe via display config

@mcp.tool
def show_dashboard():
    return {
        "display": {
            "type": "iframe",
            "url": "https://example.com/dashboard",
            "sandbox": "allow-scripts allow-same-origin"
        }
    }

Approach 2: HTML artifact with embedded iframe

@mcp.tool
def create_page():
    html = '<iframe src="https://example.com" sandbox="allow-scripts"></iframe>'
    return {
        "artifacts": [{
            "name": "page.html",
            "b64": base64.b64encode(html.encode()).decode(),
            "viewer": "html"
        }]
    }

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • astral.sh
    • Triggering command: /usr/bin/curl curl -LsSf REDACTED (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>allow loading iframes in the canvas if requested by a mcp tools response</issue_title>
<issue_description></issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI self-assigned this Nov 22, 2025
Copilot AI and others added 6 commits November 22, 2025 02:49
- 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>
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
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>
Co-authored-by: garland3 <1162675+garland3@users.noreply.github.com>
- 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>
- 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>
Copilot AI changed the title [WIP] Allow loading iframes in the canvas based on MCP tools response Add iframe support to canvas panel for MCP tool responses Nov 22, 2025
Copilot AI requested a review from garland3 November 22, 2025 03:03
Copilot finished work on behalf of garland3 November 22, 2025 03:04
@github-actions
Copy link

🔒 Security Scan Results

Security Scan Summary

Scan Results

Python SAST (Bandit)

⚠️ Security issues found in Python code

Recommendations

  • Review all SARIF files uploaded to GitHub Security tab
  • Address high and critical severity vulnerabilities immediately
  • Run npm audit fix and pip-audit locally to fix dependencies
  • Consider implementing additional security controls

…tions

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.
@github-actions
Copy link

🔒 Security Scan Results

Security Scan Summary

Scan Results

Python SAST (Bandit)

⚠️ Security issues found in Python code

Recommendations

  • Review all SARIF files uploaded to GitHub Security tab
  • Address high and critical severity vulnerabilities immediately
  • Run npm audit fix and pip-audit locally to fix dependencies
  • Consider implementing additional security controls

@github-actions
Copy link

🔒 Security Scan Results

Security Scan Summary

Scan Results

Python SAST (Bandit)

⚠️ Security issues found in Python code

Recommendations

  • Review all SARIF files uploaded to GitHub Security tab
  • Address high and critical severity vulnerabilities immediately
  • Run npm audit fix and pip-audit locally to fix dependencies
  • Consider implementing additional security controls

@github-actions
Copy link

🔒 Security Scan Results

Security Scan Summary

Scan Results

Python SAST (Bandit)

⚠️ Security issues found in Python code

Recommendations

  • Review all SARIF files uploaded to GitHub Security tab
  • Address high and critical severity vulnerabilities immediately
  • Run npm audit fix and pip-audit locally to fix dependencies
  • Consider implementing additional security controls

@garland3 garland3 marked this pull request as ready for review November 22, 2025 15:41
Copilot AI review requested due to automatic review settings November 22, 2025 15:41
@garland3 garland3 merged commit 3939b1f into main Nov 22, 2025
11 checks passed
@garland3 garland3 deleted the copilot/allow-iframe-loading-in-canvas branch November 22, 2025 15:41
Copilot finished reviewing on behalf of garland3 November 22, 2025 15:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive iframe support to the canvas panel, enabling MCP tools to display external content such as dashboards, visualizations, and web applications. The implementation follows the v2 MCP contract specification and includes two approaches: direct iframe display via display config and HTML artifacts with embedded iframes. The changes include extensive security hardening through DOMPurify sanitization, CSP configuration, and restrictive iframe sandboxing by default.

Key Changes

  • Frontend iframe rendering with configurable sandbox attributes and DOMPurify sanitization
  • Backend support for iframe-only display configurations (no artifacts required)
  • Demo tools showcasing both iframe approaches
  • Comprehensive documentation covering CSP configuration requirements and security considerations

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
frontend/src/components/CanvasPanel.jsx Added iframe viewer type with DOMPurify config for safe HTML sanitization and canDownloadFile helper
frontend/src/handlers/chat/websocketHandlers.js Creates virtual canvas files from iframe display configs, handles open_canvas flag
frontend/src/handlers/chat/websocketHandlers.test.js Test coverage for iframe canvas file creation from display config
frontend/src/hooks/chat/useChatConfig.js Added isCanvasOpen state for programmatic canvas panel control
frontend/src/contexts/ChatContext.jsx Wired setIsCanvasOpen to websocket handlers
backend/application/chat/utilities/file_utils.py Enhanced process_tool_artifacts and notify_canvas_files_v2 to handle iframe-only displays
backend/mcp/ui-demo/main.py Added create_iframe_demo and create_html_with_iframe demo tools
backend/modules/mcp_tools/client.py Enhanced config loading to support custom config paths for testing
backend/tests/test_mcp_prompts_server.py Updated to use mcp-example-configs for isolated test configuration
backend/tests/test_mcp_prompt_override_system_prompt.py Updated to use mcp-example-configs for isolated test configuration
config/overrides/mcp.json Added ui-demo server configuration, removed external-api-example (moved to example configs)
config/overrides/mcp-test.json Deleted (servers moved to individual example configs)
config/overrides/env-var-mcp.json Deleted (moved to mcp-example-configs/mcp-env-demo.json)
config/mcp-example-configs/*.json Created individual MCP server example configurations for isolated testing
docs/developer/creating-mcp-servers.md Added iframe support section with CSP configuration requirements
docs/developer/canvas-renderers.md Documented iframe viewer type and CSP configuration for external URLs
docs/archive/03_developer_guide.md Added iframe support examples and security considerations
docs/admin/configuration.md Added Security Configuration section with CSP guidance for iframe URLs
.env.example Added CSP configuration comments and example with www.sandia.gov allowed
GEMINI.md, CLAUDE.md, .github/copilot-instructions.md Added note about mcp-example-configs location
test/backend_tests.sh Added debug output for BACKEND_DIR
Comments suppressed due to low confidence (7)

frontend/src/components/CanvasPanel.jsx:51

  • Unused variable MAX_WIDTH.
    frontend/src/contexts/ChatContext.jsx:41
  • Unused variable pendingFileEvents.
    frontend/src/components/CanvasPanel.jsx:408
  • Avoid automated semicolon insertion (94% of all statements in the enclosing function have an explicit semicolon).
    backend/modules/mcp_tools/client.py:733
  • This comment appears to contain commented-out code.
        # try:
        #     logger.info(
        #         f"get_tools_schema: requested={tool_names} matched={len(matched)} missing={missing} available_index_size={len(index)}"

backend/modules/mcp_tools/client.py:737

  • This comment appears to contain commented-out code.
        #     if missing:
        #         logger.warning(
        #             "Some requested tools were not found. This usually means discover_tools() ran before those tools were available, or the tool names contain unexpected characters. Missing: %s", missing

backend/modules/mcp_tools/client.py:740

  • This comment appears to contain commented-out code.
        # except Exception:
        #     pass

backend/modules/mcp_tools/client.py:11

  • Import of 'create_authorization_manager' is not used.
from core.auth_utils import create_authorization_manager

Comment on lines +163 to +164
# To allow https://dashboard.example.com and https://www.sandia.gov/
SECURITY_CSP_VALUE="default-src 'self'; img-src 'self' data: blob:; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-src 'self' blob: data: https://dashboard.example.com https://www.sandia.gov/; frame-ancestors 'self'"
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent URL formatting: Line 164 uses https://www.sandia.gov/ (with trailing slash) but line 163's comment refers to the same URL without a trailing slash. The CSP directive should match the actual URL used in the code examples. Consider using https://www.sandia.gov (without trailing slash) in line 164 to match the comment, or update the comment to include the trailing slash.

Copilot uses AI. Check for mistakes.
#############################################
# SPLASH_CONFIG_FILE=splash-config.json # Splash screen configuration file name
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent spacing alignment in the comment. Line 88 has extra spaces before the comment delimiter, while other lines (87, 89, 90) have a single space. For consistency, align with the pattern used in lines 87, 89, and 90.

Suggested change
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +19
print(backend_root)
project_root = backend_root.parent
print(project_root)
config_path = project_root / "config" / "mcp-example-configs" / "mcp-prompts.json"
print(config_path)
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statements should be removed before merging. The print() calls on lines 15, 17, and 19 appear to be left over from debugging and should be replaced with proper logging using the logger module if needed, or removed entirely.

Suggested change
print(backend_root)
project_root = backend_root.parent
print(project_root)
config_path = project_root / "config" / "mcp-example-configs" / "mcp-prompts.json"
print(config_path)
project_root = backend_root.parent
config_path = project_root / "config" / "mcp-example-configs" / "mcp-prompts.json"

Copilot uses AI. Check for mistakes.
# IMPORTANT: To allow external URLs in iframes (for MCP tools that use iframe display),
# add the URLs to the frame-src directive. Example:
# SECURITY_CSP_VALUE="... frame-src 'self' blob: data: https://example.com https://dashboard.example.com; ..."
# HERE the www.sandia.gov is added as an allowed iframe source.
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "HERE the www.sandia.gov is added as an allowed iframe source" is unclear and grammatically awkward. Consider rephrasing to: "In this example, https://www.sandia.gov is added as an allowed iframe source."

Suggested change
# HERE the www.sandia.gov is added as an allowed iframe source.
# In this example, https://www.sandia.gov is added as an allowed iframe source.

Copilot uses AI. Check for mistakes.
return JSON.stringify(content, null, 2)
} catch (e) {
} catch {
return String(content || '')
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use of variable 'content' always evaluates to true.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

allow loading iframes in the canvas if requested by a mcp tools response

2 participants