본문 바로가기
스터디/MCP

[MCP Study] 1. 1개의 MCP 서버와 클라이언트 통신하기

by 박사개구리 2025. 5. 6.
반응형

⛔️ 요즘 MCP가 엄청나게 화제라서 개인적으로 해당 내용을 스터디 하면서 정리한 자료입니다!

     혹시 잘못된 내용이나 오타, 개선할 사항이 있으시면 편하게 댓글 남겨주세요! 🙇‍♂️

⛳️ 목표!

  • 검색을 위한 MCP 서버를 띄우고 client에서 이 서버에 띄운 도구를 호출하여 알맞은 응답을 얻기!!

🍽️ 사전 준비 사항

  • 우선 아래와 같이 필요한 패키지를 설치합니다.
pip install langgraph langchain-openai langchain-mcp-adapters duckduckgo-search beautifulsoup4 aiohttp python-dotenv

 

  • 그리고 현재 작업을 수행중인 폴더 안에 .env 파일을 생성하고 안에 openai key를 입력합니다.
OPENAI_API_KEY=OpenAI api키를 입력하세요

 

🛠️ 실습 내용

  • 여기서는 주어진 질문과 관련된 웹페이지를 검색하고 그 url의 모든 텍스트를 반환하는 1개의 MCP 서버를 구축합니다. 그리고 클라이언트에서 이를 도구로 사용하여 LLM이 응답을 도출하는 과정을 진행합니다.

🤖 도구 사용 없이 GPT에게 질문하기

  • 우선 서버와 클라이언트 코드를 작성하는 MCP 내용을 본격적으로 진행하기 전에 아래와 같은 간단한 코드를 통해 GPT에게 오늘의 주요 뉴스에 대해 질문해보겠습니다!
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

load_dotenv()

model = ChatOpenAI(model="gpt-4.1-nano")

human_message = HumanMessage(content="오늘의 주요 뉴스 알려줘")

result = model.invoke([human_message])
print(result.content)
  • 결과
    • 죄송합니다, 오늘의 최신 뉴스에 대한 정보를 제공해 드릴 수 없습니다. 대신, 특정 분야나 주제에 대한 최근 동향이나 주요 이슈에 대해 도움을 드릴 수 있으니 원하시면 말씀해 주세요.

 

  • 역시 검색과 같은 도구를 사용하지 않은 일반 GPT-4.1-nano 모델은 오늘의 뉴스와 같이 실시간성이 필요한 응답은 할 수 없는 것을 확인했습니다 😢
  • 이제 검색을 수행하는 MCP 서버의 코드를 작성하고 MCP 클라이언트에서 서버의 검색 도구를 사용하여 적절한 오늘의 뉴스 내용을 응답하는 내용을 진행해보겠습니다! 😎

🔎 검색 서버 코드 작성

  • 우선 아래와 같이 검색을 위한 서버의 코드를 작성하고 이를 server_search.py로 저장합니다.
from mcp.server.fastmcp import FastMCP
from duckduckgo_search import DDGS
from bs4 import BeautifulSoup
import re 
import requests

mcp = FastMCP("WebSearch", 
              instructions="너는 검색된 정보를 기반으로 사용자의 질문의 응답하는 인공지능 에이전트야.")

def fetch_content(url):
    try:
        resp = requests.get(url, timeout=5)
        resp.raise_for_status()  # HTTP 에러 코드(4xx, 5xx) 발생 시 예외 처리 
        html = resp.text

        soup = BeautifulSoup(html, 'html.parser')
        text = soup.get_text(separator="\\n")
        text = re.sub(r'\\n{3,}', '\\n', text)
        cleaned = text.strip()
        return cleaned

    except Exception as e:
        return f"[Failed to fetch {url}]: {e}"

@mcp.tool()
async def search_and_answer(question: str) -> str:
    """DuckDuckGo를 통해 검색한 결과 출력하는 코드"""
    with DDGS() as ddgs:
        results = list(ddgs.text(question, max_results=3))

    if not results:
        return "검색 결과를 찾을 수 없습니다."

    # Get top result text
    content = fetch_content(results[0]['href'])

    return content

