ddg-mcp

command module
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 26, 2026 License: MIT Imports: 17 Imported by: 0

README

ddg-mcp

A Model Context Protocol (MCP) server that provides web search and web fetch capabilities via DuckDuckGo.

Exposes web_search and web_fetch tools that MCP-compatible clients (Claude Desktop, Cursor, Windsurf, opencode, etc.) can use to search the web and retrieve page content.

Features

  • Web search via DuckDuckGo Lite endpoint (no API key required)
    • Titles, URLs, snippets, and domain info per result
    • Zero-click / instant answer extraction
    • Pagination via offset/vqd tokens
    • Region bias (kl), safe search (kp), and time range (df) filters
    • Sponsored result detection
    • Parser breakage detection with warning when HTML structure changes
    • Automatic retry with exponential backoff + jitter on rate limiting (HTTP 429)
  • Web fetch — retrieve and extract readable content from URLs
    • Strips scripts, styles, nav, header, footer elements
    • Content truncation with configurable max length
    • SSRF protection (blocks redirects to private/reserved IPs)
  • Query validation with length limits
  • Stdio transport for MCP compatibility
  • Cross-platform binaries via GoReleaser

Installation

Pre-built binaries

Download the latest release from the releases page.

Go install
go install github.com/bcambl/ddg-mcp@latest
Build from source
git clone https://github.com/bcambl/ddg-mcp.git
cd ddg-mcp
make build

Usage

Running the server
ddg-mcp

The server communicates over stdio using the MCP protocol. It is designed to be launched by MCP-compatible clients.

Environment Variables

The following environment variables can be used to customize server behavior without modifying the binary or client configuration:

Variable Type Default Description
DDG_SEARCH_URL string https://lite.duckduckgo.com/lite/ DuckDuckGo search endpoint URL
DDG_TIMEOUT duration 15s HTTP timeout for search requests
DDG_FETCH_TIMEOUT duration 15s HTTP timeout for fetch requests
DDG_MAX_RETRIES int 2 Max retries on HTTP 429 (rate limited) for search
DDG_MAX_BODY_SIZE int (bytes) 2097152 (2 MiB) Maximum response body size for search and fetch
DDG_USER_AGENT string Chrome 120 UA User-Agent for search requests
DDG_FETCH_USER_AGENT string Chrome 120 UA User-Agent for fetch requests
DDG_DEFAULT_REGION string (none) Default region bias (e.g., us-en, uk-en, de-de) when not specified per-request
DDG_SSRF_PROTECTION bool true Enable SSRF protection for web_fetch (redirects to private IPs blocked)
DDG_LOG_LEVEL string info Log level: debug, info, warn, error

Duration values accept Go-style strings: 15s, 1m, 500ms.

Boolean values accept: true, false, 1, 0.

Example with Go install:

DDG_DEFAULT_REGION=de-de DDG_TIMEOUT=30s ddg-mcp
MCP client configuration

Add the server to your MCP client configuration.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "ddg-search": {
      "command": "ddg-mcp"
    }
  }
}
Cursor

Add to .cursor/mcp.json in your project or global config:

{
  "mcpServers": {
    "ddg-search": {
      "command": "ddg-mcp"
    }
  }
}
Windsurf

Add to your Windsurf MCP settings:

{
  "mcpServers": {
    "ddg-search": {
      "command": "ddg-mcp"
    }
  }
}
opencode

Add to your opencode.json:

{
  "mcp": {
    "ddg-search": {
      "command": "ddg-mcp"
    }
  }
}
Using Go install path

If installed via go install, use the full path:

{
  "mcpServers": {
    "ddg-search": {
      "command": "go",
      "args": ["run", "github.com/bcambl/ddg-mcp@latest"]
    }
  }
}

Search the web using DuckDuckGo. Returns search results with titles, URLs, snippets, and domain info. Supports pagination, region bias, safe search, and time range filters. Zero-click / instant answer results are included when available.

Input:

Parameter Type Required Description
query string Yes The search query (max 500 characters)
region string No Region code for search bias (e.g., us-en, uk-en, de-de, wt-wt for no region)
safe_search int No Safe search level: 1=strict, -1=moderate, -2=off. Default: 0 (DuckDuckGo default)
time_range string No Time range filter: d=day, w=week, m=month, y=year. Empty=any time
offset int No Result offset for pagination (0, 10, 20, etc.)
vqd string No Pagination token from previous search response, required when offset > 0

