mcp-101 / app_chatbot_mcp.py
Yoon-gu Hwang
일단 λŒμ•„λŠ” κ°€λŠ” μ½”λ“œ μΆ”κ°€
00237bb
import asyncio
import gradio as gr
from mcp import ClientSession
from mcp.client.sse import sse_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
import traceback
from typing import List, Tuple, Optional
from pprint import pprint
class MCPChatbot:
def __init__(self):
self.session: Optional[ClientSession] = None
self.agent = None
self.tools = None
self.sse_url = "http://127.0.0.1:7860/gradio_api/mcp/sse"
self.is_initialized = False
async def initialize(self):
"""MCP μ—°κ²° 및 μ—μ΄μ „νŠΈ μ΄ˆκΈ°ν™”"""
try:
# SSE ν΄λΌμ΄μ–ΈνŠΈ μ—°κ²°
self.sse_client_context = sse_client(self.sse_url)
read, write = await self.sse_client_context.__aenter__()
# ν΄λΌμ΄μ–ΈνŠΈ μ„Έμ…˜ 생성
self.session_context = ClientSession(read, write)
self.session = await self.session_context.__aenter__()
# μ„Έμ…˜ μ΄ˆκΈ°ν™”
await self.session.initialize()
# 도ꡬ λ‘œλ“œ
self.tools = await load_mcp_tools(self.session)
tool_names = [tool.name for tool in self.tools]
print(f"λ‘œλ“œλœ 도ꡬ듀: {tool_names}")
# μ—μ΄μ „νŠΈ 생성
self.agent = create_react_agent("openai:gpt-4o", self.tools)
self.is_initialized = True
return f"βœ… μ΄ˆκΈ°ν™” μ™„λ£Œ! μ‚¬μš© κ°€λŠ₯ν•œ 도ꡬ: {', '.join(tool_names)}"
except Exception as e:
error_msg = f"❌ μ΄ˆκΈ°ν™” μ‹€νŒ¨: {str(e)}\n{traceback.format_exc()}"
print(error_msg)
return error_msg
async def cleanup(self):
"""λ¦¬μ†ŒμŠ€ 정리"""
try:
if hasattr(self, 'session_context') and self.session_context:
await self.session_context.__aexit__(None, None, None)
if hasattr(self, 'sse_client_context') and self.sse_client_context:
await self.sse_client_context.__aexit__(None, None, None)
self.is_initialized = False
except Exception as e:
print(f"정리 쀑 였λ₯˜: {e}")
async def chat(self, message: str) -> str:
"""λ©”μ‹œμ§€ 처리 및 응닡 생성"""
if not self.is_initialized:
return "❌ λ¨Όμ € 'μ΄ˆκΈ°ν™”' λ²„νŠΌμ„ ν΄λ¦­ν•΄μ£Όμ„Έμš”!"
try:
# μ—μ΄μ „νŠΈμ—κ²Œ λ©”μ‹œμ§€ 전달
response = await self.agent.ainvoke({
"messages": [{"role": "user", "content": message}]
})
pprint(response)
# μ‘λ‹΅μ—μ„œ λ§ˆμ§€λ§‰ λ©”μ‹œμ§€ μΆ”μΆœ
if "messages" in response and response["messages"]:
last_message = response["messages"][-1]
if hasattr(last_message, 'content'):
return last_message.content
elif isinstance(last_message, dict) and 'content' in last_message:
return last_message['content']
else:
return str(last_message)
else:
return "응닡을 λ°›μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€."
except Exception as e:
error_msg = f"❌ 였λ₯˜ λ°œμƒ: {str(e)}\n{traceback.format_exc()}"
print(error_msg)
return error_msg
# μ „μ—­ 챗봇 μΈμŠ€ν„΄μŠ€
chatbot = MCPChatbot()
# 비동기 ν•¨μˆ˜λ“€μ„ 동기 ν•¨μˆ˜λ‘œ λž˜ν•‘
def initialize_chatbot():
"""챗봇 μ΄ˆκΈ°ν™”"""
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(chatbot.initialize())
def process_message(message: str, history: List[Tuple[str, str]]) -> Tuple[List[Tuple[str, str]], str]:
"""λ©”μ‹œμ§€ 처리"""
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
response = loop.run_until_complete(chatbot.chat(message))
# νžˆμŠ€ν† λ¦¬μ— λŒ€ν™” μΆ”κ°€
history.append((message, response))
return history, ""
def cleanup_chatbot():
"""챗봇 정리"""
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(chatbot.cleanup())
return "정리 μ™„λ£Œ"
# Gradio μΈν„°νŽ˜μ΄μŠ€ 생성
def create_interface():
with gr.Blocks(title="MCP 챗봇", theme=gr.themes.Soft()) as demo:
gr.Markdown("# πŸ€– MCP 도ꡬ 연동 챗봇")
gr.Markdown("MCP(Model Context Protocol) 도ꡬ듀을 μ‚¬μš©ν•  수 μžˆλŠ” AI μ±—λ΄‡μž…λ‹ˆλ‹€.")
with gr.Row():
with gr.Column(scale=3):
# μ±„νŒ… μΈν„°νŽ˜μ΄μŠ€
chatbot_interface = gr.Chatbot(
label="λŒ€ν™”",
height=500,
show_copy_button=True
)
with gr.Row():
msg_input = gr.Textbox(
placeholder="λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš”... (예: (3 + 5) x 12λŠ” 뭐야?)",
label="λ©”μ‹œμ§€",
scale=4
)
send_btn = gr.Button("전솑", variant="primary", scale=1)
# μ˜ˆμ‹œ λ©”μ‹œμ§€λ“€
gr.Examples(
examples=[
"(3 + 5) x 12λŠ” 뭐야?",
"1234의 μ†ŒμΈμˆ˜λΆ„ν•΄λ₯Ό ν•΄μ€˜",
"였늘 λ‚ μ”¨λŠ” μ–΄λ•Œ?",
"μ•ˆλ…•ν•˜μ„Έμš”!"
],
inputs=msg_input
)
with gr.Column(scale=1):
# μ œμ–΄ νŒ¨λ„
gr.Markdown("### πŸ› οΈ μ œμ–΄ νŒ¨λ„")
init_btn = gr.Button("πŸš€ μ΄ˆκΈ°ν™”", variant="secondary")
init_status = gr.Textbox(
label="μ΄ˆκΈ°ν™” μƒνƒœ",
value="μ΄ˆκΈ°ν™”κ°€ ν•„μš”ν•©λ‹ˆλ‹€",
interactive=False
)
cleanup_btn = gr.Button("🧹 정리", variant="secondary")
cleanup_status = gr.Textbox(
label="정리 μƒνƒœ",
interactive=False
)
# 도움말
gr.Markdown("""
### πŸ“ μ‚¬μš©λ²•
1. **μ΄ˆκΈ°ν™”** λ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ MCP μ„œλ²„μ— μ—°κ²°
2. μ΄ˆκΈ°ν™”κ°€ μ™„λ£Œλ˜λ©΄ λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ—¬ λŒ€ν™” μ‹œμž‘
3. μˆ˜ν•™ 계산, 날씨 정보 λ“± λ‹€μ–‘ν•œ 질문 κ°€λŠ₯
4. μ‚¬μš© μ™„λ£Œ ν›„ **정리** λ²„νŠΌμœΌλ‘œ μ—°κ²° μ’…λ£Œ
### πŸ”§ μ‚¬μš© κ°€λŠ₯ν•œ κΈ°λŠ₯
- μˆ˜ν•™ 계산 및 μ†ŒμΈμˆ˜λΆ„ν•΄
- 날씨 정보 쑰회
- 이미지 처리 (λ°©ν–₯ 확인, μ„Έν”Όμ•„ ν•„ν„°)
- 기타 MCP 도ꡬ듀
""")
# 이벀트 ν•Έλ“€λŸ¬ μ„€μ •
init_btn.click(
initialize_chatbot,
outputs=init_status
)
cleanup_btn.click(
cleanup_chatbot,
outputs=cleanup_status
)
# λ©”μ‹œμ§€ 전솑 처리
def handle_submit(message, history):
if not message.strip():
return history, ""
return process_message(message, history)
send_btn.click(
handle_submit,
inputs=[msg_input, chatbot_interface],
outputs=[chatbot_interface, msg_input]
)
msg_input.submit(
handle_submit,
inputs=[msg_input, chatbot_interface],
outputs=[chatbot_interface, msg_input]
)
return demo
# μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰
if __name__ == "__main__":
demo = create_interface()
demo.launch(
share=False,
)