if __name__ == "__main__":
    mcp.run(transport="stdio")

  • 여기서는 duckduckgo API를 통해 사용자의 입력의 내용을 웹에서 검색하고 검색된 결과를 출력합니다. 코드의 내용을 자세히 살펴보겠습니다.

 

1. 라이브러리 불러오기 및 mcp 정의

from mcp.server.fastmcp import FastMCP
from duckduckgo_search import DDGS
from bs4 import BeautifulSoup
import aiohttp
import re 

mcp = FastMCP("WebSearch", 
              instructions="너는 검색된 정보를 기반으로 사용자의 질문의 응답하는 인공지능 에이전트야.")
  • 현재 사용하는 라이브러리들은 다음과 같습니다.
    • FastMCP: MCP 프로토콜을 따르는 서버 생성 도구
    • DDGS: DuckDuckGo 검색용 파이썬 라이브러리
    • BeatifulSoup: 검색된 url의 본문 텍스트를 추출하기 위한 라이브러리
    • aiohttp: 비동기 http 요청 처리
    • re: 정규 표현식 (이 코드에서는 줄바꿈 정리용으로 사용)
  • 그리고 FastMCP를 통해 “WebSearch”라는 이름의 MCP 서버를 생성합니다.
    • 이때 instructions는 LLM이 이 도구를 이해할 수 있도록 서버의 목적을 설명합니다.

 

2. 웹페이지 텍스트 가져오기

def fetch_content(url):
    try:
        resp = requests.get(url, timeout=5)
        resp.raise_for_status()  # HTTP 에러 코드(4xx, 5xx) 발생 시 예외 처리 
        html = resp.text

        soup = BeautifulSoup(html, 'html.parser')
        text = soup.get_text(separator="\\n")
        text = re.sub(r'\\n{3,}', '\\n', text)
        cleaned = text.strip()
        return cleaned

    except Exception as e:
        return f"[Failed to fetch {url}]: {e}"
  • 위 코드는 비동기 처리 (async)를 통해 빠른 응답을 도출하여 url에서 텍스트를 가져오고 이를 가공해서 반환합니다.
  • 함수의 각 단계는 다음과 같습니다.
    • request.get을 통해 url의 정보를 받습니다. (이때 서버 응답이 5초 이상 없으면 자동으로 실패처리합니다.)
    • resp.raise_for_status()를 통해 http의 응답 상태를 체크합니다. 만약 404나 500 같은 에러 응답을 보내면 자동으로 HTTPError 예외를 발생시킵니다.
    • html = resp.text를 통해 응답의 HTML 문자열을 텍스트로 읽어옵니다. 그리고 BeautifulSoup를 통해 HTML 문자열을 구조화 된 객체로 파싱합니다.
    • 다음으로 soup.get_text(separator="\\n")를 통해 HTML이 가진 <html>, <div> 등의 태그를 제거하고 텍스트 블럭 사이에 \n을 삽입합니다. → 이를 통해 깔끔하게 텍스트만 추출할 수 있습니다.
    • 이후 re.sub를 통해 여러번 (3번) 줄바꿈이 있는 경우 이를 1개의 줄바꿈으로 변경합니다. 그리고 text.strip()을 통해 문자열 앞뒤의 공백을 모두 제거합니다.
    • 마지막으로 이렇게 깔끔하게 정리된 문자열을 반환합니다.
  • 아래의 url의 내용을 해당 함수에 입력하면 어떤 출력이 나오는지 확인해보겠습니다.
    모델 컨텍스트 프로토콜 (MCP) - Anthropic
    Anthropic
    home page
    한국어
    Search...
    Research
    News
    Go to claude.ai
    Go to claude.ai
    Search...
    Navigation
    에이전트 및 도구
    모델 컨텍스트 프로토콜 (MCP)
    환영합니다
    사용자 가이드
    API 레퍼런스
    릴리스 노트
    프롬프트 라이브러리
    개발자 콘솔
    개발자 Discord
    지원
    시작하기
    개요
    초기 설정
    Claude 소개
    에이전트 및 도구
    Claude 코드
    컴퓨터 사용 (베타)
    모델 컨텍스트 프로토콜 (MCP)
    Google Sheets 애드온
    Claude 알아보기
    활용 사례
    모델 및 가격
    보안 및 규정 준수
    Claude로 구축하기
    성공 기준 정의
    테스트 케이스 개발
    컨텍스트 윈도우
    비전
    프롬프트 엔지니어링
    확장된 사고
    다국어 지원
    도구 사용 (function calling)
    프롬프트 캐싱
    PDF 지원
    인용
    토큰 카운팅
    배치 처리
    임베딩
    관리
    관리자 API
    리소스
    용어집
    모델 지원 중단
    시스템 상태
    Claude 3 모델 카드
    Claude 3.7 시스템 카드
    Anthropic 요리책
    Anthropic 강좌
    API 기능
    법률 센터
    Anthropic 개인정보 보호정책
    테스트 및 평가
    보안 강화
    평가 도구 사용하기
    에이전트 및 도구
    모델 컨텍스트 프로토콜 (MCP)
    MCP는 애플리케이션이 LLM에 컨텍스트를 제공하는 방법을 표준화하는 개방형 프로토콜입니다. MCP는 AI 애플리케이션을 위한 USB-C 포트와 같습니다. USB-C가 다양한 주변기기와 액세서리에 기기를 연결하는 표준화된 방법을 제공하는 것처럼, MCP는 AI 모델을 다양한 데이터 소스와 도구에 연결하는 표준화된 방법을 제공합니다.
    MCP 문서
    프로토콜에 대해 자세히 알아보고, 서버와 클라이언트를 구축하는 방법을 배우며, 다른 사람들이 만든 것들을 발견해보세요.
    Claude Desktop에서의 MCP
    Claude가 컴퓨터의 파일 시스템에서 파일을 읽고 쓸 수 있도록 하는 등 Claude Desktop에서 MCP를 설정하는 방법을 알아보세요.
    Was this page helpful?
    Yes
    No
    컴퓨터 사용 (베타)
    Google Sheets 애드온
    x
    linkedin

 

