web-search
Conduct web searches using DuckDuckGo to retrieve real-time results, customize page numbers, and specify the number of outputs for efficient information retrieval.
Instructions
Search the web using DuckDuckGo and return results
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| numResults | No | Number of results to return (default: 10) | |
| page | No | Page number (default: 1) | |
| query | Yes | The search query |
Input Schema (JSON Schema)
{
"properties": {
"numResults": {
"default": 10,
"description": "Number of results to return (default: 10)",
"maximum": 20,
"minimum": 1,
"type": "integer"
},
"page": {
"default": 1,
"description": "Page number (default: 1)",
"minimum": 1,
"type": "integer"
},
"query": {
"description": "The search query",
"type": "string"
}
},
"required": [
"query"
],
"type": "object"
}
Implementation Reference
- src/tools/searchTool.js:40-55 (handler)The handler function for the 'web-search' tool. It processes input parameters and delegates to searchDuckDuckGo helper, then formats the response as MCP content.export async function searchToolHandler(params) { const { query, numResults = 3, mode = 'short' } = params; console.log(`Searching for: ${query} (${numResults} results, mode: ${mode})`); const results = await searchDuckDuckGo(query, numResults, mode); console.log(`Found ${results.length} results`); return { content: [ { type: 'text', text: JSON.stringify(results) } ] }; }
- src/tools/searchTool.js:6-33 (schema)The schema/definition for the 'web-search' tool, including name, description, and input schema validation.export const searchToolDefinition = { name: 'web-search', title: 'Web Search', description: 'Perform a web search using DuckDuckGo and receive detailed results including titles, URLs, and summaries.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Enter your search query to find the most relevant web pages.' }, numResults: { type: 'integer', description: 'Specify how many results to display (default: 3, maximum: 20).', default: 3, minimum: 1, maximum: 20 }, mode: { type: 'string', description: "Choose 'short' for basic results (no Description) or 'detailed' for full results (includes Description).", enum: ['short', 'detailed'], default: 'short' } }, required: ['query'] } };
- src/index.ts:14-18 (registration)Registration of the tool definition in the availableTools array, returned by ListToolsRequestSchema handler.const availableTools = [ searchToolDefinition, iaskToolDefinition, monicaToolDefinition ];
- src/index.ts:50-51 (registration)Dispatch/registration of the tool handler in the switch statement within CallToolRequestSchema handler.case 'web-search': return await searchToolHandler(args);
- src/utils/search.js:117-245 (helper)Core helper function implementing the web search logic: fetches DuckDuckGo HTML, parses results with cheerio, extracts direct URLs, optionally fetches summaries via Jina AI, with caching.async function searchDuckDuckGo(query, numResults = 10, mode = 'short') { try { // Clear old cache entries clearOldCache(); // Check cache first const cacheKey = getCacheKey(query); const cachedResults = resultsCache.get(cacheKey); if (cachedResults && Date.now() - cachedResults.timestamp < CACHE_DURATION) { return cachedResults.results.slice(0, numResults); } // Get a random user agent const userAgent = getRandomUserAgent(); // Fetch results const response = await axios.get( `https://duckduckgo.com/html/?q=${encodeURIComponent(query)}`, { headers: { 'User-Agent': userAgent }, httpsAgent: httpsAgent } ); if (response.status !== 200) { throw new Error('Failed to fetch search results'); } const html = response.data; // Parse results using cheerio const $ = cheerio.load(html); const results = []; const jinaFetchPromises = []; $('.result').each((i, result) => { const $result = $(result); const titleEl = $result.find('.result__title a'); const linkEl = $result.find('.result__url'); const snippetEl = $result.find('.result__snippet'); const title = titleEl.text()?.trim(); const rawLink = titleEl.attr('href'); const description = snippetEl.text()?.trim(); const displayUrl = linkEl.text()?.trim(); const directLink = extractDirectUrl(rawLink || ''); const favicon = getFaviconUrl(directLink); const jinaUrl = getJinaAiUrl(directLink); if (title && directLink) { if (mode === 'detailed') { jinaFetchPromises.push( axios.get(jinaUrl, { headers: { 'User-Agent': getRandomUserAgent() }, httpsAgent: httpsAgent, timeout: 10000 }) .then(jinaRes => { let jinaContent = ''; if (jinaRes.status === 200 && typeof jinaRes.data === 'string') { const $jina = cheerio.load(jinaRes.data); jinaContent = $jina('body').text() } return { title, url: directLink, snippet: description || '', favicon: favicon, displayUrl: displayUrl || '', Description: jinaContent }; }) .catch(() => { return { title, url: directLink, snippet: description || '', favicon: favicon, displayUrl: displayUrl || '', Description: '' }; }) ); } else { // short mode: omit Description jinaFetchPromises.push( Promise.resolve({ title, url: directLink, snippet: description || '', favicon: favicon, displayUrl: displayUrl || '' }) ); } } }); // Wait for all Jina AI fetches to complete const jinaResults = await Promise.all(jinaFetchPromises); results.push(...jinaResults); // Get limited results const limitedResults = results.slice(0, numResults); // Cache the results resultsCache.set(cacheKey, { results: limitedResults, timestamp: Date.now() }); // If cache is too big, remove oldest entries if (resultsCache.size > MAX_CACHE_PAGES) { const oldestKey = Array.from(resultsCache.keys())[0]; resultsCache.delete(oldestKey); } return limitedResults; } catch (error) { console.error('Error searching DuckDuckGo:', error.message); throw error; } }