@mcp.tool()
async def nexus_search(
query: str,
mode: str = "general",
max_results: int = DEFAULT_MAX_RESULTS
) -> str:
"""
A hybrid search tool combining Exa's breadth and Ref's specificity.
Args:
query: The search term.
mode: 'general' for broad web search (Exa style).
'docs' to prioritize technical documentation (Ref style).
max_results: Number of results to return (1-20).
Returns:
Formatted search results with titles, URLs, and snippets.
"""
logger.info(f"Search requested - Query: '{query}', Mode: {mode}, Max results: {max_results}")
# Validate inputs
if not query or not query.strip():
error_msg = "Query cannot be empty"
logger.error(error_msg)
return f"Error: {error_msg}"
if mode not in ["general", "docs"]:
error_msg = f"Invalid mode '{mode}'. Must be 'general' or 'docs'"
logger.error(error_msg)
return f"Error: {error_msg}"
# Clamp max_results to reasonable range
max_results = max(1, min(max_results, 20))
# If 'docs' mode is selected, we modify the query to target technical sources
final_query = query.strip()
if mode == "docs":
final_query += " site:readthedocs.io OR site:github.com OR site:stackoverflow.com OR documentation API"
logger.debug(f"Enhanced query for docs mode: '{final_query}'")
results = []
try:
# Use DuckDuckGo as our free backend
with DDGS(timeout=SEARCH_TIMEOUT) as ddgs:
# Convert generator to list to properly check if empty
ddg_results = list(ddgs.text(final_query, max_results=max_results))
if not ddg_results:
logger.warning(f"No results found for query: '{query}'")
return "No results found. Try a different query or mode."
for r in ddg_results:
title = r.get('title', 'No title')
url = r.get('href', 'No URL')
snippet = r.get('body', 'No description')
results.append(f"- [Title]: {title}\n [URL]: {url}\n [Snippet]: {snippet}")
logger.info(f"Search successful - Found {len(results)} results")
return "\n\n".join(results)
except TimeoutError:
error_msg = "Search timed out. Please try again."
logger.error(f"Search timeout for query: '{query}'")
return f"Error: {error_msg}"
except Exception as e:
error_msg = f"Search failed: {str(e)}"
logger.exception(f"Unexpected error during search: {query}")
return f"Error: {error_msg}"