Windows + Docker Desktop 환경
searxng 설치 및 실행
docker-compose.yml 내용
version: "3.9"
services:
searxng:
image: searxng/searxng:latest
container_name: searxng
ports: ["8080:8080"]
environment:
- SEARXNG_SECRET=${SEARXNG_SECRET}
- BASE_URL=localhost:8080/
volumes: ["./searxng:/etc/searxng"]
restart: always
docker compose up -d 로 한 번 실행 후
./searxng 에 있는 settings.yml 수정 후 container 재기동
use_default_settings: true
server:
bind_address: "0.0.0.0"
secret_key: "CHANGE_THIS_TO_SOMETHING_SECURE" # Generate a random key
port: 8080
search:
safe_search: 0
formats:
- html
- json
engines:
- name: google
engine: google
shortcut: g
- name: duckduckgo
engine: duckduckgo
shortcut: d
- name: bing
engine: bing
shortcut: b
- name: startpage
disabled: true
server.limiter: false
claude app mcp 설정
claude_desktop_configuration.json
{
"mcpServers": {
"searxng": {
"name": "searxng",
"command": "npx",
"args": [
"-y",
"@kevinwatt/mcp-server-searxng"
],
"env": {
"SEARXNG_INSTANCES": "http://localhost:8080,https://searx.example.com",
"SEARXNG_USER_AGENT": "CustomBot/1.0",
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
}
테스트 용 파이썬 샘플
"""
Python 3.11+
pip install langchain-mcp-adapters langgraph langchain-openai
환경 변수 OPENAI_API_KEY 필수
"""
import asyncio, os, signal, sys, warnings
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
# Windows 특유의 이벤트 루프 종료 오류 방지 - ProactorEventLoop 이슈 패치
if sys.platform == "win32":
# ResourceWarning 무시 설정
warnings.filterwarnings("ignore", category=ResourceWarning)
# 파이프 및 서브프로세스 종료 오류 패치
def silence_event_loop_closed(func):
def wrapper(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except (RuntimeError, ValueError) as e:
if str(e) not in ('Event loop is closed', 'I/O operation on closed pipe'):
raise
return wrapper
# _ProactorBasePipeTransport.__del__ 패치
_ProactorBasePipeTransport = asyncio.proactor_events._ProactorBasePipeTransport
if hasattr(_ProactorBasePipeTransport, '__del__'):
_ProactorBasePipeTransport.__del__ = silence_event_loop_closed(_ProactorBasePipeTransport.__del__)
# BaseSubprocessTransport.__del__ 패치 추가
_BaseSubprocessTransport = asyncio.base_subprocess.BaseSubprocessTransport
if hasattr(_BaseSubprocessTransport, '__del__'):
_BaseSubprocessTransport.__del__ = silence_event_loop_closed(_BaseSubprocessTransport.__del__)
# ▶ 1. MCP 서버 실행 정의 ──────────────────────────────────────────────
MCP_CFG = {
"searxng": {
"name": "searxng",
"command": "npx",
"args": [
"-y",
"@kevinwatt/mcp-server-searxng"
],
"env": {
"SEARXNG_INSTANCES": "http://localhost:8080,https://searx.example.com",
"SEARXNG_USER_AGENT": "CustomBot/1.0",
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
# ▶ 2. LLM & 프롬프트 설정 ─────────────────────────────────────────────
MODEL_NAME = "gpt-4o-mini" # 도구 호출 지원 모델
SYSTEM_PROMPT = (
"너는 최신 정보를 수집하는 검색 비서이다. "
"대답을 작성하기 전에 **반드시** 'web_search' 툴을 호출해 "
"최신 웹 결과를 확보해야 한다. "
"툴을 호출한 후에는 검색 결과를 요약·해석하여 한국어로 간결하게 답하라."
)
PROMPT = ChatPromptTemplate.from_messages([
("system", SYSTEM_PROMPT),
MessagesPlaceholder("messages")
])
# ▶ 3. 실행 함수 ───────────────────────────────────────────────────────
async def main() -> None:
async with MultiServerMCPClient(MCP_CFG) as client:
tools = client.get_tools()
print("▶ MCP tools 로드 완료:", [t.name for t in tools])
# ReAct Agent 생성
model = ChatOpenAI(model=MODEL_NAME)
agent = create_react_agent(model, tools, prompt=PROMPT)
# 예시 질의
query = "2025년 4월 26일 기준 대한민국에서 국민의 힘 대선 후보 경선 현황을 알려줘"
response = await agent.ainvoke({"messages": query})
print("\n▼ 최종 답변\n", response["messages"][-1].content)
# 메인 실행 부분
if __name__ == "__main__":
# OPENAI_API_KEY 환경변수 확인
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY 가 설정되지 않았습니다."
# Windows 시그널 처리 개선
if sys.platform == 'win32':
signal.signal(signal.SIGINT, signal.SIG_DFL)
try:
# Windows에서는 ProactorEventLoop 사용 (기본값)
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
# asyncio.run() 사용
asyncio.run(main())
except KeyboardInterrupt:
print("\n프로그램이 사용자에 의해 중단되었습니다.")