Skip to main content
Glama
tisDDM

SearXNG MCP Server

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
NameRequiredDescriptionDefault
categoriesNoCategories to search in (e.g., 'general', 'images', 'news'). Default: null (all categories).
enginesNoSpecific search engines to use. Default: null (all available engines).
languageNoLanguage code for search results (e.g., 'en', 'de', 'fr'). Default: 'en'en
max_resultsNoMaximum number of search results to return. Range: 1-50. Default: 10.
pagenoNoPage number for results. Must be minimum 1. Default: 1.
queryYesSearch query
safesearchNoSafe search level: 0 (off), 1 (moderate), 2 (strict). Default: 1 (moderate).
time_rangeNoTime 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 };
    });
  • 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"]
    }
  • 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,
          };
        }
      });
    }
  • 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");
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions 'privacy-respecting' and 'returns relevant web content,' but doesn't disclose key behavioral traits like rate limits, authentication needs, error handling, or what 'relevant' entails (e.g., ranking, source diversity). For a search tool with no annotation coverage, this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded, stating the core purpose in the first sentence. The second sentence adds useful context about returns and parameters without redundancy. It could be slightly more structured (e.g., bullet points), but it's efficient with zero waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (8 parameters, no output schema, no annotations), the description is adequate but incomplete. It covers the basic purpose and hints at behavior, but lacks details on output format, error cases, or operational constraints. Without annotations or output schema, more context would help the agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all 8 parameters. The description adds minimal value beyond the schema by mentioning 'customizable parameters' but doesn't provide additional semantics or context for parameter usage. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool performs web searches using SearXNG, specifying it's a privacy-respecting metasearch engine. It distinguishes the tool by mentioning its privacy focus and customizable parameters, though without siblings, differentiation isn't needed. However, it could be more specific about the exact verb and resource (e.g., 'search the web' rather than 'perform web searches').

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, prerequisites, or contextual constraints. It mentions 'customizable parameters' but doesn't explain when specific settings are appropriate. Without siblings, this is less critical, but it still lacks usage context for the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tisDDM/searxng-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server