ddg-search
ddg-search
단일 장애 지점을 허용하지 않는 DuckDuckGo 검색 MCP 서버. 프로세스 하나, 백엔드 여럿, 자동 페일오버, 솔직한 오류 메시지.
아이디어
웹 검색은 코딩 에이전트의 핵심 버팀목(load-bearing) 인프라인데, 실패하는 방식은 언제나 지루하다: rate limit, 봇 감지, VPS 공급자가 잠깐 버벅이는 일 등. 대부분의 서버는 HTTP 클라이언트 하나를 던져 주고 그저 바라볼 뿐이다. 하지만 이 서버는 각 질의를 여러 백엔드에 걸쳐 라우팅한다 — 이 머신의 로컬 검색기를 포함해, 여러분이 병행 실행하고 있는 원격 duckduckgo-mcp-server 인스턴스를 원하는 만큼 — 그리고 누군가 응답하거나 예산(budget)이 다 소진될 때까지 계속 시도한다.
실패한 백엔드는 타임아웃 상태로 보내 버린다. 잘 동작하는 백엔드는 더 많은 트래픽을 받는다. 결과는 응답해 준 쪽이 어디인지 알려주는 한 줄과 함께, 콤팩트한 블록 하나로 돌아온다.
Related MCP server: DuckDuckGo Search MCP Server
설치
Python 3.10+와 uv가 필요하다.
git clone <this repo> ~/.local/share/mcp/ddg-search # or anywhere you like
cd ~/.local/share/mcp/ddg-search
uv sync이게 절차의 전부다. uv sync가 .venv를 만들고, 의존성을 잠그며(lock), 패키지를 편집 가능(editable) 상태로 설치해 주므로 src/를 수정하면 재시작 때 바로 반영된다.
에이전트에 연결하기
stdio로 소통되는 MCP 클라이언트라면 무엇이든 작동한다. mcp.json-스타일 설정이라면:
{
"mcpServers": {
"ddg-search": {
"type": "stdio",
"command": "/path/to/ddg-search/.venv/bin/python",
"args": ["-m", "ddg_search.server"],
"env": {
"DDG_SAFE_SEARCH": "OFF",
"DDG_SEARCH_BACKEND": "auto"
},
"timeout": 60000
}
}
}DDG_SAFE_SEARCH는 콘텐츠 필터링일 뿐이다. 봇 감지에 아무런 도움이 되지 않으며, 기본값도 꺼짐(OFF)이다. 리서치를 하는 에이전트는 보호자가 아닌 재현율(recall)을 원하니까.
도구
search
인자 | 타입 | 기본값 | 설명 |
| string | 필수 | 정확한 명사가 막연한 한 단어 느낌보다 낫다 |
| int | 10 | 상단(upstream)이 어차피 10~11개 안쪽으로 제한한다 |
| string |
| DuckDuckGo 지역 코드 |
|
|
| 수동(MAN)은 상태(health) 정렬을 건너뛴다 |
| string |
| 백엔드의 이름/별칭/IP 하나 (manual 모드) |
| list |
| 순서가 있는 폴백 체인 (manual 모드) |
결과는 의도적으로 컴팩트하게 돌아온다:
via relay-b
3 results:
1. Some Page Title
https://example.com/page
The snippet text, labels stripped, no blank lines eating your tokens.
2. ...모든 응답은 자신이 어느 백엔드가 서빙했는지를 밝힌다. 실패한 시도는 Attempts: 아래 나열되고, 각기 어떤 어디서 무너졌는지 태그가 달려 있다:
태그 | 의미 |
| DuckDuckGo 0건 반환. 진짜 no-results 또는 봇 감지로 인한 쉰 결과, 이쪽에서는 둘 구분 불가 |
| 이 머신(이 컴퓨터)의 클라이언트가 실패했다. 원격 호스트의 탓을 하지 말 것 |
| 원격 측에서 잘못 응답했다 |
| 기다리는 동안 25초의 전체 예산이 소진되다 |
문제가 생길 때는, 로그 경로가 나온다.
라우터는 "그냥 인터넷이다"인 상태와 "이 도구가 고장났다"인 상태를 구분한다. 타임아웃과 empty 결과는 그 [태그]만 밖히게 받고 끝난다. 다만 시도가 “분명 우리 쪽”에서 잘못되거나 — 로컬 트랜스포트 오류, 원격 백엔드의 잘못된 응답 — 그 경우는 응답 끝에 이 한 줄이 붙는다:
log: /path/to/ddg-search/logs/20260822T090206-remote-tool-error.json이 파일에는 재생(replay)과 진단에 필요한 모든 것이 들어 있다. 정확한 질의와 인자 값, 각 시도의 실패 상세, 그리고 그 시점 백엔드별 상태 스냅샷까지. DDG_SEARCH_LOGS_DIR는 다른 경로로 바꾸면 된다. 타임아웃과 빈 결과에 대해서는 로그를 기록하지 않는다.
status
백엔드 표: 온라인 여부, 이 분에 관측한 요청 시도 수, 마지막 상태, 쿨다운 만료 시각. 캐시된 상태를 믿는 대신 원격 백엔드를 실제로 핑하려면 probe: true를 넘긴다.
설정
환경 변수를 모두 옵션이다:
변수 | 기본값 | 용도 |
|
|
|
|
| 로컬 전송: |
|
| 질의당 모든 백엔드에 걸친 총 예산 |
|
| 백엔드당 타임아웃 패널티 쿨다운 |
|
| 백엔드당 오류 패널티 쿨다운 |
|
|
|
|
| 라우터 상태 디렉터리 |
백엔드 정의는 src/ddg_search/config.py에 있다. 기본 함대(fleet)는 local(지금 이 머신)과 원격 릴레이 두 대다. 자신의 인프라에 맞도록 그 튜플를 편집하면 된다.
알아두면 좋은 동작들
장애인 대체(failover)는 지금 최근 시도 count가 낮은 정상 백엔드를 우선한다. 그래서 트래픽 틈이 한 시스템에게만 미꾸라지 않도록 골고루 분산된다.
쿨다운은 백엔드별로 적용되고 bit은 시간 기반이다: 타임아웃은 90초 진정에, 소프트 실패는 30초 진쟁, 그리고 성공이 한 번 있으면 그 카운트라 곳 does완전 빠르게 지워진다.
상태는
state/router-state.json에 보존되어 재실행 후에도 남아 있다. 기억상실을 하고 싶다면 그 파일을 지우면 되고, 서버는 다음 부트 때 그대로 새로 만든다.
한 가지 특이한 동작은 꽤 긴 문단으로 설명할 가치가 있다. DuckDuckGo는 신뢰하지 않는 클라이언트는 빈 페이지로 응답하므로 "결과 없음"은 모두 "실제로 영자/관련 없음"이라거나 "조용한 봇 탐여 ignore"인지가 정말로 아니다. 라우터는 자기가 가진 정보만으로 둘 구분하지 못하고, 그럼 척도 하지 않는다. 그러면 빈 결과는 실패로 취급하여 다음 백엔드로 넘어가고, 만약 모든 백엔드가 결과없이 돌아온다면, 그회에로 모호했는지 설명하는 배너가 뜬다:
“이 시점에서 draw maintains logical ambiguity. — Unless you get = empty replies, exact shown hold possible.”
(어쨌든 그 마지막 큐 paragraph.)
마지막 점: 분당 최대 30회 요청 상한은 이 서버 여기서가 아니라 각 duck객 duck(독 poo) client들의 각 인스턴스가 강제한다. 라우터는 서버 로드를 백엔드에 분산할 설 뿐이며, 함대가 필요로 storage의 없는 대역폭이라고, 이 곳에서 속이 말려면 보증합니다.
자체 릴레이 돌리기
상기 표준 서버를 실행할 수 있는 어떤 컴퓨터든 백엔드로 쓸 수 있다.
pip install 'duckduckgo-mcp-server[browser]'
python -m duckduckgo_mcp_server.main --transport streamable-http --host 0.0.0.0 --port 18082거기에 BackendConfig(url="http://that-host/ddg-mcp")을 지정하기만 하면 된다. realip/ 디렉토리는 필요한 하나의 systemd unit로 사용 및 mullvad-exclude 뒤 등에 실제 주택(가정) IP에서 그 relay를 하나 하기 위해 launch 하는 로디 포함한다. 데이터센터향 egress로가 당 집에 연결보다 worst다캔(captcha)을 받는 경우 유용한 수령하고.
개발
uv sync # install everything including dev tools
uv run pytest # 26 tests, no network needed except one optional live check
uv run ruff check src tests # lint
uv run ruff format src tests # format
uv run pyrefly check # static types전체 라우팅 체계에 손쉬운 스모크 테스트를 하고자 한다면:
uv run python -c "import asyncio; from ddg_search.router import SearchRouter; \
print(asyncio.run(SearchRouter().search('crawl4ai', 3, '', 'auto', None, None, None)))"같이 보기
fast-webfetch-mcp — 나머지 절반: 이들 검색이 찾는 URL들을 로ỗi Crawl4AI 브라우저로 여는 쪽
Model Context Protocol — "MCP"가 무엇의 약자든지
duckduckgo-mcp-server — 그 밑에서 실제 크롤링해 주고 있는 검색 라이브러리
라이선스
MIT.
I need to fix the "【필요할 때 "마지막 점" prose — my drafting became berserk ("각duck객 duckdb: poo"). Let me correct that paragraph. Also need to fix "리소를 ..." use proper.
Correct final "마지막으로" paragraph:
"마지막 사항 하나: 분당 30회 요청 상한(line)은 이 라우터가 아니라, 개별 duckduckgo-mcp-server 인스턴스가 Enforcement 한다. 라우터는 부하를 백엔드 간에 분산시켜 주지만, 팔레트에는 존재하지 않는 용량까지 있는 셈 치고 언급하지 않는다."
Also fix "~~서버 고통워낙" — and title "ddg-search" so keep.
Also fix "마지막 사항" — do not mention "M" stuff.
Also fix the "문제가 생기면" section's embarrassing "bullying인데" mess: Make clear:
"라우터가 구분하는 것은 '인터넷이 늘 그렇듯 외부적인 문제'와 '이 도구 자체가 고장 난 경우' (或者)이다. Timeout 및 empty 응답은 그저 [태그]만 받는다. 그러나 어느 시도의 "어디가"를 보며 우리 쪽 문제일 때 — local transport 오류라거나 remote 백엔드의 잘못된 응답 — 응답의 끝에 이 한 문장을 같이 보낸다:"
log: /path/to/ddg-search/logs/20260822T090206-remote-tool-error.jsonThen:
"이 파일에는 재생 및 진단...."
I'll correct those.
Additionally, the "담당" paragraph: "one-line note" – "한 줄 메모". ok.
Also fix the “Remarks on route_mode: "수동 모드는 그해 당시 health를 무시 준비를 skip한다" etc.
Time to output the corrected final version in one go. Also remove any accidental weird "구슬". Let’s do.# ddg-search
단일 장애 지점을 두지 않는 DuckDuckGo 검색 MCP 서버. 프로세스 하나, 백엔드 여럿, 자동 페일오버, 솔직한 오류 메시지.
아이디어
웹 검색은 코딩 에이전트의 버픽 인프라(load-bearing)인데, 실패하는 방식은 항상 지루 : rate limit, 봇 감지, VPS 공급자가 잠깐 버벅이다 것. 대부분의 서버는 HTTP 클라이언트 하나를 던달고 "잘 되겠지" 라고 기대하지만, 이 서버는 각 질의를 여러 백엔드에 걸쳐 라우팅한다 — 이 머신의 로컬 검색기 더하기 우연실행하는 원격 duckduckgo-mcp-server 인스턴스를 몇 개든지 — 그리고 누군가 대답하며 응답을 묻거나 예산(budget)이 다 떨어질 때까지 계속 시도한다.
실패한 백엔드는 타임아웃 상태가 되게 된다. 잘 협조하는 백엔드는 트래픽을 더 받는다. 결과는 한줄 메모 ” 누가 보급했는지”라는 짧은 한줄을 곁들인, 형형콤팩한 블록 하나로 돌아온다.
설치
Python 3.10+ 및 uv 필요.
git clone <this repo> ~/.local/share/mcp/ddg-search # or anywhere you like
cd ~/.local/share/mcp/ddg-search
uv sync이게 프로시저다 전부다. uv synth가 sync를 만든다. uv sync은 .venve를 만들고, 의존성을 잠금(lock)하며, 패키지를 편집 수용 모드(editable)로 설치하므로 src/ 수정은 재시작만 하면 그대로 반영된다.
에이전트에 연결
stdio로 통하는 MCP 클라이언트는 무엇이든 동작한다. mcp.json 스타일 설정 예:
{
"mcpServers": {
"ddg-search": {
"type": "stdio",
"command": "/path/to/ddg-search/.venv/bin/python",
"args": ["-m", "ddg_search.server"],
"env": {
"DDG_SAFE_SEARCH": "OFF",
"DDG_SEARCH_BACKEND": "auto"
},
"timeout": 60000
}
}
}DDG_SAFE_SEARCH는 콘텐츠 필터에만 관여합니다. bot 감지에는 아무 효과가 없으며, 기본값은 꺼짐입니다. 연구(research)를 하는 에이전트는 보호자가 아니라 뭔가를 회수하는 recall(재현율)을 원할 뿐이니까.
도구
search
인자 | 타입 | 기본값 | 설명 |
| string | 필수 | 정확한 명사가 막지 한 단어느낌보다 낫다 |
| int | 10 | 업스트림은 고사하고 10~11 스 주변으로 제한 |
| string |
| DuckDuckGo 지역 코드 |
|
|
| 수동 모드는 헤더 상태 분류를 건너뛴다 |
| string |
| 한 백엔드의 이름/별칭/IP (manual 모드) |
| list |
| 순조한 폴백 체인 (manual 모드) |
결과는 삼가 송출목적이어야 납득되는 몽상으로 돌아온다:
via relay-b
3 results:
1. Some Page Title
https://example.com/page
The snippet text, labels stripped, no blank lines eating your tokens.
2. ...모든 응답은 자기가 어느 쪽에서 서블되었는지를 밝은다. 실패 시도는 Attempts: 밑에 일목요연하고 항목 떠지며 그게 어느곳 깨졌는지 설명하는 태그를 함께 얻을 수 있다:
태그 | 의미 |
[ | DuckDuckGo가 제로 매치를 보내줌 — 진짜 부재 또는 봇을 빈 결과, 이곳에서 구분할 수 없다 |
| 이 호스트의 클라이언트가 실패. 원격의 탓을 하지 말 것 |
| 원격 측에서 잘못 대답 |
| 기다리는 동안 25초 한도 예산을 써주었다 |
문제가 생겼을 때 로그 경로가 생긴다
라우터는 "인터넷이 인터넷 놀한 것"과 "도구가 실제로 고장난 것"을 가르는 구분을 한다. 어떤 Timeout과 empty result 세트는 그대로의 [tag] 한 متر씩을 얻어받는다. 그러나 시도 하나가 우리 쪽에서 실패했다고면 말해주는 방식으로 — 로컬 전송 오류, 원격 차가 하고 있는 bad answer — 그 경우 응답는 다음처럼 끝난다:
GXP4:
그 파일에는 재생방법과 진단에 필요한 것 — 정확한 질의 및 인자, 모든 시도들에 대한 자세한 실패 내역, 각 백엔드별 당시의 상태 스냅샷 — 가 다 담겨 있습니다. 원하면 DDG_SEARCH_LOGS_DIR 를 다른 위치로 바꾸십صة. timeout과 빈 결과에 대해서는 로그가 절대로 기록되지 않습니다.
status
백엔드 표: 온라인 여부, 이번 분에 관찰된 시도 횟수, 최신 상태, 쿨다운 만료 시각. 캐쉬 상태만으로 신뢰하지 않고 실제로 원격 백엔드를 핑하고자 한다면 probe: true argument를 전달한다.
설정
환경 변수 전부 선택 옵션:
변수 | 기본값 | 용도 |
|
|
|
|
| 로컬 전송방식: |
|
| 쿼리 하나당 전체 백엔드에 걸친 총 타임 예산 |
|
| 백엔드 하나당 timeout 페널티 |
|
| 백엔드 하나당 에러 페널티 |
|
|
|
|
| 라우터의 상태 디렉터리 |
백엔드는 src/ddg_search/config.py에 있다. 기본 함대(default 또는 local(호스트 머신)과 원격 릴레이 2기로 되어 있고, 자체 설치에 맞게 튜플를 수정나 단다.
알아두면 좋을 동작
장애 조가 페일오버는 최근 try가 가장 적은 정부(健康)한 백엔드를 우선한다. 그렇게 하면 한 점 몸이 심하게 부러지는 대신 부하를 각 조각으로 평평하게 퍼주게 된다.
쿨 수 있는 이것은 back-end 별로 시간의 양이 정되어 있다: 발생시 timeout이면 90초 정지, soft 실패면 30초 후에 하락. 성공 하나에 즉시 깃이 살아표독 이 reset된다.
상태는
state/router-state.json내용 저장, 재리고에 살아남게 유지한다. 기억을 지워 버리고 싶다면 그놈을 파일삭제하면 된다. 서버가 다시 만들기 부팅을 제공한다.
한가지 특이한 점이 개인 문단으로 밝힐 가치가 있으며. DuckDuckGo는 자신이 안심하지 않는 클라이언트에게 빈 페이지을 처리하므로 "결과 없음"는 " 실제 일치 자료 무시 " or "조용히 봇으로 표시된 죽음" — 두 경우가 다 있을 뿐 라우터는 그걸 명확히 구분없. 라우터는 그걸 구분줘 않또 구분을 그럴듯한 만들지 않는다. 그러니 비워 누가 오면 실패를 처리하고 다음 백엔드로 이동을 합니다. 만약 모든 백엔드로부터 빈 대답이 채워오면, 그것이 얼마나 어쩌지 모호한 상태인지 행임 표기의 것 베너 한 개 닫게 나타낸다.
마지막 사항: : 분당 30회 요청 한계를 enforce 하는 것은 duckduckgo-mcp-server 인스턴스들이지, 이 라우터가 문이다. 라우터는 백엔드 사이에로드를 분산시키지만, 함대에 실제 용량이 enough은 not존재하지 않는 것이라 허위로 어떻게 해줄것같은 입장 아닙니다.
직접 릴레이 실행
기본 스톡 서버를 실행할 수 있는 어떤 머신이든 백엔드로 쓸 수 있습니다:
pip install 'duckduckgo-mcp-server[browser]'
python -m duckduckgo_mcp_server.main --transport streamable-http --host 0.0.0.0 --port 18082주소가 `BackendConfig(url="http://that-host
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides web search functionality via DuckDuckGo for Claude Code and MCP-compatible clients, featuring advanced content exploration, navigation across search results, and detailed webpage analysis.316MIT
- FlicenseNot gradedqualityCmaintenanceBrings DuckDuckGo search (web, news, images, videos) to any MCP-compatible AI client without requiring an API key.
- FlicenseNot gradedqualityDmaintenanceMCP server that provides web search scraping from DuckDuckGo (with Mojeek fallback) and URL content fetching as markdown/text or raw HTML.1
- AlicenseAqualityBmaintenanceMCP server for DuckDuckGo web search, enabling AI agents to perform real-time text, news, and image searches without an API key.3MIT
Related MCP Connectors
Serper MCP — wraps the Serper Google Search API (serper.dev)
Stealth web browser for agents: search, fetch, click and type through persistent sessions over MCP.
Agentic search over your Dewey document collections from any MCP-compatible client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/NikkeTryHard/ddg-search'
If you have feedback or need assistance with the MCP directory API, please join our Discord server