#
tokens: 908/50000 9/9 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .dockerignore
├── .env
├── .gitignore
├── .python-version
├── ddgs.py
├── docker-compose.yml
├── Dockerfile
├── pyproject.toml
├── README.md
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
.venv
```

--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------

```
3.11

```

--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------

```
PORT=8000
NETWORK_MODE=mcp-ddgs_default #or you can use "host"
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
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
FROM python:3.12-slim-bookworm

COPY --from=ghcr.io/astral-sh/uv:0.6.1 /uv /uvx /bin/

RUN apt update && apt install curl -y

ADD . /app

WORKDIR /app

RUN uv venv --python 3.12 venv

RUN uv sync --frozen

CMD ["uv", "run", "python", "ddgs.py" ]
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[project]
name = "mcp-ddgs"
version = "0.1.0"
description = "Simple Model Context Protocol server for searching Duck Duck Go"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "duckduckgo-search>=7.4.2",
    "httpx>=0.28.1",
    "mcp[cli]>=1.2.1",
]

```

--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------

```yaml
services:
  mcp-ddgs:
    container_name: mcp-ddgs
    restart: unless-stopped
    pull_policy: build
    image: lowlyocean/mcp-ddgs:latest
    build:
      context: .
      network: host
    ports:
      - ${PORT}:${PORT}
    environment:
      FASTMCP_PORT: ${PORT}
    extra_hosts:
      - "host.docker.internal:host-gateway"
    network_mode: ${NETWORK_MODE}
  
```

--------------------------------------------------------------------------------
/ddgs.py:
--------------------------------------------------------------------------------

```python
from mcp.server.fastmcp import FastMCP
from duckduckgo_search import DDGS
from typing import Annotated
from pydantic import Field

# Initialize FastMCP server
mcp = FastMCP("ddgs")

@mcp.tool()
async def search(query: Annotated[str, Field(description="The query to search for")]) -> list[dict]:
    """
    Search using DuckDuckGo's Search API and return the results as a list of dictionaries
    """
    # Use the DDGS context manager to create a DDGS object
    with DDGS() as ddgs:
        # Use the ddgs.text() method to perform the search
        ddgs_gen = ddgs.text(
            query, safesearch="moderate", max_results=3, backend="auto"
        )
        # Check if there are search results
        if ddgs_gen:
            # Convert the search results into a list
            search_results = [r for r in ddgs_gen]

    # Return the list of search results
    return [
        {
            "link": result["href"],
            "title": result.get("title"),
            "snippet": result.get("body"),
        }
        for result in search_results
    ]

if __name__ == "__main__":
    # Initialize and run the server
    mcp.run(transport='sse')
```