mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-09-01 12:03:19 +02:00
- Create working PyScript demos using actual FastMCP patterns (not mocks)
- Add browser-compatible MCP server with DOM access and session tools
- Include comprehensive integration guide and test suite
- Enable web applications to provide LLM context through MCP
🤖 Generated with Claude Code
Co-authored-by: William Easton <strawgate@users.noreply.github.com>
8.9 KiB
8.9 KiB
FastMCP Browser Integration with PyScript
This guide demonstrates how to integrate FastMCP with PyScript to run MCP servers directly in web browsers, enabling rich web application context for LLMs.
Overview
The FastMCP browser integration allows you to:
- Provide Web Context: Give LLMs access to current page content, DOM state, and user interactions
- Authenticated Operations: Use the user's existing session and cookies for API calls
- Real-time Updates: Dynamically update page content based on LLM responses
- Seamless Integration: Embed MCP functionality directly into web applications
Use Cases
1. Context-Aware Chatbots
<!-- Chatbot that understands the current page -->
<script type="py">
@mcp.tool
def get_page_context() -> dict:
"""Extract current page context for chatbot"""
return {
"title": document.title,
"content": document.body.innerText[:1000],
"form_data": extract_form_data(),
"user_state": get_user_session_data()
}
</script>
2. Dynamic Content Generation
<!-- Tools that can modify page content -->
<script type="py">
@mcp.tool
def update_page_content(element_id: str, new_content: str) -> bool:
"""Update page element based on LLM response"""
element = document.getElementById(element_id)
if element:
element.innerHTML = new_content
return True
return False
</script>
3. Authenticated API Integration
<!-- Make API calls using user's session -->
<script type="py">
@mcp.tool
async def fetch_user_data(endpoint: str) -> dict:
"""Fetch data using user's authentication"""
response = await fetch(endpoint, {
'credentials': 'include', # Include cookies
'headers': get_auth_headers()
})
return await response.json()
</script>
Implementation Guide
1. Basic Setup
Create an HTML file with PyScript and FastMCP:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/releases/2025.8.1/core.css">
<script type="module" src="https://pyscript.net/releases/2025.8.1/core.js"></script>
</head>
<body>
<py-config>
packages = ["fastmcp", "pydantic"]
</py-config>
<script type="py" src="mcp_server.py"></script>
</body>
</html>
2. Create MCP Tools
Define browser-specific MCP tools in mcp_server.py:
from fastmcp import FastMCP
from pyscript import document, window
mcp = FastMCP("Browser MCP Server")
@mcp.tool
def analyze_page_content() -> dict:
"""Analyze current web page for LLM context"""
return {
"title": document.title,
"url": str(window.location.href),
"text_content": document.body.innerText,
"links": [a.href for a in document.querySelectorAll("a[href]")],
"forms": analyze_forms(),
"meta_data": extract_meta_tags()
}
@mcp.tool
def get_user_session() -> dict:
"""Extract user session information"""
return {
"logged_in": check_auth_state(),
"user_data": extract_user_data(),
"preferences": get_user_preferences(),
"session_id": get_session_identifier()
}
@mcp.tool
def make_authenticated_request(url: str, method: str = "GET") -> dict:
"""Make API request with user's authentication"""
# Use fetch API with credentials: 'include'
return make_request_with_cookies(url, method)
3. Browser-Specific Features
DOM Access
@mcp.tool
def get_form_data() -> dict:
"""Extract all form data from the page"""
forms = {}
for form in document.querySelectorAll("form"):
form_data = {}
for input_elem in form.querySelectorAll("input, select, textarea"):
if input_elem.name:
form_data[input_elem.name] = input_elem.value
forms[form.id or form.action or "unnamed"] = form_data
return forms
Local Storage Integration
@mcp.tool
def store_conversation_context(context: dict) -> bool:
"""Store conversation context in browser storage"""
from js import localStorage
import json
try:
localStorage.setItem("mcp_context", json.dumps(context))
return True
except Exception:
return False
@mcp.tool
def get_stored_context() -> dict:
"""Retrieve stored conversation context"""
from js import localStorage
import json
try:
stored = localStorage.getItem("mcp_context")
return json.loads(stored) if stored else {}
except Exception:
return {}
Cookie and Session Access
@mcp.tool
def get_session_info() -> dict:
"""Get browser session information"""
from js import navigator, location
return {
"user_agent": str(navigator.userAgent),
"language": str(navigator.language),
"current_url": str(location.href),
"referrer": str(document.referrer),
"cookies_enabled": bool(navigator.cookieEnabled)
}
Compatibility Considerations
PyScript Environment
- Limited Packages: Not all Python packages work in PyScript/Pyodide
- Async Handling: Use PyScript's async capabilities for non-blocking operations
- Memory Constraints: Browser environments have memory limitations
FastMCP Adaptations
For full compatibility, create a browser-compatible FastMCP wrapper:
class BrowserFastMCP:
"""Browser-optimized FastMCP implementation"""
def __init__(self, name: str):
self.name = name
self.tools = {}
self.resources = {}
def tool(self, func):
"""Register tool with browser-safe execution"""
self.tools[func.__name__] = func
return func
def call_tool(self, name: str, args: dict = None):
"""Execute tool with error handling"""
try:
return self.tools[name](**(args or {}))
except Exception as e:
return {"error": str(e)}
Security Considerations
Same-Origin Policy
- Browser security restrictions apply
- Cross-origin requests need proper CORS headers
- Local file access is limited
Data Privacy
- Be mindful of sensitive data in page content
- Implement proper sanitization for user inputs
- Consider privacy implications of context extraction
Authentication
@mcp.tool
def make_secure_request(url: str, data: dict = None) -> dict:
"""Make authenticated request with proper security"""
# Validate URL is allowed
if not is_allowed_domain(url):
return {"error": "Domain not allowed"}
# Include CSRF protection
headers = {
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/json"
}
# Get CSRF token from page
csrf_token = get_csrf_token()
if csrf_token:
headers["X-CSRF-Token"] = csrf_token
return make_request(url, headers, data)
Complete Example
See examples/browser_pyscript_demo.html for a full working demonstration that includes:
- ✅ Real FastMCP integration (not mock)
- 🌐 DOM content analysis
- 👤 User session extraction
- 💾 Local storage tools
- 🔧 Browser API access
- 📊 Live status updates
Running the Demo
- Clone the FastMCP repository
- Open
examples/browser_pyscript_demo.htmlin a modern web browser - The demo will automatically initialize and show available tools
- Click the buttons to test different MCP capabilities
Integration Patterns
For Existing Web Apps
// Initialize MCP integration
window.initializeMCP = async function(config) {
// Load PyScript dynamically
await loadPyScript();
// Initialize MCP server
await pyodide.runPython(`
mcp = FastMCP("${config.serverName}")
# Add your tools here
`);
// Create interface for your app
window.mcpTools = {
analyzeContent: () => pyodide.runPython("mcp.call_tool('analyze_page_content')"),
getUserContext: () => pyodide.runPython("mcp.call_tool('get_user_context')")
};
};
For Chat Interfaces
@mcp.tool
def get_chat_context() -> dict:
"""Get context for chat interface"""
return {
"page_title": document.title,
"page_content": get_relevant_content(),
"user_inputs": get_recent_user_inputs(),
"conversation_history": get_stored_conversation(),
"user_preferences": get_user_settings()
}
@mcp.tool
def update_chat_ui(message: str, sender: str) -> bool:
"""Update chat interface with new message"""
chat_container = document.getElementById("chat-messages")
if chat_container:
message_elem = document.createElement("div")
message_elem.className = f"message {sender}"
message_elem.textContent = message
chat_container.appendChild(message_elem)
chat_container.scrollTop = chat_container.scrollHeight
return True
return False
This integration enables powerful web-native MCP servers that can provide rich context to LLMs while maintaining the security and capabilities of the browser environment.