Output:

{
  "query": "golang mcp",
  "result_count": 3,
  "results": [
    {
      "title": "Example Result",
      "url": "https://example.com/page",
      "snippet": "A brief description of the search result.",
      "domain": "example.com",
      "sponsored": false
    }
  ],
  "zero_click": {
    "title": "Instant Answer Title",
    "url": "https://example.com/answer",
    "description": "Brief answer from DuckDuckGo's instant answers."
  },
  "has_next_page": true,
  "next_offset": 10,
  "vqd": "3-123-456",
  "parser_warning": ""
}

Pagination:

To get the next page of results, pass offset and vqd from the previous response:

{
  "query": "golang mcp",
  "offset": 10,
  "vqd": "3-123-456"
}

Error cases:

  • Empty query: "query must not be empty"
  • Query too long: "query exceeds maximum length of 500 characters"
  • Invalid region: "region must match format xx-xx (e.g., us-en, uk-en, de-de)"
  • Invalid safe_search: "safe_search must be one of: 0 (default), 1 (strict), -1 (moderate), -2 (off)"
  • Invalid time_range: "time_range must be one of: d (day), w (week), m (month), y (year), or empty"
  • Paginated request without vqd: "vqd token is required for paginated requests (offset > 0)"
  • Rate limited: "rate limited by DuckDuckGo (HTTP 429), please retry later"
  • Server error: "DuckDuckGo returned HTTP 500: ..."
  • Parser breakage: "parser may be broken: HTML contains result markers but no results were extracted"
Tool: web_fetch

Fetch and extract readable content from a URL. Returns the page title and text content with scripts, styles, navigation, header, and footer elements removed. Only text/html and text/plain content types are supported.

Input:

Parameter Type Required Description
url string Yes The URL to fetch (must be HTTP or HTTPS)
max_length int No Maximum content length in characters (default 10000, max 50000)

Output:

{
  "url": "https://example.com/page",
  "title": "Example Page Title",
  "content": "Extracted readable text content from the page...",
  "content_length": 8234,
  "content_type": "text/html; charset=utf-8",
  "truncated": false
}

Error cases:

  • Empty URL: "url must not be empty"
  • Invalid URL: "url must be a valid HTTP or HTTPS URL"
  • Private IP (SSRF protection enabled): "url must not point to a private or reserved IP address"
  • Unsupported content type: "unsupported content type: application/pdf (only text/html and text/plain are supported)"
  • Server error: "fetch returned HTTP 404"

Development

Prerequisites
  • Go 1.26+
Build
make build
Test
make test
Run all checks
make check
Integration tests

Integration tests hit the real DuckDuckGo endpoint and require a build tag:

go test -tags=integration -v ./...

How it works

The server scrapes DuckDuckGo's Lite search endpoint (lite.duckduckgo.com/lite/) rather than using a formal API. This approach:

  • Requires no API key
  • Returns results similar to a browser search
  • Is subject to DuckDuckGo's rate limiting

DuckDuckGo wraps result URLs in redirect links (e.g., //duckduckgo.com/l/?uddg=<encoded-url>). The server extracts the real URL from the uddg query parameter and falls back to the raw href for direct links.

Pagination works by POSTing form data (including a vqd token and offset) back to the Lite endpoint when offset > 0. The vqd token identifies the search session and is extracted from a hidden input in the results page.

The parser detects potential breakage — if the response HTML contains result markers (CSS classes, redirect link patterns) but no results were extracted, a parser_warning is included in the response.

Fetch

The fetch tool makes a GET request to the provided URL, strips unwanted HTML elements (scripts, styles, nav, header, footer), and returns the readable text content. It enforces a configurable max content length and blocks requests to private/reserved IPs when SSRF protection is enabled (default).

Limitations

  • Search results are limited to the first page per request (~10 results); pagination requires explicit offset/vqd tokens
  • Subject to DuckDuckGo rate limiting; avoid rapid successive queries
  • HTML scraping may break if DuckDuckGo changes their page structure (a parser_warning is included when detected)
  • No support for image search, news search, or other DuckDuckGo features
  • Fetch only supports text/html and text/plain content types

License

MIT

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL