searxngsearch
Conduct web searches while respecting privacy with customizable parameters like language, time range, and categories. Returns relevant results tailored to your query.
Instructions
Perform web searches using SearXNG, a privacy-respecting metasearch engine. Returns relevant web content with customizable parameters.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| categories | No | Categories to search in (e.g., 'general', 'images', 'news'). Default: null (all categories). | |
| engines | No | Specific search engines to use. Default: null (all available engines). | |
| language | No | Language code for search results (e.g., 'en', 'de', 'fr'). Default: 'en' | en |
| max_results | No | Maximum number of search results to return. Range: 1-50. Default: 10. | |
| pageno | No | Page number for results. Must be minimum 1. Default: 1. | |
| query | Yes | Search query | |
| safesearch | No | Safe search level: 0 (off), 1 (moderate), 2 (strict). Default: 1 (moderate). | |
| time_range | No | Time range for search results. Options: 'day', 'week', 'month', 'year'. Default: null (no time restriction). |
Implementation Reference
- src/index.ts:155-216 (registration)Tool registration in ListToolsRequestSchema handler, defining the searxngsearch tool with name, description, and input schema.this.server.setRequestHandler(ListToolsRequestSchema, async () => { // Define available tools const tools: Tool[] = [ { name: "searxngsearch", description: "Perform web searches using SearXNG, a privacy-respecting metasearch engine. Returns relevant web content with customizable parameters.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query" }, language: { type: "string", description: "Language code for search results (e.g., 'en', 'de', 'fr'). Default: 'en'", default: "en" }, time_range: { type: "string", enum: ["day", "week", "month", "year"], description: "Time range for search results. Options: 'day', 'week', 'month', 'year'. Default: null (no time restriction).", default: null }, categories: { type: "array", items: { type: "string" }, description: "Categories to search in (e.g., 'general', 'images', 'news'). Default: null (all categories).", default: null }, engines: { type: "array", items: { type: "string" }, description: "Specific search engines to use. Default: null (all available engines).", default: null }, safesearch: { type: "number", enum: [0, 1, 2], description: "Safe search level: 0 (off), 1 (moderate), 2 (strict). Default: 1 (moderate).", default: 1 }, pageno: { type: "number", description: "Page number for results. Must be minimum 1. Default: 1.", minimum: 1, default: 1 }, max_results: { type: "number", description: "Maximum number of search results to return. Range: 1-50. Default: 10.", default: 10, minimum: 1, maximum: 50 } }, required: ["query"] } } ]; return { tools }; });
- src/index.ts:162-212 (schema)Input schema for searxngsearch tool, specifying parameters like query (required), language, time_range, categories, engines, safesearch, pageno, and max_results with types, enums, defaults, and constraints.type: "object", properties: { query: { type: "string", description: "Search query" }, language: { type: "string", description: "Language code for search results (e.g., 'en', 'de', 'fr'). Default: 'en'", default: "en" }, time_range: { type: "string", enum: ["day", "week", "month", "year"], description: "Time range for search results. Options: 'day', 'week', 'month', 'year'. Default: null (no time restriction).", default: null }, categories: { type: "array", items: { type: "string" }, description: "Categories to search in (e.g., 'general', 'images', 'news'). Default: null (all categories).", default: null }, engines: { type: "array", items: { type: "string" }, description: "Specific search engines to use. Default: null (all available engines).", default: null }, safesearch: { type: "number", enum: [0, 1, 2], description: "Safe search level: 0 (off), 1 (moderate), 2 (strict). Default: 1 (moderate).", default: 1 }, pageno: { type: "number", description: "Page number for results. Must be minimum 1. Default: 1.", minimum: 1, default: 1 }, max_results: { type: "number", description: "Maximum number of search results to return. Range: 1-50. Default: 10.", default: 10, minimum: 1, maximum: 50 } }, required: ["query"] }
- src/index.ts:218-311 (handler)Core handler logic for executing searxngsearch: validates input, prepares parameters, calls SearXNG /search endpoint via axios, limits results, formats as JSON response, handles errors including auth and API failures.this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { if (request.params.name !== "searxngsearch") { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } const args = request.params.arguments ?? {}; // Validate required parameters if (!args.query || typeof args.query !== 'string') { throw new McpError( ErrorCode.InvalidParams, "Query parameter is required and must be a string" ); } // Prepare search parameters with defaults const searchParams: SearchParams = { q: args.query, format: 'json', language: typeof args.language === 'string' ? args.language : 'en', safesearch: typeof args.safesearch === 'number' ? args.safesearch : 1, pageno: typeof args.pageno === 'number' ? args.pageno : 1, }; // Add optional parameters if provided if (args.time_range && typeof args.time_range === 'string') searchParams.time_range = args.time_range; if (Array.isArray(args.categories)) searchParams.categories = args.categories; if (Array.isArray(args.engines)) searchParams.engines = args.engines; console.error(`[SearXNG] Searching for: ${args.query}`); // Make request to SearXNG const response = await this.axiosInstance.get('/search', { params: searchParams }); const searchResults: SearXNGResponse = response.data; // Limit results if max_results is specified const maxResults = typeof args.max_results === 'number' ? args.max_results : 10; const limitedResults = searchResults.results.slice(0, maxResults); // Construct a new response object with the limited results const finalResponse: SearXNGResponse = { ...searchResults, // Copy other fields like query, answers, suggestions etc. results: limitedResults, // Use the truncated results list number_of_results: limitedResults.length // Update the count to reflect the truncation }; // Return the modified JSON data return { content: [{ type: "text", text: JSON.stringify(finalResponse, null, 2) }] }; } catch (error: any) { console.error("[SearXNG Error]", error); if (axios.isAxiosError(error)) { // Handle authentication errors if (error.response?.status === 401) { return { content: [{ type: "text", text: "Authentication failed. Please check your SearXNG username and password." }], isError: true, }; } return { content: [{ type: "text", text: `SearXNG API error: ${error.response?.data?.message ?? error.message}` }], isError: true, }; } return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true, }; } }); }
- src/index.ts:28-70 (helper)Helper function to fetch public list of SearXNG instances and select a random standard (non-onion/hidden) instance for use when no SEARXNG_URL is provided.async function getRandomSearXNGInstance(): Promise<string> { try { console.error("[SearXNG] Fetching list of SearXNG instances..."); const response = await axios.get(INSTANCES_LIST_URL); const instancesData = parse(response.data); // Debug the structure console.error("[SearXNG] Instances data structure:", Object.keys(instancesData)); // Filter for standard internet instances (not onion or hidden) const standardInstances: string[] = []; // The instances.yml file has a structure where each key is a URL for (const [url, data] of Object.entries(instancesData)) { const instanceData = data as any; // Check if it's a standard instance (not hidden or onion) if ( instanceData && (!instanceData.comments || (!instanceData.comments.includes("hidden") && !instanceData.comments.includes("onion"))) && (!instanceData.network_type || instanceData.network_type === "normal") ) { standardInstances.push(url); } } console.error(`[SearXNG] Found ${standardInstances.length} standard instances`); if (standardInstances.length === 0) { throw new Error("No standard SearXNG instances found"); } // Select a random instance const randomInstance = standardInstances[Math.floor(Math.random() * standardInstances.length)]; console.error(`[SearXNG] Selected random instance: ${randomInstance}`); return randomInstance; } catch (error) { console.error("[SearXNG] Error fetching instances:", error); throw new Error("Failed to fetch SearXNG instances list"); } }