Duckduckgo Websearch
MCP Server for searching via DuckDuckGo (Node.js version)
Ask AI about Duckduckgo Websearch
Powered by Claude Β· Grounded in docs
I know everything about Duckduckgo Websearch. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
DuckDuckGo Search MCP Server
A Model Context Protocol (MCP) server that provides web search and content fetching via DuckDuckGo. No API key required.
Features
- Web Search β Multi-page results (supports 10 ~ 100+ results via automatic pagination)
- Content Fetching β Fetch and parse any webpage to clean text
- Bot Detection & Retry β Automatically retries up to 3 times on DDG bot challenges
- Session Cookie Jar β Persists DDG session cookies in-memory across requests
- Advanced Query Syntax β Full support for DDG/Google-style operators (
site:,OR,intitle:, etc.) - Zero-Click Results β Instant answer cards (e.g. search
ip,weather) - Zero Dependencies at Runtime β Only
cheerio+@modelcontextprotocol/sdk - Node.js & Bun β Works with both runtimes (Node β₯ 18)
Installation
As MCP Server (global CLI)
npm install -g duckduckgo-websearch
# or
npx duckduckgo-websearch
As npm Library
npm install duckduckgo-websearch
MCP Server Usage
Claude Desktop
Edit your Claude Desktop config:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"ddg-search": {
"command": "npx",
"args": ["duckduckgo-websearch"]
}
}
}
Or with a local build:
{
"mcpServers": {
"ddg-search": {
"command": "node",
"args": ["/path/to/duckduckgo-mcp-server/build/index.js"]
}
}
}
Other MCP Clients
Any MCP-compatible client (Cursor, Cline, Continue, etc.) can connect via stdio transport using the same command above.
MCP Tools
search
Search DuckDuckGo and return paginated results.
| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | required | Search query. Supports advanced syntax (see below) |
max_results | integer | 25 | Number of results to return. Triggers automatic pagination when > 10 |
Advanced Query Syntax (DDG supports Google-style operators):
| Syntax | Example | Effect |
|---|---|---|
site:domain | site:github.com python | Restrict to a domain |
site:a.com OR site:b.com | site:docs.python.org OR site:stackoverflow.com | Multiple domains |
"exact phrase" | "model context protocol" | Exact match |
-word | python -snake | Exclude keyword |
intitle:word | intitle:tutorial python | Match in page title |
filetype:ext | filetype:pdf machine learning | Filter by file type |
OR / AND | python OR javascript async | Boolean operators |
Response format:
Found 25 search results:
1. Page Title
URL: https://example.com/page
Summary: Brief description of the page content...
2. ...
fetch_content
Fetch and parse a webpage to clean, LLM-readable text.
| Parameter | Type | Default | Description |
|---|---|---|---|
url | string | required | Webpage URL to fetch |
max_content_length | integer | 8000 | Maximum characters to return |
Library Usage (Node.js / Bun)
import { WebSearch, WebFetcher } from 'duckduckgo-websearch';
// Search
const searcher = new WebSearch();
// Basic search β returns up to 10 results (1 page)
const results = await searcher.search('claude anthropic');
// Request more results β auto-paginates across multiple DDG pages
const results = await searcher.search('python tutorial', { maxResults: 50 });
// Advanced query syntax works natively in the query string
const results = await searcher.search('site:github.com mcp server typescript');
const results = await searcher.search('site:docs.python.org OR site:realpython.com async await');
// Format for LLM consumption
console.log(searcher.formatResultsForLLM(results));
// Fetch webpage content
const fetcher = new WebFetcher();
const content = await fetcher.fetchAndParse('https://example.com', 8000);
SearchResult type
interface SearchResult {
title: string;
link: string;
snippet: string;
position: number;
}
Error handling
import { WebSearch, SearchError } from 'duckduckgo-websearch';
try {
const results = await searcher.search('query');
} catch (err) {
if (err instanceof SearchError) {
console.error(err.code); // 'BOT_DETECTED' | 'HTTP_ERROR' | 'TIMEOUT' | 'UNKNOWN'
console.error(err.message);
}
}
SearchError codes:
| Code | Meaning |
|---|---|
BOT_DETECTED | DDG bot challenge triggered after 3 retry attempts |
HTTP_ERROR | Non-2xx HTTP response |
TIMEOUT | Request timed out (30s limit) |
UNKNOWN | Unexpected failure |
Development
git clone https://github.com/HeiSir2014/duckduckgo-mcp-server
cd duckduckgo-mcp-server
npm install
npm run build # compile TypeScript β build/
# Run example test (Bun)
bun example/test.ts
# Run with Node
node -e "require('./build/index.js')"
Architecture
src/
βββ index.ts # MCP server entry, tool definitions
βββ duckduckgoSearcher.ts # Search logic: fetch, bot detection, retry, pagination
βββ cookieJar.ts # In-memory cookie jar for DDG session persistence
βββ webContentFetcher.ts # Webpage fetch + text extraction
βββ rateLimiter.ts # Token-bucket rate limiter
βββ types.ts # Shared types (SearchResult, SearchOptions, SearchError)
Pagination mechanism β DDG HTML endpoint returns 10 results per page with a vqd session token embedded in the "Next" form. When maxResults > 10, the searcher chains page requests using vqd and the form parameters (s, dc) extracted from each page's nav-link.
Bot detection β On each page fetch the searcher checks for missing result containers (.serp__results) and known challenge keywords. On detection it awaits the report ping (/t/sl_h) which warms the session, then retries. After 3 failures it throws SearchError('BOT_DETECTED').
Rate Limits
| Operation | Limit |
|---|---|
| Search | 30 requests / minute |
| Content Fetch | 20 requests / minute |
License
MIT
