web_search
Gather fresh web search results using SearXNG to find current information across multiple sources for research and analysis tasks.
Instructions
Use this first to gather fresh web search results via the local SearXNG instance.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| reasoning | Yes | ||
| category | No | science | |
| max_results | No |
Input Schema (JSON Schema)
{
"properties": {
"category": {
"default": "science",
"title": "Category",
"type": "string"
},
"max_results": {
"default": 8,
"title": "Max Results",
"type": "integer"
},
"query": {
"title": "Query",
"type": "string"
},
"reasoning": {
"title": "Reasoning",
"type": "string"
}
},
"required": [
"query",
"reasoning"
],
"type": "object"
}
Implementation Reference
- src/searxng_mcp/server.py:56-99 (handler)MCP tool handler for 'web_search': performs SearXNG search, handles errors, tracks usage, and returns formatted results.@mcp.tool() async def web_search( query: Annotated[str, "Natural-language web query"], reasoning: Annotated[str, "Why you're using this tool (required for analytics)"], category: Annotated[ str, "Optional SearXNG category (general, images, news, it, science, etc.)" ] = DEFAULT_CATEGORY, max_results: Annotated[int, "How many ranked hits to return (1-10)"] = DEFAULT_MAX_RESULTS, ) -> str: """Use this first to gather fresh web search results via the local SearXNG instance.""" start_time = time.time() success = False error_msg = None result = "" try: hits = await searcher.search(query, category=category, max_results=max_results) if not hits: result = f"No results for '{query}' in category '{category}'." else: result = _format_search_hits(hits) success = True except Exception as exc: # noqa: BLE001 error_msg = str(exc) result = f"Search failed: {exc}" finally: # Track usage response_time = (time.time() - start_time) * 1000 # Convert to ms tracker.track_usage( tool_name="web_search", reasoning=reasoning, parameters={ "query": query, "category": category, "max_results": max_results, }, response_time_ms=response_time, success=success, error_message=error_msg, response_size=len(result.encode("utf-8")), ) return result
- src/searxng_mcp/search.py:34-78 (helper)Core implementation of SearxNG web search in SearxSearcher.search method, called by the web_search handler.async def search( self, query: str, *, category: str = DEFAULT_CATEGORY, max_results: int = DEFAULT_MAX_RESULTS, time_range: str | None = None, ) -> list[SearchHit]: """Return up to *max_results* hits for *query* within *category*. Args: query: Search query string category: SearXNG category (general, it, etc.) max_results: Maximum number of results to return time_range: Optional time filter (day, week, month, year) """ limit = max(1, min(max_results, MAX_SEARCH_RESULTS)) params = { "q": query, "categories": category, "format": "json", "pageno": 1, } # Add time range filter if specified if time_range: params["time_range"] = time_range async with httpx.AsyncClient(timeout=self.timeout, headers=self._headers) as client: response = await client.get(self.base_url, params=params) response.raise_for_status() payload = response.json() hits: list[SearchHit] = [] for item in payload.get("results", [])[:limit]: title = ( item.get("title") or item.get("pretty_url") or item.get("url") or "Untitled" ).strip() url = item.get("url") or "" snippet = (item.get("content") or item.get("snippet") or "").strip() snippet = clamp_text(snippet, MAX_SNIPPET_CHARS, suffix="…") if snippet else "" hits.append(SearchHit(title=title, url=url, snippet=snippet)) return hits
- src/searxng_mcp/server.py:47-54 (helper)Formats the list of SearchHit objects into a readable numbered list with titles, URLs, and snippets.def _format_search_hits(hits): lines = [] for idx, hit in enumerate(hits, 1): snippet = f"\n{hit.snippet}" if hit.snippet else "" lines.append(f"{idx}. {hit.title} — {hit.url}{snippet}") body = "\n\n".join(lines) return clamp_text(body, MAX_RESPONSE_CHARS)
- src/searxng_mcp/server.py:57-64 (schema)Input schema defined by Annotated type hints for query, reasoning, category, and max_results parameters.async def web_search( query: Annotated[str, "Natural-language web query"], reasoning: Annotated[str, "Why you're using this tool (required for analytics)"], category: Annotated[ str, "Optional SearXNG category (general, images, news, it, science, etc.)" ] = DEFAULT_CATEGORY, max_results: Annotated[int, "How many ranked hits to return (1-10)"] = DEFAULT_MAX_RESULTS, ) -> str:
- src/searxng_mcp/server.py:56-56 (registration)MCP tool registration via @mcp.tool() decorator on the web_search function.@mcp.tool()