3. 검색 후 내용 가져오기

@mcp.tool()
async def search_and_answer(question: str) -> str:
    """DuckDuckGo를 통해 검색한 결과 출력하는 코드"""
    with DDGS() as ddgs:
        results = list(ddgs.text(question, max_results=3))

    if not results:
        return "검색 결과를 찾을 수 없습니다."

    # 첫번째 검색 결과의 내용 추출
    content = fetch_content(results[0]['href'])

    return content
  • 먼저 @mcp.tool()을 통해 이 search_and_answer 함수가 MCP 서버에 등록된 하나의 도구가 되도록 합니다.
  • 내부 과정에 대한 설명은 다음과 같습니다.
    • DuckDuckGo를 사용하여 입력 (question)의 내용과 관련이 높은 3개의 글을 검색하여 가져옵니다. 이때 result는 다음과 같이 제목 (title), 링크 (href), 일부 본문 (body)을 포함한 결과들을 가집니다. (사용한 입력: “MCP에 대해 설명해줘”) 
      • [{'title': 'Mcp란 무엇인가: Ai 분야 Mcp의 개념과 활용 : 네이버 블로그', 'href': 'https://m.blog.naver.com/groom1001/223816016948', 'body': '안녕하세요. 😀 오늘은 최근 ai 업계에서 화제 가 되고 있는 모델 컨텍스트 프로토콜(mcp)에 대해 알아보겠습니다. ... 예를 들어 "어제 업데이트된 코드 기준으로 설명해줘"라는 질문에, ai가 mcp로 최신 코드를 읽고 반영해서 답할 수 있습니다.'}, {'title': 'MCP(Modular Client Protocol) 완벽 정리: 개념 | 활용 사례 | 개발 방법', 'href': 'https://digit2sight.com/mcpmodular-client-protocol-완벽-정리-개념-활용-사례-개발-방법/', 'body': 'mcp는 다양한 분야에서 실용적인 적용이 가능합니다. 대표적인 활용 사례를 살펴보겠습니다: 파일 관리 자동화: 사용자: "내 다운로드 폴더에서 1gb 이상 파일을 찾아서 목록을 만들어줘" ai: mcp를 통해 파일 시스템에 접근하여 대용량 파일을 식별하고 목록화'}, {'title': 'Mcp란 무엇인가? 작동 방식 및 활용사례 총정리 - Ai 개발자가 알아야 할 모델 컨텍스트 프로토콜의 모든 것', 'href': 'https://ui-tricks.tistory.com/111', 'body': '안녕하세요, 개발자 여러분! 최근 AI 개발 생태계에서 가장 뜨거운 화제가 되고 있는 MCP(Model Context Protocol)에 대해 알고 계신가요? 2025년 4월 현재, 앤트로픽(Anthropic)이 주도하는 이 혁신적인 프로토콜은 AI 애플리케이션과 외부 도구 간의 연결 방식을 완전히 바꾸고 있습니다.'}]
    • 만약 검색된 결과가 없으면 "검색 결과를 찾을 수 없습니다."라는 텍스트를 출력합니다.
    • result의 가장 첫번째 결과의 ‘href’를 위에서 설명한 fetch_content 함수에 입력하여 해당 링크의 모든 텍스트 내용을 불러옵니다.
    • 그리고 이 텍스트를 반환합니다.

 

