Skip to main content
Glama

ChillMCP

by greatSumini
test_qa.py15.6 kB
""" ChillMCP 서버 QA 자동 검증 스크립트 """ import json import re import subprocess import time import sys from typing import Dict, Any, Tuple, Optional class Colors: """터미널 색상 코드""" GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[93m' BLUE = '\033[94m' BOLD = '\033[1m' END = '\033[0m' class MCPClient: """MCP 서버와 통신하는 클라이언트""" def __init__(self, boss_alertness: int = 50, boss_alertness_cooldown: int = 300): self.process = None self.boss_alertness = boss_alertness self.boss_alertness_cooldown = boss_alertness_cooldown self.request_id = 0 def _initialize_mcp(self): """MCP 프로토콜 초기화""" # Step 1: Send initialize request init_request = { "jsonrpc": "2.0", "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "test-client", "version": "1.0.0" } }, "id": 1 } self.process.stdin.write(json.dumps(init_request) + "\n") self.process.stdin.flush() self.process.stdout.readline() # Read initialize response # Step 2: Send initialized notification initialized_notif = { "jsonrpc": "2.0", "method": "notifications/initialized" } self.process.stdin.write(json.dumps(initialized_notif) + "\n") self.process.stdin.flush() time.sleep(0.5) def start_server(self): """서버 시작""" cmd = [ "python", "main.py", "--boss_alertness", str(self.boss_alertness), "--boss_alertness_cooldown", str(self.boss_alertness_cooldown) ] print(f"{Colors.BLUE}서버 시작: {' '.join(cmd)}{Colors.END}") self.process = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1 ) # 서버 초기화 대기 time.sleep(2) # 서버가 정상 시작되었는지 확인 if self.process.poll() is not None: stderr = self.process.stderr.read() raise Exception(f"서버 시작 실패: {stderr}") # MCP 초기화 self._initialize_mcp() def stop_server(self): """서버 종료""" if self.process: self.process.terminate() try: self.process.wait(timeout=5) except subprocess.TimeoutExpired: self.process.kill() self.process = None def call_tool(self, method: str, arguments: Optional[Dict[str, Any]] = None) -> Tuple[Dict[str, Any], float]: """도구 호출""" if not self.process: raise Exception("서버가 시작되지 않았습니다.") self.request_id += 1 request = { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": method, "arguments": arguments or {} }, "id": self.request_id } request_json = json.dumps(request) + "\n" # 요청 시간 측정 start_time = time.time() self.process.stdin.write(request_json) self.process.stdin.flush() # 응답 읽기 response_line = self.process.stdout.readline() elapsed_time = time.time() - start_time if not response_line: raise Exception("서버로부터 응답이 없습니다.") try: response = json.loads(response_line) return response, elapsed_time except json.JSONDecodeError as e: raise Exception(f"잘못된 JSON 응답: {response_line}\nError: {e}") def validate_response(response_text: str) -> Tuple[bool, str, Optional[Dict[str, Any]]]: """응답 텍스트 검증""" # Break Summary 추출 break_summary_pattern = r"Break Summary:\s*(.+?)(?:\n|$)" break_summary = re.search(break_summary_pattern, response_text, re.MULTILINE) # Stress Level 추출 (0-100 범위) stress_level_pattern = r"Stress Level:\s*(\d{1,3})" stress_match = re.search(stress_level_pattern, response_text) # Boss Alert Level 추출 (0-5 범위) boss_alert_pattern = r"Boss Alert Level:\s*([0-5])" boss_match = re.search(boss_alert_pattern, response_text) if not stress_match or not boss_match: return False, "필수 필드 누락", None stress_val = int(stress_match.group(1)) boss_val = int(boss_match.group(1)) if not (0 <= stress_val <= 100): return False, f"Stress Level 범위 오류: {stress_val}", None if not (0 <= boss_val <= 5): return False, f"Boss Alert Level 범위 오류: {boss_val}", None extracted_data = { "break_summary": break_summary.group(1) if break_summary else None, "stress_level": stress_val, "boss_alert_level": boss_val } return True, "유효한 응답", extracted_data def print_result(test_name: str, passed: bool, message: str = ""): """테스트 결과 출력""" status = f"{Colors.GREEN}✓ PASS{Colors.END}" if passed else f"{Colors.RED}✗ FAIL{Colors.END}" print(f"{status} {Colors.BOLD}{test_name}{Colors.END}") if message: print(f" {message}") def test_1_basic_environment(): """1. 기본 환경 및 실행 검증""" print(f"\n{Colors.BOLD}{'='*60}{Colors.END}") print(f"{Colors.BOLD}테스트 1: 기본 환경 및 실행 검증{Colors.END}") print(f"{Colors.BOLD}{'='*60}{Colors.END}\n") # 1.1 의존성 설치 확인 try: import fastmcp print_result("1.1 의존성 설치", True, "fastmcp가 정상적으로 설치되어 있습니다.") except ImportError: print_result("1.1 의존성 설치", False, "fastmcp를 찾을 수 없습니다.") return False # 1.2 서버 기본 실행 client = MCPClient() try: client.start_server() print_result("1.2 서버 기본 실행", True, "서버가 정상적으로 시작되었습니다.") # 1.3 서버 정상 종료 client.stop_server() print_result("1.3 서버 정상 종료", True, "서버가 정상적으로 종료되었습니다.") return True except Exception as e: print_result("1.2 서버 기본 실행", False, str(e)) client.stop_server() return False def test_2_command_line_parameters(): """2. 필수 요구사항 검증 (커맨드라인 파라미터)""" print(f"\n{Colors.BOLD}{'='*60}{Colors.END}") print(f"{Colors.BOLD}테스트 2: 필수 요구사항 검증{Colors.END}") print(f"{Colors.BOLD}{'='*60}{Colors.END}\n") # 2.1 boss_alertness 100% 확률 테스트 client = MCPClient(boss_alertness=100, boss_alertness_cooldown=9999) try: client.start_server() boss_levels = [] for i in range(3): response, _ = client.call_tool("watch_netflix") if "result" in response and "content" in response["result"]: text = response["result"]["content"][0]["text"] is_valid, msg, data = validate_response(text) if is_valid and data: boss_levels.append(data["boss_alert_level"]) # Boss Alert Level이 1씩 증가했는지 확인 if len(boss_levels) == 3 and boss_levels == [1, 2, 3]: print_result("2.1 boss_alertness 100% 확률", True, f"Boss Alert Level이 예상대로 증가: {boss_levels}") else: print_result("2.1 boss_alertness 100% 확률", False, f"Boss Alert Level이 예상과 다름: {boss_levels} (예상: [1, 2, 3])") client.stop_server() except Exception as e: print_result("2.1 boss_alertness 100% 확률", False, str(e)) client.stop_server() return False # 2.2 boss_alertness_cooldown 동작 테스트 client = MCPClient(boss_alertness=100, boss_alertness_cooldown=10) try: client.start_server() # Boss Alert를 1로 만들기 response1, _ = client.call_tool("show_meme") text1 = response1["result"]["content"][0]["text"] _, _, data1 = validate_response(text1) print(f" 첫 호출 후 Boss Alert Level: {data1['boss_alert_level']}") # 15초 대기 (cooldown 10초보다 길게) print(" 15초 대기 중...") time.sleep(15) # 다시 호출 response2, _ = client.call_tool("show_meme") text2 = response2["result"]["content"][0]["text"] _, _, data2 = validate_response(text2) print(f" 15초 후 호출 Boss Alert Level: {data2['boss_alert_level']}") # cooldown으로 감소했다가 다시 증가했으므로 1이어야 함 if data2['boss_alert_level'] == 1: print_result("2.2 boss_alertness_cooldown 동작", True, "Boss Alert Level이 cooldown에 따라 감소 후 증가") else: print_result("2.2 boss_alertness_cooldown 동작", False, f"Boss Alert Level이 예상과 다름: {data2['boss_alert_level']} (예상: 1)") client.stop_server() return True except Exception as e: print_result("2.2 boss_alertness_cooldown 동작", False, str(e)) client.stop_server() return False def test_3_core_features(): """3. 핵심 기능 및 상태 관리 검증""" print(f"\n{Colors.BOLD}{'='*60}{Colors.END}") print(f"{Colors.BOLD}테스트 3: 핵심 기능 및 상태 관리{Colors.END}") print(f"{Colors.BOLD}{'='*60}{Colors.END}\n") # 3.1 Stress Level 자동 증가 client = MCPClient() try: client.start_server() # 즉시 호출 response1, _ = client.call_tool("take_a_break") text1 = response1["result"]["content"][0]["text"] _, _, data1 = validate_response(text1) print(f" 초기 Stress Level: {data1['stress_level']}") # 2분(120초) 대기 print(" 120초 대기 중...") time.sleep(120) # 다시 호출 response2, _ = client.call_tool("take_a_break") text2 = response2["result"]["content"][0]["text"] _, _, data2 = validate_response(text2) print(f" 120초 후 Stress Level: {data2['stress_level']}") # 스트레스가 증가했어야 함 (감소량을 고려해도 초기보다 높아야 함) # 2분 = 2 포인트 증가 예상 if data2['stress_level'] >= data1['stress_level'] - 80: # 여유롭게 확인 print_result("3.1 Stress Level 자동 증가", True, "Stress Level이 시간에 따라 증가") else: print_result("3.1 Stress Level 자동 증가", False, f"Stress Level 변화가 예상과 다름") client.stop_server() except Exception as e: print_result("3.1 Stress Level 자동 증가", False, str(e)) client.stop_server() # 3.2 Boss Alert Level 5 도달 시 20초 지연 client = MCPClient(boss_alertness=100, boss_alertness_cooldown=9999) try: client.start_server() # Boss Alert Level을 5로 만들기 print(" Boss Alert Level을 5로 올리는 중...") for i in range(5): response, _ = client.call_tool("coffee_mission") text = response["result"]["content"][0]["text"] _, _, data = validate_response(text) print(f" 호출 {i+1}: Boss Alert Level = {data['boss_alert_level']}") # 추가 호출 시 20초 지연 확인 print(" Boss Alert Level 5에서 추가 호출...") response, elapsed = client.call_tool("deep_thinking") print(f" 응답 시간: {elapsed:.2f}초") if 18 <= elapsed <= 25: # 20초 ±5초 print_result("3.2 Boss Alert Level 5 도달 시 20초 지연", True, f"지연 시간이 예상대로: {elapsed:.2f}초") else: print_result("3.2 Boss Alert Level 5 도달 시 20초 지연", False, f"지연 시간이 예상과 다름: {elapsed:.2f}초 (예상: 20초)") client.stop_server() except Exception as e: print_result("3.2 Boss Alert Level 5 도달 시 20초 지연", False, str(e)) client.stop_server() # 3.3 모든 필수 도구 실행 client = MCPClient() try: client.start_server() tools = [ "take_a_break", "watch_netflix", "show_meme", "bathroom_break", "coffee_mission", "urgent_call", "deep_thinking", "email_organizing" ] all_passed = True for tool in tools: try: response, _ = client.call_tool(tool) if "result" in response: text = response["result"]["content"][0]["text"] is_valid, msg, data = validate_response(text) if is_valid: print(f" ✓ {tool}: {data['break_summary'][:30]}...") else: print(f" ✗ {tool}: {msg}") all_passed = False else: print(f" ✗ {tool}: 응답 오류") all_passed = False except Exception as e: print(f" ✗ {tool}: {str(e)}") all_passed = False if all_passed: print_result("3.3 모든 필수 도구 실행", True, "모든 도구가 정상 동작") else: print_result("3.3 모든 필수 도구 실행", False, "일부 도구 실행 실패") client.stop_server() except Exception as e: print_result("3.3 모든 필수 도구 실행", False, str(e)) client.stop_server() def test_4_response_format(): """4. 응답 형식 및 파싱 검증""" print(f"\n{Colors.BOLD}{'='*60}{Colors.END}") print(f"{Colors.BOLD}테스트 4: 응답 형식 및 파싱 검증{Colors.END}") print(f"{Colors.BOLD}{'='*60}{Colors.END}\n") client = MCPClient() try: client.start_server() response, _ = client.call_tool("bathroom_break") text = response["result"]["content"][0]["text"] is_valid, msg, data = validate_response(text) if is_valid: print(f" Break Summary: {data['break_summary']}") print(f" Stress Level: {data['stress_level']}") print(f" Boss Alert Level: {data['boss_alert_level']}") print_result("4.1 응답 정규표현식 파싱", True, msg) else: print_result("4.1 응답 정규표현식 파싱", False, msg) client.stop_server() except Exception as e: print_result("4.1 응답 정규표현식 파싱", False, str(e)) client.stop_server() def main(): """메인 함수""" print(f"\n{Colors.BOLD}{Colors.BLUE}ChillMCP 서버 QA 자동 검증 시작{Colors.END}\n") # 모든 테스트 실행 test_1_basic_environment() test_2_command_line_parameters() test_3_core_features() test_4_response_format() print(f"\n{Colors.BOLD}{Colors.BLUE}QA 검증 완료!{Colors.END}\n") if __name__ == "__main__": main()

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/greatSumini/hello-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server