|
from crewai.tools import BaseTool |
|
from typing import Optional, Type, Any |
|
from pydantic import BaseModel, Field |
|
import logging |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
class SearchInput(BaseModel): |
|
"""Input schema for the search tool""" |
|
query: str = Field(..., description="The search query to find information") |
|
|
|
class DuckDuckGoSearchTool(BaseTool): |
|
"""DuckDuckGo search tool that uses MCP server""" |
|
|
|
name: str = "duckduckgo_search" |
|
description: str = "Search the web using DuckDuckGo search engine via MCP server. Use this tool to find current information about locations, attractions, restaurants, weather, and travel-related topics." |
|
args_schema: Type[BaseModel] = SearchInput |
|
|
|
def __init__(self, **kwargs): |
|
super().__init__(**kwargs) |
|
|
|
self._initialize_mcp() |
|
|
|
def _initialize_mcp(self) -> None: |
|
"""Initialize connection to MCP server and get search tool""" |
|
try: |
|
logger.info("π Initializing MCP client...") |
|
|
|
|
|
try: |
|
from smolagents.mcp_client import MCPClient |
|
|
|
self._mcp_client = MCPClient( |
|
{"url": "https://agents-mcp-hackathon-duckduckgo-mcp-server.hf.space/gradio_api/mcp/sse", |
|
"transport": "sse"} |
|
) |
|
|
|
|
|
tools = self._mcp_client.get_tools() |
|
logger.info(f"π MCP returned {len(tools) if isinstance(tools, list) else 0} tools") |
|
|
|
if isinstance(tools, list) and len(tools) > 0: |
|
|
|
self._search_tool = tools[0] |
|
logger.info("β
MCP search tool initialized successfully") |
|
else: |
|
raise Exception("No tools available from MCP server") |
|
|
|
except ImportError: |
|
logger.error("β smolagents not available") |
|
self._mcp_client = None |
|
self._search_tool = None |
|
|
|
except Exception as e: |
|
logger.error(f"β Failed to initialize MCP client: {e}") |
|
self._mcp_client = None |
|
self._search_tool = None |
|
|
|
def _run(self, query: str, **kwargs: Any) -> str: |
|
"""Execute the search using MCP DuckDuckGo tool""" |
|
try: |
|
if not hasattr(self, '_mcp_client') or not self._mcp_client or not hasattr(self, '_search_tool') or not self._search_tool: |
|
return f"β MCP server not available. Cannot search for: {query}" |
|
|
|
logger.info(f"π Executing search for: {query}") |
|
|
|
|
|
result = self._search_tool(query=query, max_results=5) |
|
logger.info(f"π Search completed, processing results...") |
|
|
|
|
|
if isinstance(result, (list, tuple)): |
|
if not result: |
|
return f"π No results found for query: {query}" |
|
|
|
formatted_results = [] |
|
for i, item in enumerate(result[:5], 1): |
|
if isinstance(item, dict): |
|
title = item.get('title', 'No title available') |
|
url = item.get('url', item.get('link', 'No URL available')) |
|
snippet = item.get('snippet', item.get('description', item.get('body', 'No description available'))) |
|
|
|
formatted_results.append( |
|
f"π Result {i}:\n" |
|
f" π Title: {title}\n" |
|
f" π URL: {url}\n" |
|
f" π Description: {snippet}\n" |
|
) |
|
else: |
|
formatted_results.append(f"π Result {i}: {str(item)}\n") |
|
|
|
final_result = "\n".join(formatted_results) |
|
logger.info(f"β
Formatted {len(formatted_results)} search results") |
|
return final_result |
|
|
|
elif isinstance(result, str): |
|
return f"π Search Result:\n{result}" |
|
else: |
|
return f"π Search completed:\n{str(result)}" |
|
|
|
except Exception as e: |
|
error_msg = f"β Search failed: {str(e)}" |
|
logger.error(error_msg) |
|
return f"{error_msg}\nπ Query: {query}" |
|
|
|
|
|
class FallbackSearchTool(BaseTool): |
|
name: str = "fallback_search" |
|
description: str = "Fallback search tool that provides general travel information" |
|
args_schema: Type[BaseModel] = SearchInput |
|
|
|
def _run(self, query: str, **kwargs: Any) -> str: |
|
"""Provide fallback search results with general travel information""" |
|
logger.info(f"π Fallback search for: {query}") |
|
|
|
|
|
fallback_info = { |
|
"vizag": { |
|
"title": "Visakhapatnam (Vizag), Andhra Pradesh, India", |
|
"info": """ |
|
## Top Attractions |
|
- **RK Beach (Ramakrishna Beach)** - Popular beach with shopping and dining |
|
- **Submarine Museum (INS Kurusura)** - Unique submarine converted to museum |
|
- **Kailasagiri Hill Park** - Scenic hilltop park with city views |
|
- **Araku Valley** - Beautiful hill station 3 hours away |
|
- **Borra Caves** - Stunning limestone caves near Araku |
|
- **Simhachalam Temple** - Ancient temple dedicated to Lord Narasimha |
|
|
|
## Local Cuisine |
|
- Andhra-style spicy seafood |
|
- Traditional South Indian breakfast items |
|
- Local street food at Beach Road |
|
|
|
## Best Time to Visit |
|
October to March (pleasant weather) |
|
|
|
## Transportation |
|
- Auto-rickshaws and taxis available |
|
- Train connectivity to Araku Valley |
|
- Airport: Visakhapatnam Airport (VTZ) |
|
""" |
|
}, |
|
"goa": { |
|
"title": "Goa, India", |
|
"info": """ |
|
## Popular Beaches |
|
- **North Goa**: Baga, Calangute, Anjuna, Vagator |
|
- **South Goa**: Palolem, Colva, Benaulim (quieter beaches) |
|
|
|
## Attractions |
|
- **Old Goa Churches** - UNESCO World Heritage Sites |
|
- **Dudhsagar Falls** - Spectacular waterfall (seasonal) |
|
- **Spice Plantations** - Guided tours available |
|
- **Flea Markets** - Anjuna and Mapusa markets |
|
|
|
## Activities |
|
- Water sports, casino cruises, dolphin watching |
|
- Portuguese heritage tours |
|
- Beach parties and nightlife |
|
|
|
## Best Time |
|
November to February (dry season) |
|
""" |
|
}, |
|
"dubai": { |
|
"title": "Dubai, United Arab Emirates", |
|
"info": """ |
|
## Iconic Attractions |
|
- **Burj Khalifa** - World's tallest building with observation decks |
|
- **Dubai Mall** - Massive shopping and entertainment complex |
|
- **Palm Jumeirah** - Artificial palm-shaped island |
|
- **Dubai Fountain** - World's largest choreographed fountain |
|
- **Burj Al Arab** - Iconic sail-shaped luxury hotel |
|
- **Dubai Marina** - Modern waterfront district |
|
|
|
## Activities |
|
- Desert safari with dune bashing |
|
- Dubai Creek dhow cruise |
|
- Ski Dubai (indoor skiing) |
|
- Gold and Spice Souks |
|
|
|
## Best Time |
|
November to March (cooler weather) |
|
""" |
|
} |
|
} |
|
|
|
|
|
query_lower = query.lower() |
|
for location, info in fallback_info.items(): |
|
if location in query_lower: |
|
return f""" |
|
# Search Results for: {query} |
|
|
|
## {info['title']} |
|
|
|
{info['info']} |
|
|
|
--- |
|
|
|
**Note**: This is fallback information as the live search service is currently unavailable. |
|
For the most current information, please verify details from official tourism websites. |
|
|
|
**Technical Note**: To enable live search, ensure MCP server is running on https://huggingface.co/spaces/Agents-MCP-Hackathon/DuckDuckGo-MCP-Server |
|
""" |
|
|
|
|
|
return f""" |
|
# Search Results for: {query} |
|
|
|
## Search Service Currently Unavailable |
|
|
|
The live search service is not currently available, but here are some general recommendations: |
|
|
|
### Planning Your Trip |
|
1. **Research official tourism websites** for your destination |
|
2. **Check travel advisories** and entry requirements |
|
3. **Compare prices** on multiple booking platforms |
|
4. **Read recent reviews** from other travelers |
|
5. **Contact local tourism boards** for current information |
|
|
|
### Popular Travel Resources |
|
- **Booking Platforms**: Booking.com, Expedia, MakeMyTrip |
|
- **Reviews**: TripAdvisor, Google Reviews |
|
- **Transportation**: Official airline and railway websites |
|
- **Local Info**: Tourism board websites |
|
|
|
--- |
|
|
|
**Technical Note**: To enable live search functionality, please ensure the MCP server is running on https://huggingface.co/spaces/Agents-MCP-Hackathon/DuckDuckGo-MCP-Server |
|
""" |
|
|
|
|
|
try: |
|
search = DuckDuckGoSearchTool() |
|
logger.info("β
DuckDuckGoSearchTool created successfully") |
|
except Exception as e: |
|
logger.error(f"β Failed to create DuckDuckGoSearchTool: {e}") |
|
search = FallbackSearchTool() |
|
logger.warning("β οΈ Using fallback search tool") |
|
|
|
|
|
def test_mcp_connection(): |
|
"""Test MCP connection independently""" |
|
try: |
|
logger.info("π§ͺ Testing MCP connection...") |
|
|
|
try: |
|
from smolagents.mcp_client import MCPClient |
|
|
|
mcp_client = MCPClient( |
|
{"url": "https://agents-mcp-hackathon-duckduckgo-mcp-server.hf.space/gradio_api/mcp/sse", |
|
"transport": "sse"} |
|
) |
|
|
|
tools = mcp_client.get_tools() |
|
logger.info(f"π§ͺ Test successful: {len(tools) if isinstance(tools, list) else 0} tools available") |
|
|
|
if isinstance(tools, list) and len(tools) > 0: |
|
|
|
search_func = tools[0] |
|
result = search_func(query="test", max_results=2) |
|
logger.info(f"π§ͺ Test search result type: {type(result)}") |
|
return True |
|
else: |
|
logger.warning("π§ͺ No tools available in MCP server") |
|
return False |
|
|
|
except ImportError: |
|
logger.error("π§ͺ smolagents package not available") |
|
return False |
|
|
|
except Exception as e: |
|
logger.error(f"π§ͺ MCP connection test failed: {e}") |
|
return False |
|
|
|
if __name__ == "__main__": |
|
print("π Testing DuckDuckGo MCP Tool") |
|
print("=" * 50) |
|
|
|
|
|
if test_mcp_connection(): |
|
print("β
MCP connection test passed") |
|
|
|
|
|
test_result = search._run("Vizag attractions") |
|
print("\nπ Tool Test Result:") |
|
print(test_result) |
|
else: |
|
print("β MCP connection test failed") |
|
print("π‘ Make sure your MCP server is running on https://huggingface.co/spaces/Agents-MCP-Hackathon/DuckDuckGo-MCP-Server") |
|
|
|
|
|
print("\nπ Testing Fallback Tool:") |
|
fallback_result = search._run("Vizag attractions") |
|
print(fallback_result) |