4. MCP 서버 실행

if __name__ == "__main__":
    mcp.run(transport="stdio")
  • 마지막으로 mcp.run을 사용하여 MCP 서버를 실행합니다.
  • 여기서는 transport를 “stdio”로 설정합니다. transport에는 대표적으로 stdio와 sse를 설정할 수 있습니다. 이에 대한 차이는 다음 내용에서 다수의 서버를 실행할 때 설명하겠습니다.
    • 일단 stdio는 웹서버를 사용하지 않고 표준입출력 기반의 하위 프로세스 (subprocess)로 실행하여 단순하고 빠르게 처리하기 위한 방법이라고 생각해주시면 될 것 같습니다.
  • 여기까지 완료되었으면 server_search.py 코드를 저장합니다.

 

🕵️ 클라이언트 코드

  • 다음과 같이 클라이언트 코드를 작성합니다.
import asyncio
from dotenv import load_dotenv
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from mcp.client.stdio import stdio_client
from mcp import ClientSession, StdioServerParameters
from langchain_mcp_adapters.tools import load_mcp_tools

# .env에서 환경 변수 불러오기 
load_dotenv()

# LLM 모델 설정 
model = ChatOpenAI(model="gpt-4.1-nano")

async def main():
    async with stdio_client(
            StdioServerParameters(
            command="python",
            args=["server_search.py"], 
        )
    ) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await load_mcp_tools(session)
            agent = create_react_agent(model, tools)
            
            response = await agent.ainvoke({"messages": "오늘 주요 뉴스 알려줘"})

            for step, item in enumerate(response['messages']):
                print(f'---------- tool response step: {step}------------')
                print(item)

            print(f"\\nFinal response: {response['messages'][-1].content}")
                
# 비동기적으로 main 함수 실행
if __name__ == "__main__":
    asyncio.run(main())
  • 위 코드의 내용을 자세히 살펴보겠습니다.

 

1. 라이브러리 불러오기

import asyncio
from dotenv import load_dotenv
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from mcp.client.stdio import stdio_client
from mcp import ClientSession, StdioServerParameters
from langchain_mcp_adapters.tools import load_mcp_tools
  • 각 라이브러리에 대한 설명은 다음과 같습니다.
    • asyncio: 비동기 IO 관리를 위한 라이브러리
    • dotenv: .env 파일로부터 환경변수 (예시: openai 키) 불러옴
    • create_react_agent: LangGraph에서 제공하는 ReAct 기반 LLM 에이전트 생성
    • ChatOpenAI: OpenAI API 호출을 위한 라이브러리
    • stdio_client: stdio 통신을 위한 MCP 클라이언트
    • ClientSession, StdioServerParameters: MCP 프로토콜 클라이언트 설정 및 통신
    • load_mcp_tools: MCP 서버로부터 도구 정보를 받아서 LangChain 도구로 사용

 

2. 환경 변수 불러오기 및 LLM 모델 설정

# .env에서 환경 변수 불러오기 
load_dotenv()

# LLM 모델 설정 
model = ChatOpenAI(model="gpt-4.1-nano")
  • 먼저 load_dotenv를 통해 현재 경로의 .env 파일의 환경 변수들을 불러옵니다.
  • 그리고 LLM 모델은 gpt-4.1-nano 모델을 사용하겠습니다.

 

3. main 함수를 비동기로 수행

