#!/usr/bin/env python3
"""
ddg-mcp4 MCP Server
An awesome MCP generated by AI
"""
import argparse
import asyncio
import logging
import os
from typing import Dict, Any, List, Optional
from urllib.parse import urlparse
import httpx
from fastmcp import FastMCP
from bs4 import BeautifulSoup
from duckduckgo_search import DDGS
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Initialize FastMCP
mcp = FastMCP("ddg-mcp4 MCP Server")
@mcp.tool()
async def duckduckgo_scrape(url: str) -> Dict[str, Any]:
"""
Scrape content from a website given its URL using DuckDuckGo.
Args:
url: The URL to scrape
Returns:
Dictionary with the scraped content
"""
try:
# Parse the URL
parsed_url = urlparse(url)
if not parsed_url.scheme or not parsed_url.netloc:
raise ValueError("Invalid URL format")
# Use DuckDuckGo to search for the URL
with DDGS() as ddgs:
results = [r for r in ddgs.text(url, max_results=1)]
if not results:
return {
"success": False,
"url": url,
"error": "No results found"
}
# Fetch the page content
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.get(url)
response.raise_for_status()
# Parse the HTML content
soup = BeautifulSoup(response.text, 'html.parser')
# Remove script and style elements
for script in soup(["script", "style"]):
script.decompose()
# Extract text
text = soup.get_text()
# Break into lines and remove leading and trailing space on each
lines = (line.strip() for line in text.splitlines())
# Break multi-headlines into a line each
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
# Drop blank lines
text = '\n'.join(chunk for chunk in chunks if chunk)
return {
"success": True,
"url": url,
"title": results[0]['title'] if results else "",
"content": text[:1000] if len(text) < 1000 else text[:1000] + "...",
"full_content_length": len(text)
}
except Exception as e:
logger.error(f"Failed to scrape {url}: {e}")
return {
"success": False,
"url": url,
"error": str(e)
}
@mcp.tool()
async def get_server_info() -> Dict[str, Any]:
"""
Get information about this MCP server.
Returns:
Server information including name and available tools
"""
return {
"name": "ddg-mcp4",
"version": "1.0.0",
"description": "An awesome MCP generated by AI",
"author": "AI Generator",
"framework": "FastMCP",
"tools": [
"duckduckgo_scrape",
"get_server_info"
],
"features": [
"HTTP and stdio transport support",
"Async/await support",
"Type hints",
"Error handling",
"Logging"
]
}
def main():
"""Main entry point for the MCP server"""
parser = argparse.ArgumentParser(description="ddg-mcp4 MCP Server")
parser.add_argument("--transport", default="stdio", help="Transport type (stdio, streamable-http)")
parser.add_argument("--host", default="localhost", help="Host for HTTP transport")
parser.add_argument("--port", type=int, default=8080, help="Port for HTTP transport")
args = parser.parse_args()
logger.info(f"π Starting ddg-mcp4 MCP Server...")
logger.info(f"π‘ Transport: {args.transport}")
if args.transport == "streamable-http":
logger.info(f"π Host: {args.host}")
logger.info(f"π Port: {args.port}")
logger.info(f"π Server URL: http://{args.host}:{args.port}/mcp")
mcp.run(transport=args.transport, host=args.host, port=args.port)
else:
logger.info(f"π Using stdio transport for direct integration")
mcp.run(transport=args.transport)
if __name__ == "__main__":
main()