The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Duckduckgo MCP Server listing page.
A Model Context Protocol (MCP) server that provides web search capabilities through DuckDuckGo, with additional features for content fetching and parsing.
ref:// tokens that fetch_content accepts directly, saving contextInstall from PyPI using uv:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonAdd the following configuration:
Basic Configuration (No SafeSearch, No Default Region):
With SafeSearch and Region Configuration:
Configuration Options:
DDG_SAFE_SEARCH: SafeSearch filtering level (optional)
STRICT: Maximum content filtering (kp=1)MODERATE: Balanced filtering (kp=-1, default if not specified)OFF: No content filtering (kp=-2)DDG_REGION: Default region/language code (optional, examples below)
us-en: United States (English)cn-zh: China (Chinese)jp-ja: Japan (Japanese)wt-wt: No specific regionDDG_CA_CERTS: Path to a PEM CA bundle used to verify TLS certificates on outbound requests (optional). Needed behind TLS-intercepting proxies — see Running behind a TLS-intercepting proxy.DDG_RATE_LIMIT_STRATEGY: sliding (default, historical 60s window) or token_bucket (burst, then smooth).DDG_SEARCH_RPM: Search requests per minute (default: 30).DDG_FETCH_RPM: Global fetch_content requests per minute (default: 20).DDG_FETCH_HOST_RPM: Optional per-host fetch cap (default: 0, off). Set a positive number to enable.DDG_CACHE_TTL: Seconds to keep a parsed page in the in-memory fetch_content cache (default: 300). Paginated reads of the same URL reuse one download. Set 0 to disable.DDG_CACHE_MAX_ENTRIES: Maximum pages kept in that cache (default: 64). Least-recently-used eviction. Set 0 to disable.DDG_PARSE_MODE: Default fetch_content extractor (text, main, or markdown). Default is text (historical flattened page). Per-call parse_mode overrides this.DDG_REF_URL_THRESHOLD: Search-result URLs longer than this many characters are replaced with short ref://<id> tokens (default: 120). Set 0 to always show full URLs. Also --ref-url-threshold.uvenv is installed and the uvx command is availableclaude mcp add ddg-search uvx duckduckgo-mcp-serverThe server supports alternative transports for use with other MCP clients:
The default transport is stdio, which is used by Claude Desktop and Claude Code.
When running with sse or streamable-http, override the default bind address (127.0.0.1:8000) with the --host and --port flags:
The MCP SDK enables DNS-rebinding protection for the HTTP transports and, by default, only accepts Host/Origin headers for localhost. Behind a reverse proxy or in a container the client's Host header won't match, so requests fail with 421 Misdirected Request.
Fix it by allow-listing the host(s) and origin(s) clients actually use (preferred over disabling protection). Values support host, host:port, and wildcard-port host:*:
Equivalent environment variables (comma-separated) are also available: DDG_ALLOWED_HOSTS, DDG_ALLOWED_ORIGINS.
As a last resort you can turn the check off entirely with --disable-dns-rebinding-protection (or DDG_DISABLE_DNS_REBINDING_PROTECTION=1). Prefer an allow-list — disabling protection removes a defense against DNS-rebinding attacks. When nothing is configured, the secure localhost-only default is preserved.
Corporate proxies that re-sign HTTPS traffic with their own CA (via HTTPS_PROXY) cause outbound requests to fail with certificate verification errors, because the HTTP clients don't trust the proxy's self-signed CA (and httpx no longer reads the SSL_CERT_FILE environment variable). Point the server at your proxy's CA bundle:
Or set DDG_CA_CERTS=/path/to/proxy-ca.pem. The bundle is used by both the search and fetch_content tools, on the httpx and curl backends alike.
As a last resort, --no-ssl-verify (or DDG_SSL_VERIFY=0) disables certificate verification entirely. This exposes traffic to interception by anyone on the network path — prefer --ca-certs.
Some sites — and, as of recently, DuckDuckGo's own search endpoint (html.duckduckgo.com) — block the default httpx client because of its distinctive TLS fingerprint, regardless of User-Agent. Cloudflare Bot Management and similar filters key on the JA3/TLS handshake, not on headers, so html.duckduckgo.com may answer httpx with an empty HTTP 202 page (silently yielding "no results"). An opt-in backend, curl (implemented via curl_cffi), impersonates a real Chrome browser's TLS handshake and passes through those checks.
Both the search tool and the fetch_content tool support these backends.
Installation:
Backend options:
| Value | Behavior | Needs [browser] |
|---|---|---|
httpx | Lightweight async HTTP. Default. Works on most sites. | no |
curl | Uses curl_cffi with Chrome 131 TLS impersonation. Passes TLS-fingerprint-based filters. | yes |
auto | Tries httpx first; on 403 or a Cloudflare challenge response, retries with curl. | yes |
Two ways to configure the backend:
Server-wide default via the --fetch-backend CLI flag (applies to every fetch_content call):
Per-call override via the backend argument on the fetch_content tool (overrides the CLI default for that single call). The tool exposes backend in its input schema, so an MCP client can choose "httpx", "curl", or "auto" on a fetch-by-fetch basis.
For fetch_content, the default stays httpx so users who don't need the impersonation don't pay for the extra dependency.
Because DuckDuckGo's search endpoint now fingerprint-blocks plain httpx, the search tool defaults to auto: it tries httpx first and falls back to curl when it detects a block (HTTP 202/403). The fallback only works if the [browser] extra is installed; otherwise search returns a message telling you to install it.
Configure the search backend with the --search-backend CLI flag or the DDG_SEARCH_BACKEND environment variable (auto (default) / httpx / curl):
For local development:
Performs a web search on DuckDuckGo and returns formatted results.
Parameters:
query: Search query stringmax_results: Maximum number of results to return (default: 10)region: (Optional) Region/language code to override the default. Leave empty to use the configured default region.Region Code Examples:
us-en: United States (English)cn-zh: China (Chinese)jp-ja: Japan (Japanese)de-de: Germany (German)fr-fr: France (French)wt-wt: No specific regionReturns: Formatted string containing search results with titles, URLs, and snippets.
Example Usage:
search("python tutorial")search("latest news", region="jp-ja") for Japanese newsFetches and parses content from a webpage.
Parameters:
url: The webpage URL to fetch content from, or a ref://<id> token from search resultsstart_index: Character offset to start reading from (for pagination)max_length: Maximum number of characters to returnbackend: Optional per-call override of the default fetch backend ("httpx", "curl", or "auto"). When omitted, uses whatever was set via --fetch-backend at server startup.parse_mode: Optional per-call extractor ("text", "main", or "markdown"). When omitted, uses DDG_PARSE_MODE / --parse-mode (default text).Returns:
Cleaned and formatted text content from the webpage. The parsed full page is cached in memory (default 5 minutes) so later pages via start_index do not re-download. Metadata includes cache=hit or cache=miss when the cache is enabled.
SSRF protection: By default
fetch_contentrefuses URLs that resolve to loopback, private (RFC1918), link-local (including the169.254.169.254cloud metadata endpoint), reserved, multicast, or unspecified addresses, and it re-validates every redirect hop. Onlyhttp/httpsURLs are allowed. For trusted local deployments that need to fetch internal hosts, disable the guard withDDG_ALLOW_PRIVATE_URLS=1or--allow-private-urls. See SECURITY.md for details.
Search results replace URLs longer than DDG_REF_URL_THRESHOLD characters (default 120) with short, stable ref://<id> tokens so long tracking-laden links do not eat context. fetch_content accepts a token in place of a URL, so the model only needs this tool when it has to show or cite the real link.
Parameters:
token: A ref://<id> token exactly as it appeared in search results (the bare id also works)Returns: The full original URL, or an error if the token is unknown. Tokens live in memory for the lifetime of the server process (bounded by an LRU cap), so they are forgotten on restart.
DDG_SEARCH_RPM / --search-rpm)DDG_FETCH_RPM / --fetch-rpm)DDG_FETCH_HOST_RPM / --fetch-host-rpm)sliding (default) or token_bucket via DDG_RATE_LIMIT_STRATEGY / --rate-limit-strategyRetry-After (capped at 30s) and retry oncefetch_content skip both the download and the fetch rate limiterDDG_CACHE_TTL / DDG_CACHE_MAX_ENTRIES or --cache-ttl / --cache-max-entries0 to disablefetch_content accepts parse_mode:
| Mode | Behavior |
|---|---|
text | Historical default. Strip chrome, return flattened page text. |
main | Keep the primary article / main / content container only. |
markdown | Same primary content, rendered as lightweight markdown (headings, lists, links, code). |
SafeSearch Filtering: Configured at server startup via DDG_SAFE_SEARCH environment variable
kp parameterRegion Localization:
DDG_REGION environment variableIssues and pull requests are welcome!
This project is licensed under the MIT License.