🌐 FastMCP + PyScript

Browser-based MCP Server Demo

Making web content accessible to LLMs through the Model Context Protocol

🎯 Use Case: Chatbot Web Integration

This demonstrates how a chatbot integrated into a web application can access page content, user session data, and make authenticated API calls using FastMCP.

📄 Sample Web Application Content

User: john.doe@example.com

Current Page: Dashboard

Last Updated: Loading...

Session ID: abc123def456

🛠 MCP Tools Demo

Click buttons below to test MCP tools that can access web page content:

FastMCP tools ready. Click buttons above to test functionality.

💡 How This Enables LLM Context

packages = [] import asyncio from js import document, console, JSON, fetch, Date import json import time from pyodide.ffi import create_proxy # Lightweight FastMCP implementation for browser class BrowserMCP: def __init__(self, name: str): self.name = name self.tools = {} def tool(self, func): """Register a tool function""" self.tools[func.__name__] = func return func async def call_tool(self, name: str, **kwargs): """Call a registered tool""" if name in self.tools: try: return await self.tools[name](**kwargs) except Exception as e: return f"Error in tool {name}: {str(e)}" else: return f"Tool {name} not found" # Create MCP server mcp = BrowserMCP("Web Context Server") @mcp.tool async def read_page_content(selector: str = "body") -> dict: """Read content from the current web page""" try: if selector == "body": title = document.title url = document.location.href text_content = document.body.textContent[:500] # Limit content return { "title": title, "url": url, "content_preview": text_content, "success": True } else: element = document.querySelector(selector) if element: return { "selector": selector, "content": element.textContent, "html": element.innerHTML, "success": True } else: return {"error": f"Element not found: {selector}", "success": False} except Exception as e: return {"error": str(e), "success": False} @mcp.tool async def get_user_context() -> dict: """Extract user context from the current page""" try: username = document.getElementById("username").textContent if document.getElementById("username") else "Unknown" session_id = document.getElementById("session-id").textContent if document.getElementById("session-id") else "None" theme = document.getElementById("theme-selector").value if document.getElementById("theme-selector") else "light" return { "username": username, "session_id": session_id, "theme": theme, "page_title": document.title, "url": document.location.href, "user_agent": document.navigator.userAgent, "timestamp": Date.new().toISOString(), "success": True } except Exception as e: return {"error": str(e), "success": False} @mcp.tool async def extract_form_data() -> dict: """Extract data from forms on the page""" try: form_data = {} # Get theme selector theme_selector = document.getElementById("theme-selector") if theme_selector: form_data["theme"] = theme_selector.value # Get any input elements inputs = document.querySelectorAll("input") for i in range(inputs.length): input_elem = inputs[i] if input_elem.name: form_data[input_elem.name] = input_elem.value return { "form_data": form_data, "form_count": len(form_data), "success": True } except Exception as e: return {"error": str(e), "success": False} @mcp.tool async def simulate_api_call(endpoint: str = "/api/user/profile") -> dict: """Simulate making an authenticated API call""" try: # In real implementation, this would use: # response = await fetch(endpoint, {"credentials": "include"}) # For demo, we simulate the response simulated_response = { "endpoint": endpoint, "method": "GET", "status": 200, "data": { "user_id": "12345", "email": "john.doe@example.com", "preferences": { "theme": "light", "notifications": True }, "last_login": "2024-01-15T10:30:00Z" }, "cookies_included": True, "success": True } return simulated_response except Exception as e: return {"error": str(e), "success": False} @mcp.tool async def update_page_element(element_id: str, new_content: str) -> dict: """Update content of a page element""" try: element = document.getElementById(element_id) if element: element.textContent = new_content return { "element_id": element_id, "new_content": new_content, "success": True } else: return {"error": f"Element with ID '{element_id}' not found", "success": False} except Exception as e: return {"error": str(e), "success": False} def log_output(message): """Helper to display output""" output_elem = document.getElementById("tool-output") if isinstance(message, dict): message = json.dumps(message, indent=2) output_elem.textContent = str(message) async def demo_page_reading(): """Demo reading page content""" result = await mcp.call_tool("read_page_content") log_output(f"📖 Page Content Tool Result:\n{json.dumps(result, indent=2)}") async def demo_user_context(): """Demo getting user context""" result = await mcp.call_tool("get_user_context") log_output(f"👤 User Context Tool Result:\n{json.dumps(result, indent=2)}") async def demo_form_data(): """Demo extracting form data""" result = await mcp.call_tool("extract_form_data") log_output(f"📝 Form Data Tool Result:\n{json.dumps(result, indent=2)}") async def demo_api_simulation(): """Demo API call simulation""" result = await mcp.call_tool("simulate_api_call", endpoint="/api/dashboard/stats") log_output(f"🌐 API Call Tool Result:\n{json.dumps(result, indent=2)}") async def demo_update_content(): """Demo updating page content""" new_time = Date.new().toLocaleTimeString() result = await mcp.call_tool("update_page_element", element_id="timestamp", new_content=new_time) log_output(f"✏️ Update Content Tool Result:\n{json.dumps(result, indent=2)}") # Create proxies for event handlers demo_page_reading_proxy = create_proxy(demo_page_reading) demo_user_context_proxy = create_proxy(demo_user_context) demo_form_data_proxy = create_proxy(demo_form_data) demo_api_simulation_proxy = create_proxy(demo_api_simulation) demo_update_content_proxy = create_proxy(demo_update_content) # Make functions available globally globals()['demo_page_reading'] = demo_page_reading_proxy globals()['demo_user_context'] = demo_user_context_proxy globals()['demo_form_data'] = demo_form_data_proxy globals()['demo_api_simulation'] = demo_api_simulation_proxy globals()['demo_update_content'] = demo_update_content_proxy # Initialize def update_timestamp(): timestamp_elem = document.getElementById("timestamp") if timestamp_elem: timestamp_elem.textContent = Date.new().toLocaleString() update_timestamp() console.log("FastMCP Browser Demo initialized successfully!")