A minimal, production-ready Model Context Protocol (MCP) server enabling LLMs to perform web searches via DuckDuckGo. Built with FastAPI and Server-Sent Events (SSE).
Features:
- ✅ Single-file implementation (~324 lines)
- ✅ No API keys required
- ✅ HTTP + SSE streaming (LM Studio compatible)
- ✅ Docker-ready
- ✅ Minimal dependencies (FastAPI, Uvicorn, duckduckgo-search)
- Python 3.10+ or Docker
- LM Studio (optional)
- Install VS Code and Docker Desktop
- Install Dev Containers extension
- Open folder in VS Code → "Reopen in Container"
make install-dev
make run-dev
# Server: http://localhost:8000make docker-build
make docker-run
# Or: make docker-composecurl -X POST http://localhost:8000/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": {"query": "Python async", "all_results": true, "region": "us-en", "safesearch": "moderate", "timelimit": "w"}
}
}'Initialize:
{"method": "initialize", "id": 1}List Tools:
{"method": "tools/list", "id": 2}Call Search:
{
"method": "tools/call",
"id": 3,
"params": {
"name": "web_search",
"arguments": {"query": "your query", "max_results": 5, "all_results": false, "region": "wt-wt", "safesearch": "moderate"}
}
}Add to mcp-config.json:
{
"mcpServers": {
"duckduckgo-search": {
"type": "http",
"url": "http://localhost:8000",
"disabled": false
}
}
}Data Flow:
LM Studio → POST / (JSON-RPC) → FastAPI → SSE Events → Client
↓
MCP Handlers (initialize, tools/list, tools/call)
↓
DuckDuckGo Search API
Key Components:
mcp_http_sse_server.py- Single FastAPI app with async streamingrequirements.txt- Dependencies onlyDockerfile- Production & dev stagesdocker-compose.yml- Orchestrationtest-mcp-sse.py- Integration tests
make help # Show all commands
make install-dev # Install dev dependencies
make run-dev # Run with auto-reload
make test # Run tests
make lint # Ruff + mypy type checking
make format # Black + ruff auto-format
make clean # Remove build artifacts
make docker-build # Build Docker image
make docker-compose # Run with Docker Composemake format # Auto-format code
make lint # Run linters| Variable | Default | Purpose |
|---|---|---|
MCP_DEBUG |
false |
Verbose logging |
MCP_ENVIRONMENT |
production |
Dev mode |
MCP_LOG_LEVEL |
INFO |
Log level |
MCP_PORT |
8000 |
Server port |
Set in docker-compose.yml:
environment:
- MCP_ENVIRONMENT=development
- MCP_LOG_LEVEL=INFOProduction requires: mcp_http_sse_server.py + requirements.txt
event: message
data: {"jsonrpc": "2.0", "id": 1, "result": {...}}
event: done
data: {}
- Input:
query(string, required)max_results(int, 1-10, default 5). Ignored whenall_resultsis true.all_results(boolean, default false): fetch maximum results (capped at 10)region(string, defaultwt-wt)safesearch(string enum:off|moderate|strict, defaultmoderate)timelimit(string enum:d|w|m|y, optional)
- Output: Formatted markdown with title, URL, snippet
Keep this project minimal:
- Single-file design (avoid modularization)
- MCP protocol compliance
- Test with real LM Studio
- Run
make format && make lintbefore submission
- Examples: test-mcp-sse.py
- Debugging: Set
MCP_DEBUG=true - Config: See mcp-config.json
- 1.2.0: Add
all_resultsflag (internal cap 100) to support larger result sets; bump version and docs. - 1.1.0: Improved DuckDuckGo search with region/safesearch/timelimit parameters; better markdown formatting; version surfaced in initialize.
See LICENSE