async def main():
  • 다음으로 main 함수는 비동기로 수행되도록 합니다. MCP에서 외부 도구를 subprocess로 실행하거나 (stdio), SSE 서버에 접속할 때 비동기 통신이 필수적입니다.

 

4. MCP 서버와 통신 후 세션 열기

async def main():
    async with stdio_client(
            StdioServerParameters(
            command="python",
            args=["server_search.py"], 
        )
    ) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
  • server_search.py를 하위 프로세스 (subprocess)로 실행합니다 (MCP 서버의 transport가 “stdio”로 동작해야함)
  • 그리고 (read, write)라는 reader/writer 스트림 쌍을 받아옵니다.
  • 다음으로 MCP 프로토콜 세션 (session)을 열고 초기화 (initialize)를 수행합니다.

 

5. MCP 도구 불러오기 및 에이전트를 통해 응답 도출

            tools = await load_mcp_tools(session)
            agent = create_react_agent(model, tools)
            
            response = await agent.ainvoke({"messages": "오늘 주요 뉴스 알려줘"})
  • MCP 서버의 도구를 LangChain Tool객체로 변환합니다.
  • 그리고 create_react_agent에 현재 LLM 모델과 도구를 입력하여 ReAct 기반의 에이전트를 구축합니다.
    • 🚦 여기서 잠깐! ReAct 에이전트란? → ReAct 에이전트는 LLM이 문제를 해결할 때 '추론(reasoning)'과 '행동(acting)'을 번갈아 수행하며, 외부 지식이나 환경과 상호작용하면서 스스로 계획을 세우고 수정해가는 에이전트
    • 참고: https://arxiv.org/pdf/2210.03629
  • 그리고 이 에이전트에 메세지를 입력합니다. 여기서는 “오늘의 주요 뉴스를 알려줘”라는 입력을 사용하였으며 가장 처음 살펴본 것 처럼 이는 일반적인 gpt-4.1-nano 모델 API를 통해서는 응답할 수 없는 내용입니다.

 

6. 결과 출력 및 검증

            for step, item in enumerate(response['messages']):
                print(f'---------- tool response step: {step}------------')
                print(item)

            print(f"\\nFinal response: {response['messages'][-1].content}")
  • 에이전트가 진행한 각 단계를 출력하며 어떤 도구를 쓰고 중간과정을 거쳤는지 확인한 다음 최종 응답을 출력해보겠습니다.
  • 우선 각 단계를 출력한 결과가 다음과 같습니다.
---------- tool response step: 0------------
content='오늘 주요 뉴스 알려줘' additional_kwargs={} response_metadata={} id='14a0d12d-0808-4aee-85c6-5599cd9773d8'

