# mcp_server.py import requests import json from bs4 import BeautifulSoup from fastmcp import FastMCP from concurrent.futures import ThreadPoolExecutor mcp = FastMCP("BatchWebContentExtraction") def scrape_single_url(url: str) -> dict: """Helper function to scrape and clean a single URL.""" try: headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') for script in soup(["script", "style", "nav", "footer", "header", "aside"]): script.decompose() raw_text = soup.get_text(separator=' ') lines = (line.strip() for line in raw_text.splitlines()) chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) clean_text = '\n'.join(chunk for chunk in chunks if chunk) return {"url": url, "status": "success", "content": clean_text[:5000]} except Exception as e: return {"url": url, "status": "failed", "content": str(e)} @mcp.tool() def batch_web_content_extraction(urls: list[str]) -> str: """ Fetches text content from a list of multiple URLs simultaneously in parallel. Returns a unified JSON string of results. """ if not urls: return json.dumps({"error": "No URLs provided"}) results = [] with ThreadPoolExecutor(max_workers=min(len(urls), 5)) as executor: futures_map = {executor.submit(scrape_single_url, url): url for url in urls} for future in futures_map: results.append(future.result()) return json.dumps(results) if __name__ == "__main__": # Use standard streamable-http protocol on port 8000 mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)