search_web
Search the web to find information using DuckDuckGo or SerpAPI. Enter a query to get relevant web results and content.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| limit | No |
Implementation Reference
- src/server.js:75-92 (handler)The main execution handler for the 'search_web' tool. It first tries SerpAPI search, falls back to DuckDuckGo scraping if that fails, formats the top results as numbered list with titles and URLs, and returns as text content.async (input) => { const limit = input.limit ?? SEARCH_RESULTS_LIMIT_DEFAULT; let results = null; try { results = await serpApiSearch(input.query, limit); } catch (e) { } if (!results) { results = await ddgSearch(input.query, limit); } const text = results .map((r, i) => `${i + 1}. ${r.title}\n${r.url}`) .join("\n\n"); return { content: [{ type: "text", text: text || "No results" }], }; }
- src/server.js:71-74 (schema)Zod schema for input validation: required 'query' string and optional 'limit' integer between 1 and 10.{ query: z.string().min(1), limit: z.number().int().min(1).max(10).optional(), },
- src/server.js:69-93 (registration)Registers the 'search_web' tool on the MCP server with its schema and handler function.server.tool( "search_web", { query: z.string().min(1), limit: z.number().int().min(1).max(10).optional(), }, async (input) => { const limit = input.limit ?? SEARCH_RESULTS_LIMIT_DEFAULT; let results = null; try { results = await serpApiSearch(input.query, limit); } catch (e) { } if (!results) { results = await ddgSearch(input.query, limit); } const text = results .map((r, i) => `${i + 1}. ${r.title}\n${r.url}`) .join("\n\n"); return { content: [{ type: "text", text: text || "No results" }], }; } );
- src/server.js:16-51 (helper)Helper function for scraping DuckDuckGo search results as fallback when SerpAPI is unavailable.async function ddgSearch(query, limit) { const params = new URLSearchParams({ q: query, kl: "us-en" }); const res = await fetch(`https://duckduckgo.com/html/?${params.toString()}`, { headers: { "User-Agent": "Mozilla/5.0 (compatible; MCP-Web-Tools/0.1; +https://example.com)", "Accept-Language": "en-US,en;q=0.9", }, }); const html = await res.text(); const dom = new JSDOM(html); const doc = dom.window.document; const results = []; const nodes = doc.querySelectorAll(".result__title a.result__a"); for (const a of nodes) { const title = a.textContent?.trim() || ""; let href = a.getAttribute("href") || ""; if (!href) continue; let url = href; try { if (href.startsWith("/l/?") || href.includes("duckduckgo.com/l/?")) { const full = href.startsWith("http") ? href : `https://duckduckgo.com${href}`; const u = new URL(full); const target = u.searchParams.get("uddg"); if (target) url = decodeURIComponent(target); } else if (href.startsWith("//")) { url = `https:${href}`; } else if (href.startsWith("/")) { url = `https://duckduckgo.com${href}`; } } catch { } if (!url) continue; results.push({ title, url }); if (results.length >= limit) break; } return results; }
- src/server.js:53-67 (helper)Primary helper for web search using SerpAPI (requires SERPAPI_KEY env var), returns top organic Google results.async function serpApiSearch(query, limit) { const apiKey = process.env.SERPAPI_KEY; if (!apiKey) return null; const params = new URLSearchParams({ engine: "google", q: query, num: String(Math.min(limit, 10)), api_key: apiKey, }); const res = await fetch(`https://serpapi.com/search.json?${params.toString()}`); if (!res.ok) throw new Error(`SerpAPI error: ${res.status}`); const data = await res.json(); const organic = Array.isArray(data.organic_results) ? data.organic_results : []; return organic.slice(0, limit).map((r) => ({ title: r.title || "", url: r.link || "" })); }