---------- tool response step: 1------------
content='' additional_kwargs={'tool_calls': [{'id': 'call_wAi49Lm8AK59WnsN9hEzG4tr', 'function': {'arguments': '{"question":"오늘의 주요 뉴스"}', 'name': 'search_and_answer'}, 'type': 'function'}], 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 55, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-nano-2025-04-14', 'system_fingerprint': 'fp_eede8f0d45', 'id': 'chatcmpl-BREixSxOeLdQfRz5Va6KDmcgDxYNL', 'finish_reason': 'tool_calls', 'logprobs': None} id='run-cbf2daee-76dd-4994-a23b-4c8168b5f07c-0' tool_calls=[{'name': 'search_and_answer', 'args': {'question': '오늘의 주요 뉴스'}, 'id': 'call_wAi49Lm8AK59WnsN9hEzG4tr', 'type': 'tool_call'}] usage_metadata={'input_tokens': 55, 'output_tokens': 19, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}

---------- tool response step: 2------------
content='[12뉴스] 오늘의 주요뉴스\n \n뉴스\n뉴스 본문 바로가기\n전체 메뉴 바로가기\n기사 검색창 바로가기\n배너 닫기\nSBS뉴스\n2025년 4월 28일(월) \n로그인\n회원가입\nSBS\nSBS뉴스 주요 서비스\n8뉴스\n정치\n경제\n사회\n국제\n생활·문화\n스포츠\n연예\n취재파일\nTV뉴스\n[12뉴스] 오늘의 주요뉴스\n제보\nLIVE\n추천 검색어\n검색\n알림\n속보 및 알림\n최근 24시간 이내 속보 및 알림을 표시합니다.\n닫기\n전체메뉴\nSBS뉴스 전체메뉴\n분야별\n메뉴\n최신\n정치\n경제\n사회\n국제\n생활 · 문화\n스포츠\n연예\nTV 네트워크\n이슈\nTV뉴스\n메뉴\n8뉴스\n모닝와이드 1부\n모닝와이드 2부\n편상욱의 뉴스브리핑\n오뉴스\n나이트라인\n뉴스토리\n뉴스특보\n취재파일\n메뉴\n軍심戰심\n씨네멘터리\n오디오\n메뉴\n골라듣는 뉴스룸\n보이스\nSBS뉴스 브리핑\nSBS 낮 종합뉴스\n연재\n메뉴\n사실은\nSBS 단독보도\n친절한 경제\n경제 365\nD리포트\n탐사리포트\n메뉴\n끝까지 판다\n현장탐사\n비디오머그\n메뉴\n스포츠머그\n귀에 빡!종원\n교양이를 부탁해\n스토브리그\n스브스뉴스\n메뉴\n갓 나온 맛도리\n오목교 전자상가\n문명특급\n바로가기\n메뉴\n날씨\n제보\n라이브\n기자\n앱소개\nRSS\n시청자 게시판\n공지사항\n메뉴\n스브스프리미엄\n메뉴\n지식뉴스\n팟캐스트\n커뮤니티\n스퀴즈\n폴리스코어\nSBS연예뉴스\n새창 열기\nSBS Star\n새창 열기\nSBS D포럼\n새창 열기\nFOLLOW SBS NEWS\nSBS뉴스 공식 유튜브\nSBS뉴스 공식 페이스북\nSBS뉴스 공식 X\nSBS뉴스 공식 인스타그램\n전체메뉴 닫기\n사회\nLIVE\n팝업 닫기\n[12뉴스] 오늘의 주요뉴스\nSBS 뉴스\nSeoul\n작성\n2025.04.28 12:02\n조회\n조회수\n프린트\n기사 본문 프린트\n글자 크기\n크게보기\n글자크기 작게보기\n \nPIP 닫기\n  1. 해킹 사고가 발생한\xa0SK텔레콤이 오늘(28일)부터 가입자들의 유심을\xa0무료로 교체해 주면서 대리점 곳곳에서 아침부터\xa0긴 줄이 늘어섰습니다.\xa0유심보호 서비스를 신청하려는\xa0사람들도 몰리면서 온라인 접속 장애도 빚어졌습니다. \n  \n 2. 오늘 오전 청주의 한 고등학교에서 학생이 흉기를 휘둘러 교장과 교직원 등 모두 6명이 다쳤습니다.\xa0가해 학생은 특수교육 대상자로 범행 뒤 근처 저수지에 뛰어들었다 구조돼 병원으로 옮겨졌습니다. \n  \n 3. 검찰은 내일(29일) 윤 전 대통령 부부의 공천 개입 의혹 핵심 인물인 명태균 씨를 불러 조사합니다.\xa0김건희 여사 소환이 임박했단 관측이 나오는 가운데, 명 씨를 상대로 관련 의혹들에 대한 확인을 마무리할 것으로 보입니다. \n  \n 4. 민주당 이재명 대선 후보가 첫 공식일정으로 현충원을 참배한 뒤 반도체 공약을 발표했습니다.\xa0국민의힘은 오늘 2차 경선 당원투표와 국민여론조사를 마친 뒤 내일 오후 결선 진출자 2명을 발표합니다.\n \nCopyright Ⓒ SBS. All rights reserved. 무단 전재, 재배포 및 AI학습 이용 금지\n페이스북으로 공유하기\n트위터로 공유하기\n네이버로 공유하기\nSNS 공유버튼 더보기\n카카오톡으로 공유하기\n밴드로 공유하기\nurl 주소 복사하기\n복사가 완료 되었습니다.\n이 기사의 덧글 보기\n0\n이 기사 좋아요 하기\n0\n관련 뉴스\n추천 뉴스\n \n이 시각 인기기사\n"못 봤다" 25톤 트럭으로 \'쾅\'…아내 숨지게 한 50대 남성\n엉덩이 벌겋게 되도록 \'퍽퍽\'…인천 떨게한 MZ 조폭들 최후\n"66년 외로웠다, 은퇴 안 꺼내는건" 마지막 공연서 고백\n동영상 기사\n"이상한데?" 현관 뜯었다가…"집주인 해외여행" 이럴 땐\n온몸 털 빠진 채 앙상…"모른척 지나가달라" 송도 화들짝\n많이 본 뉴스\n스브스프리미엄\n스브스프리미엄이란?\n당신의 지적 탐험과 발견, 성장, 나눔의\n 세계로 이끌어줄 프리미엄 콘텐츠\n레이어 닫기\n스브스프리미엄\n더 보기\n리스트\n더 보기\n리스트\n더 보기\n페이지 최상단으로 가기\n \n \n \n \n \n \n \n \n \n \n \n닫기\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n정정보도\nSBS뉴스 전체메뉴\n분야별\n메뉴\n최신\n정치\n경제\n사회\n국제\n생활 · 문화\n스포츠\n연예\nTV 네트워크\n이슈\nTV뉴스\n메뉴\n8뉴스\n모닝와이드 1부\n모닝와이드 2부\n편상욱의 뉴스브리핑\n오뉴스\n나이트라인\n뉴스토리\n뉴스특보\n취재파일\n메뉴\n軍심戰심\n씨네멘터리\n오디오\n메뉴 리스트\n골라듣는 뉴스룸\n보이스\nSBS뉴스 브리핑\nSBS 낮 종합뉴스\n연재\n메뉴 리스트\n사실은\nSBS 단독보도\n친절한 경제\n경제 365\nD리포트\n탐사리포트\n메뉴\n끝까지 판다\n현장탐사\n비디오머그\n메뉴\n스포츠머그\n귀에 빡!종원\n교양이를 부탁해\n스토브리그\n스브스뉴스\n메뉴\n갓 나온 맛도리\n오목교 전자상가\n문명특급\n바로가기\n메뉴\n날씨\n제보\n라이브\n기자\n앱소개\nRSS\n시청자 게시판\n공지사항\n스브스프리미엄\n메뉴\n지식뉴스\n팟캐스트\n커뮤니티\n스퀴즈\n폴리스코어\n\r\n\t\t\t\t\tSBS연예뉴스\n새창 열기\n\r\n\t\t\t\t\tSBS Star\n새창 열기\n\r\n\t\t\t\t\tSBS D포럼\n새창 열기\nSBS 소개\n리스트\nSBS PR\nSBS IR\n채용정보\n이용약관\nSBS 방송편성규약\n개인정보처리방침\n시청자위원회\n영상판매\n웹광고\nSBS 제작협찬\nSBS 방송기술\nSBS 문화사업\nSBS Prism Tower 아트컬렉션\n고객센터\n사이트맵\n시청자 참여\n리스트\n공지사항\n시청자 게시판\n뉴스제보\n고충처리인(방송피해신고)\n열린TV시청자세상\n윤리경영신고 \n웹광고\n개인정보처리방침\nSBS Family\n리스트\nTY홀딩스\nSBSi 뉴스서비스부문\nSBSi\nSBS연예뉴스\nSBS Star\nSBS 콘텐츠허브\nSBS Plus\nSBS Sports\nSBS Golf\nSBS Biz\nSBS 인터내셔널\nSBS A&T\nSBS M&C\nSBS 문화재단\n서암학술장학재단\nSBS 기술인협회\nSBS 아나운서\n스튜디오 S\n지역민영방송\n리스트\n강원 민방\n광주 방송\n대구 방송\n대전 방송\n부산 방송\n울산 방송\n전주 방송\n청주 방송\n제주 방송\nSBS뉴스 공식 유튜브\nSBS뉴스 공식 페이스북\nSBS뉴스 공식 X\nSBS뉴스 공식 인스타그램\nSBS 정보\nSBS\n기사 관련문의 : 02-2061-0006\n뉴스 기사제보 : 02-2113-6000\nEmail : \nsbs8news@sbs.co.kr\n대표이사 : 방문신\n편집 책임자 : 김수형\nSBSi 정보\nSBS뉴스\n서울특별시 양천구 목동서로 161\n고객센터 : 1577-1003\nEmail : \nnewsservice@sbs.co.kr\n등록번호 : 서울 자00540\n등록일자  : 2017-09-07\n발행인 : 김기헌\n기사배열책임자, 청소년보호책임자 : 정인영\n기사배열 기본 원칙\n청소년 보호정책\nCopyright Ⓒ SBS. All rights reserved. 무단 전재, 재배포 및 AI학습 이용 금지' name='search_and_answer' id='eef0c4d3-ac5e-45c1-9847-84aa9d34bb9e' tool_call_id='call_wAi49Lm8AK59WnsN9hEzG4tr'

---------- tool response step: 3------------
content='오늘의 주요 뉴스는 다음과 같습니다:\n\n1. SK텔레콤 해킹 사고 이후 유심 무료 교체 서비스가 시작되었으며, 대리점에 긴 줄이 늘어나고 온라인 접속 장애도 발생했습니다.\n2. 청주 한 고등학교에서 학생이 흉기를 휘둘러 학생 6명이 다쳤으며, 가해 학생은 구조되어 병원으로 이송되었습니다.\n3. 검찰은 내일 윤 전 대통령 부부의 공천 개입 의혹 관련 핵심 인물인 명태균 씨를 조사할 예정이며, 김건희 여사 소환도 임박한 것으로 보입니다.\n4. 민주당 이재명 대선 후보가 현충원을 참배한 뒤 반도체 공약을 발표했고, 국민의힘은 오늘 2차 경선 당원투표와 국민여론조사를 마치고 결선 진출자를 발표할 예정입니다.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 201, 'prompt_tokens': 2258, 'total_tokens': 2459, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-nano-2025-04-14', 'system_fingerprint': 'fp_eede8f0d45', 'id': 'chatcmpl-BREizyswmiOFDByWBxdCSNswpLnNg', 'finish_reason': 'stop', 'logprobs': None} id='run-5b8cc8b6-59ba-4154-b309-7b4ebca3dca2-0' usage_metadata={'input_tokens': 2258, 'output_tokens': 201, 'total_tokens': 2459, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}

 

  • 과정을 살펴보면 아래와 같은 과정을 통해 최종 응답을 도출하는 것을 살펴볼 수 있습니다.
    • 에이전트가 사용자의 입력을 받음
    • 도구 호출, 판단 및 계획
    • 검색 도구를 사용하여 검색 내용을 받음
    • LLM이 최종 응답 수행
  • 그리고 이렇게 출력된 최종 결과는 다음과 같습니다
