fastmcp/examples/pyscript_integration_guide.md
marvin-context-protocol[bot] 474fe542ca feat: Add PyScript browser integration demo for FastMCP
- Simple browser demo with working PyScript integration
- Comprehensive demo with advanced FastMCP tools
- Browser-compatible server example
- Complete integration guide and documentation
- Test suite for browser demo functionality

Enables FastMCP to run in browser via PyScript, allowing:
- Web page content access for LLM context
- Authenticated API calls using user cookies
- Dynamic page content updates
- Form data extraction and analysis

Addresses issue #1508 - Run In Browser

Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
2025-08-15 15:47:53 +00:00

6.9 KiB

FastMCP + PyScript Browser Integration Guide

This guide demonstrates how to run FastMCP servers in the browser using PyScript, enabling web applications to provide LLM context through the Model Context Protocol.

Use Cases

1. Chatbot Web Integration

  • Problem: Web chatbots lack access to current page content, user session, and authenticated APIs
  • Solution: FastMCP tools can read DOM, access cookies, and make authenticated requests

2. Context-Aware Web Assistants

  • Problem: AI assistants need understanding of current user context in web apps
  • Solution: MCP resources provide real-time page state, form data, and session info

3. Dynamic Content Generation

  • Problem: AI needs to update web UI based on conversation
  • Solution: MCP tools can inject content, update elements, and modify page state

Implementation Approaches

Approach 1: Pure PyScript (Demo)

# Simplified FastMCP implementation for browser
class FastMCP:
    def __init__(self, name: str):
        self.name = name
        self.tools = {}
    
    def tool(self, func):
        self.tools[func.__name__] = func
        return func

# Browser-specific tools
@mcp.tool
async def get_page_content(selector: str) -> str:
    from js import document
    element = document.querySelector(selector)
    return element.textContent if element else ""

Approach 2: Hybrid Client-Server

# Server runs FastMCP normally
# Browser client connects via WebSocket/HTTP
# Tools proxy between browser DOM and server

# Browser side
async def proxy_dom_access(selector: str) -> str:
    content = document.querySelector(selector).textContent
    return await send_to_server("dom_content", {"content": content})
# Subset of FastMCP optimized for browser
# Direct DOM integration
# Minimal dependencies

PyScript Compatibility Analysis

Compatible Components

  • Core FastMCP classes: Server, tool decorators, resource management
  • Pydantic models: For data validation and serialization
  • Basic HTTP clients: Using fetch API through JS interop
  • JSON/dict operations: Native Python data handling

Incompatible Components

  • HTTPX: Uses threading/async libraries not available in PyScript
  • Rich console: Terminal formatting not relevant in browser
  • File system operations: Browser security restrictions
  • Process management: No subprocess support in browser

Workarounds 🔧

  • Replace HTTPX with JavaScript fetch API
  • Use browser console instead of Rich
  • Store data in localStorage/sessionStorage instead of files
  • Use Web Workers for concurrent operations

Browser Security Considerations

Same-Origin Policy

@mcp.tool
async def make_request(url: str) -> str:
    # Must respect CORS policies
    # Can only access same-origin or CORS-enabled endpoints
    response = await fetch(url, {"credentials": "include"})
    return await response.text()
@mcp.tool  
async def get_auth_context() -> dict:
    # Can access cookies for same domain
    # Enables authenticated API calls
    from js import document
    return {"cookies": document.cookie}

DOM Manipulation

@mcp.tool
async def update_ui(selector: str, content: str) -> str:
    # Full DOM access within page
    # Can modify any page element
    element = document.querySelector(selector)
    element.innerHTML = content
    return "Updated successfully"

Integration Examples

Example 1: E-commerce Assistant

@mcp.tool
async def get_cart_items() -> list:
    """Read current shopping cart contents"""
    cart_elements = document.querySelectorAll('.cart-item')
    return [item.textContent for item in cart_elements]

@mcp.tool
async def get_product_details() -> dict:
    """Extract current product information"""
    return {
        "name": document.querySelector('.product-name').textContent,
        "price": document.querySelector('.price').textContent,
        "availability": document.querySelector('.stock-status').textContent
    }

Example 2: Dashboard Analytics

@mcp.tool
async def get_dashboard_metrics() -> dict:
    """Read current dashboard metrics"""
    return {
        "revenue": document.querySelector('#revenue').textContent,
        "users": document.querySelector('#active-users').textContent,
        "conversion": document.querySelector('#conversion-rate').textContent
    }

@mcp.tool
async def update_dashboard_filter(date_range: str) -> str:
    """Update dashboard date filter"""
    filter_select = document.querySelector('#date-filter')
    filter_select.value = date_range
    filter_select.dispatchEvent(Event('change'))
    return f"Updated filter to {date_range}"

Example 3: Form Assistant

@mcp.tool
async def analyze_form_fields() -> dict:
    """Analyze current form state and validation"""
    form = document.querySelector('form')
    fields = {}
    
    for input_elem in form.querySelectorAll('input, select, textarea'):
        fields[input_elem.name] = {
            "value": input_elem.value,
            "valid": input_elem.checkValidity(),
            "required": input_elem.required
        }
    
    return fields

@mcp.tool
async def fill_form_field(field_name: str, value: str) -> str:
    """Fill a form field with provided value"""
    field = document.querySelector(f'[name="{field_name}"]')
    if field:
        field.value = value
        field.dispatchEvent(Event('input'))
        return f"Filled {field_name} with {value}"
    return f"Field {field_name} not found"

Performance Considerations

Memory Management

  • PyScript runs in browser memory constraints
  • Limit large data processing in tools
  • Use streaming for large responses

Async Operations

  • All browser APIs are async
  • FastMCP tools should use async/await
  • Consider request timeouts

Bundle Size

  • PyScript loads full Python runtime
  • Minimize dependencies
  • Consider lazy loading for complex tools

Getting Started

  1. Create HTML page with PyScript CDN
  2. Define FastMCP server with browser-specific tools
  3. Test DOM access and API integration
  4. Add error handling for browser-specific issues
  5. Deploy and test in target browsers

Deployment Options

Static Hosting

  • Deploy HTML + PyScript to CDN
  • No server required for basic functionality
  • Suitable for client-side only features

Hybrid Architecture

  • FastMCP server for heavy processing
  • Browser client for UI interaction
  • WebSocket connection between them

Progressive Enhancement

  • Start with basic web functionality
  • Add MCP features as enhancement layer
  • Graceful degradation if PyScript fails

Future Possibilities

  • WebAssembly integration: Faster Python execution
  • Service Worker: Background MCP processing
  • Web Components: Reusable MCP-enabled UI elements
  • Extension APIs: Browser extension integration