# Directory Structure
```
├── .dockerignore
├── .env
├── .gitignore
├── .python-version
├── ddgs.py
├── docker-compose.yml
├── Dockerfile
├── pyproject.toml
├── README.md
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
```
1 | .venv
```
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
```
1 | 3.11
2 |
```
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
```
1 | PORT=8000
2 | NETWORK_MODE=mcp-ddgs_default #or you can use "host"
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Python-generated files
2 | __pycache__/
3 | *.py[oc]
4 | build/
5 | dist/
6 | wheels/
7 | *.egg-info
8 |
9 | # Virtual environments
10 | .venv
11 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | Simple Model Context Protocol server for searching Duck Duck Go, based on [Open-WebUI's built-in Web Search feature](https://github.com/open-webui/open-webui/blob/3f3a5bb0ab8ce3425f317f1e57b084523aa2b2a5/backend/open_webui/retrieval/web/duckduckgo.py) and using SSE transport
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM python:3.12-slim-bookworm
2 |
3 | COPY --from=ghcr.io/astral-sh/uv:0.6.1 /uv /uvx /bin/
4 |
5 | RUN apt update && apt install curl -y
6 |
7 | ADD . /app
8 |
9 | WORKDIR /app
10 |
11 | RUN uv venv --python 3.12 venv
12 |
13 | RUN uv sync --frozen
14 |
15 | CMD ["uv", "run", "python", "ddgs.py" ]
```
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
```toml
1 | [project]
2 | name = "mcp-ddgs"
3 | version = "0.1.0"
4 | description = "Simple Model Context Protocol server for searching Duck Duck Go"
5 | readme = "README.md"
6 | requires-python = ">=3.11"
7 | dependencies = [
8 | "duckduckgo-search>=7.4.2",
9 | "httpx>=0.28.1",
10 | "mcp[cli]>=1.2.1",
11 | ]
12 |
```
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
```yaml
1 | services:
2 | mcp-ddgs:
3 | container_name: mcp-ddgs
4 | restart: unless-stopped
5 | pull_policy: build
6 | image: lowlyocean/mcp-ddgs:latest
7 | build:
8 | context: .
9 | network: host
10 | ports:
11 | - ${PORT}:${PORT}
12 | environment:
13 | FASTMCP_PORT: ${PORT}
14 | extra_hosts:
15 | - "host.docker.internal:host-gateway"
16 | network_mode: ${NETWORK_MODE}
17 |
```
--------------------------------------------------------------------------------
/ddgs.py:
--------------------------------------------------------------------------------
```python
1 | from mcp.server.fastmcp import FastMCP
2 | from duckduckgo_search import DDGS
3 | from typing import Annotated
4 | from pydantic import Field
5 |
6 | # Initialize FastMCP server
7 | mcp = FastMCP("ddgs")
8 |
9 | @mcp.tool()
10 | async def search(query: Annotated[str, Field(description="The query to search for")]) -> list[dict]:
11 | """
12 | Search using DuckDuckGo's Search API and return the results as a list of dictionaries
13 | """
14 | # Use the DDGS context manager to create a DDGS object
15 | with DDGS() as ddgs:
16 | # Use the ddgs.text() method to perform the search
17 | ddgs_gen = ddgs.text(
18 | query, safesearch="moderate", max_results=3, backend="auto"
19 | )
20 | # Check if there are search results
21 | if ddgs_gen:
22 | # Convert the search results into a list
23 | search_results = [r for r in ddgs_gen]
24 |
25 | # Return the list of search results
26 | return [
27 | {
28 | "link": result["href"],
29 | "title": result.get("title"),
30 | "snippet": result.get("body"),
31 | }
32 | for result in search_results
33 | ]
34 |
35 | if __name__ == "__main__":
36 | # Initialize and run the server
37 | mcp.run(transport='sse')
```