Final response: 오늘의 주요 뉴스는 다음과 같습니다:

1. SK텔레콤 해킹 사고 이후 유심 무료 교체 서비스가 시작되었으며, 대리점에 긴 줄이 늘어나고 온라인 접속 장애도 발생했습니다.
2. 청주 한 고등학교에서 학생이 흉기를 휘둘러 학생 6명이 다쳤으며, 가해 학생은 구조되어 병원으로 이송되었습니다.
3. 검찰은 내일 윤 전 대통령 부부의 공천 개입 의혹 관련 핵심 인물인 명태균 씨를 조사할 예정이며, 김건희 여사 소환도 임박한 것으로 보입니다.
4. 민주당 이재명 대선 후보가 현충원을 참배한 뒤 반도체 공약을 발표했고, 국민의힘은 오늘 2차 경선 당원투표와 국민여론조사를 마치고 결선 진출자를 발표할 예정입니다.
  • (2025.04.28)의 주요 뉴스에 맞게 잘 출력된 것 같습니다! 

 

7. main 함수 실행

# 비동기적으로 main 함수 실행
if __name__ == "__main__":
    asyncio.run(main())
  • 마지막으로 main 함수를 비동기적으로 실행합니다!

 

👋 결론

  • 여기까지의 내용으로 웹 검색을 수행하는 하나의 MCP 서버를 작성하고 이를 도구로 사용하여 LLM 에이전트가 응답하도록 하는 과정을 진행해보았습니다!
  • 다음 내용에서는 stdio와 다른 sse 방식으로 서버를 추가하고 에이전트가 질문에 따라 적절한 도구를 선택하여 응답할 수 있도록 구현을 진행해보겠습니다!!

 

반응형