Skip to main content
Glama
OEvortex

DuckDuckGo Search MCP

by OEvortex

iask-search

Get AI-generated answers to complex questions using web content. Choose search modes for academic, forum, wiki, or deep analysis responses with adjustable detail levels.

Instructions

AI-powered search using IAsk.ai. Retrieves comprehensive, AI-generated responses based on web content. Supports different search modes (question, academic, forums, wiki, thinking) and detail levels (concise, detailed, comprehensive). Ideal for getting well-researched answers to complex questions.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesThe search query or question to ask. Supports natural language questions for comprehensive AI-generated responses.
modeNoSearch mode to use. Options: "question" (general questions), "academic" (scholarly/research), "forums" (community discussions), "wiki" (encyclopedia-style), "thinking" (deep analysis). Default is "question".question
detailLevelNoLevel of detail in the response. Options: "concise" (brief), "detailed" (moderate), "comprehensive" (extensive). Default is null (standard response).

Implementation Reference

  • The main handler function `iaskToolHandler` that destructures input params, calls the `searchIAsk` helper, formats the response as MCP content, and handles errors.
    export async function iaskToolHandler(params) {
      const { 
        query, 
        mode = 'thinking', 
        detailLevel = null
      } = params;
      
      console.log(`Searching IAsk AI for: "${query}" (mode: ${mode}, detailLevel: ${detailLevel || 'default'})`);
      
      try {
        const response = await searchIAsk(query, mode, detailLevel);
        
        return {
          content: [
            {
              type: 'text',
              text: response || 'No results found.'
            }
          ]
        };
      } catch (error) {
        console.error(`Error in IAsk search: ${error.message}`);
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Error searching IAsk: ${error.message}`
            }
          ]
        };
      }
    }
  • The `iaskToolDefinition` object defining the tool's metadata, description, input schema for validation (query required, optional mode and detailLevel with enums), and annotations.
    export const iaskToolDefinition = {
      name: 'iask-search',
      title: 'IAsk AI Search',
      description: 'AI-powered search using IAsk.ai. Retrieves comprehensive, AI-generated responses based on web content. Supports different search modes (question, academic, forums, wiki, thinking) and detail levels (concise, detailed, comprehensive). Ideal for getting well-researched answers to complex questions.',
      inputSchema: {
        type: 'object',
        properties: {
          query: {
            type: 'string',
            description: 'The search query or question to ask. Supports natural language questions for comprehensive AI-generated responses.'
          },
          mode: {
            type: 'string',
            description: 'Search mode to use. Options: "question" (general questions), "academic" (scholarly/research), "forums" (community discussions), "wiki" (encyclopedia-style), "thinking" (deep analysis). Default is "question".',
            enum: VALID_MODES,
            default: 'question'
          },
          detailLevel: {
            type: 'string',
            description: 'Level of detail in the response. Options: "concise" (brief), "detailed" (moderate), "comprehensive" (extensive). Default is null (standard response).',
            enum: VALID_DETAIL_LEVELS
          }
        },
        required: ['query']
      },
      annotations: {
        readOnlyHint: true,
        openWorldHint: false
      }
    };
  • src/index.js:35-40 (registration)
    Registration in MCP ListToolsRequest handler: includes iaskToolDefinition in the availableTools array returned to clients.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      console.log('Tools list requested, returning:', availableTools.length, 'tools');
      return {
        tools: availableTools
      };
    });
  • src/index.js:49-61 (registration)
    Registration in MCP CallToolRequest handler: switch case routes 'iask-search' calls to the iaskToolHandler.
    switch (name) {
      case 'web-search':
        return await searchToolHandler(args);
    
      case 'iask-search':
        return await iaskToolHandler(args);
    
      case 'monica-search':
        return await monicaToolHandler(args);
    
      default:
        throw new Error(`Tool not found: ${name}`);
    }
  • The `searchIAsk` helper function performs the actual search via WebSocket to iask.ai, handles caching, parsing, formatting of AI-generated responses, and exports VALID_MODES/VALID_DETAIL_LEVELS used in schema.
    async function searchIAsk(prompt, mode = 'thinking', detailLevel = null) {
      // Input validation
      if (!prompt || typeof prompt !== 'string') {
        throw new Error('Invalid prompt: prompt must be a non-empty string');
      }
    
      // Validate mode
      if (!VALID_MODES.includes(mode)) {
        throw new Error(`Invalid mode: ${mode}. Valid modes are: ${VALID_MODES.join(', ')}`);
      }
    
      // Validate detail level
      if (detailLevel && !VALID_DETAIL_LEVELS.includes(detailLevel)) {
        throw new Error(`Invalid detail level: ${detailLevel}. Valid levels are: ${VALID_DETAIL_LEVELS.join(', ')}`);
      }
    
      console.log(`IAsk search starting: "${prompt}" (mode: ${mode}, detailLevel: ${detailLevel || 'default'})`);
    
      // Clear old cache entries
      clearOldCache();
    
      const cacheKey = getCacheKey(prompt, mode, detailLevel);
      const cachedResults = resultsCache.get(cacheKey);
    
      if (cachedResults && Date.now() - cachedResults.timestamp < CACHE_DURATION) {
        console.log(`Cache hit for IAsk query: "${prompt}"`);
        return cachedResults.results;
      }
    
      // Build URL parameters
      const params = new URLSearchParams({ mode, q: prompt });
      if (detailLevel) {
        params.append('options[detail_level]', detailLevel);
      }
    
      // Create a cookie jar for session management
      const jar = new CookieJar();
      const client = wrapper(axios.create({ jar }));
    
      try {
        // Get initial page and extract tokens
        console.log('Fetching IAsk AI initial page...');
        const response = await client.get(API_ENDPOINT, {
          params: Object.fromEntries(params),
          timeout: DEFAULT_TIMEOUT,
          headers: {
            'User-Agent': getRandomUserAgent()
          }
        });
    
        const $ = cheerio.load(response.data);
        
        const phxNode = $('[id^="phx-"]').first();
        const csrfToken = $('[name="csrf-token"]').attr('content');
        const phxId = phxNode.attr('id');
        const phxSession = phxNode.attr('data-phx-session');
    
        if (!phxId || !csrfToken) {
          throw new Error('Failed to extract required tokens from IAsk AI page');
        }
    
        // Get the actual response URL (after any redirects)
        const responseUrl = response.request.res?.responseUrl || response.config.url;
        
        // Get cookies from the jar for WebSocket connection
        const cookies = await jar.getCookies(API_ENDPOINT);
        const cookieString = cookies.map(c => `${c.key}=${c.value}`).join('; ');
        
        // Build WebSocket URL
        const wsParams = new URLSearchParams({
          '_csrf_token': csrfToken,
          'vsn': '2.0.0'
        });
        const wsUrl = `wss://iask.ai/live/websocket?${wsParams.toString()}`;
    
        return new Promise((resolve, reject) => {
          const ws = new WebSocket(wsUrl, {
            headers: {
              'Cookie': cookieString,
              'User-Agent': getRandomUserAgent(),
              'Origin': 'https://iask.ai'
            }
          });
          
          let buffer = '';
          let timeoutId;
          let connectionTimeoutId;
    
          // Set connection timeout
          connectionTimeoutId = setTimeout(() => {
            ws.close();
            reject(new Error('IAsk connection timeout: unable to establish WebSocket connection'));
          }, 15000);
    
          ws.on('open', () => {
            clearTimeout(connectionTimeoutId);
            console.log('IAsk WebSocket connection established');
            
            // Send phx_join message
            ws.send(JSON.stringify([
              null,
              null,
              `lv:${phxId}`,
              'phx_join',
              {
                params: { _csrf_token: csrfToken },
                url: responseUrl,
                session: phxSession
              }
            ]));
    
            // Set message timeout
            timeoutId = setTimeout(() => {
              ws.close();
              if (buffer) {
                resolve(buffer || 'No results found.');
              } else {
                reject(new Error('IAsk response timeout: no response received'));
              }
            }, DEFAULT_TIMEOUT);
          });
    
          ws.on('message', (data) => {
            try {
              const msg = JSON.parse(data.toString());
              if (!msg) return;
    
              const diff = msg[4];
              if (!diff) return;
    
              let chunk = null;
    
              try {
                // Try to get chunk from diff.e[0][1].data
                if (diff.e) {
                  chunk = diff.e[0][1].data;
                  
                  if (chunk) {
                    let formatted;
                    if (/<[^>]+>/.test(chunk)) {
                      formatted = formatHtml(chunk);
                    } else {
                      formatted = chunk.replace(/<br\/>/g, '\n');
                    }
                    
                    buffer += formatted;
                  }
                } else {
                  throw new Error('No diff.e');
                }
              } catch {
                // Fallback to cacheFind
                const cache = cacheFind(diff);
                if (cache) {
                  let formatted;
                  if (/<[^>]+>/.test(cache)) {
                    formatted = formatHtml(cache);
                  } else {
                    formatted = cache;
                  }
                  buffer += formatted;
                  // Close after cache find
                  ws.close();
                  return;
                }
              }
            } catch (err) {
              console.error('Error parsing IAsk message:', err.message);
            }
          });
    
          ws.on('close', () => {
            clearTimeout(timeoutId);
            clearTimeout(connectionTimeoutId);
            
            console.log(`IAsk search completed: ${buffer.length} characters received`);
            
            // Cache the result
            if (buffer) {
              resultsCache.set(cacheKey, {
                results: buffer,
                timestamp: Date.now()
              });
            }
            
            resolve(buffer || 'No results found.');
          });
    
          ws.on('error', (err) => {
            clearTimeout(timeoutId);
            clearTimeout(connectionTimeoutId);
            console.error('IAsk WebSocket error:', err.message);
            
            if (err.message.includes('timeout')) {
              reject(new Error('IAsk WebSocket timeout: connection took too long'));
            } else if (err.message.includes('connection refused')) {
              reject(new Error('IAsk connection refused: service may be unavailable'));
            } else {
              reject(new Error(`IAsk WebSocket error: ${err.message}`));
            }
          });
        });
      } catch (error) {
        console.error('Error in IAsk search:', error.message);
        
        // Enhanced error handling
        if (error.code === 'ENOTFOUND') {
          throw new Error('IAsk network error: unable to resolve host');
        }
        
        if (error.code === 'ECONNREFUSED') {
          throw new Error('IAsk network error: connection refused');
        }
        
        if (error.message.includes('timeout')) {
          throw new Error(`IAsk timeout: ${error.message}`);
        }
        
        throw new Error(`IAsk search failed for "${prompt}": ${error.message}`);
      }
    }
    
    export { searchIAsk, VALID_MODES, VALID_DETAIL_LEVELS };
Install Server

Other 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/OEvortex/ddg_search'

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