├── .cursor ├── mcp.json └── rules │ └── report.mdc ├── .env.example ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.ko.md ├── README.md ├── assets └── cursor_setting.png ├── docker-compose.yml ├── mysql_mcp_server ├── executors │ ├── __init__.py │ ├── create_table.py │ ├── desc_table.py │ ├── explain.py │ ├── insert_query.py │ ├── insight_starter.py │ ├── invoke_viz_pro.py │ ├── select_query.py │ └── show_tables.py ├── helper │ ├── __init__.py │ ├── db_conn_helper.py │ ├── logger_helper.py │ └── tool_decorator.py └── main.py ├── requirements.txt ├── showcase ├── 2019_sales_report.html ├── report.html └── sales_report_2020.html └── test ├── test_execute.py ├── test_sales_insert.py └── test_tool_list.py /.cursor/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "mysql-mcp": { 4 | "url": "http://localhost:8081/sse" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /.cursor/rules/report.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 보고서를 작성할 때 참고하세요. 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # Cursor Rules for Dashboard Report Template 7 | 8 | ## 보고서 개요 9 | - 역할: 데이터 분석 대시보드 HTML/CSS 템플릿 생성기 10 | - 기술 스택: HTML5, Tailwind CSS, ApexCharts, Pretendard 폰트 11 | - 목표: 일관된 디자인과 구조를 가진 데이터 분석 보고서 페이지 생성, 사용자 요청에 따라 색상 커스터마이징 가능 12 | - 필수 포함 사항: 이 보고서 내용을 기반으로 한 결과나 데이터를 기반으로 한 인사이트가 도출 된 내용이 포함 13 | 14 | ## 파일 구조 규칙 15 | - 파일 확장자: `.html` 16 | - DOCTYPE 선언으로 시작: `` 17 | - 언어 설정: `` 18 | - 문자 인코딩: `` 19 | - 뷰포트 설정: `` 20 | - 제목 형식: `분석 보고서 대시보드` (또는 유사한 주제 기반 제목) 21 | 22 | ## 외부 리소스 23 | - Tailwind CSS: `` 24 | - Feather Icons: `` 25 | - ApexCharts: `` 26 | - Pretendard 폰트: `` 27 | 28 | ## CSS 스타일 규칙 29 | - 루트 변수 정의: 30 | - 사용자가 색상을 제공하지 않을 경우 기본값 사용: 31 | ``` 32 | :root { 33 | --primary-color: #8A6AF2; /* 기본값, 사용자 지정 시 대체 */ 34 | --primary-light: #B39DFF; /* 기본값, 사용자 지정 시 대체 */ 35 | --primary-dark: #6845E0; /* 기본값, 사용자 지정 시 대체 */ 36 | --accent-color: #F3F0FF; /* 기본값, 사용자 지정 시 대체 */ 37 | --text-primary: #333; 38 | --text-secondary: #666; 39 | } 40 | ``` 41 | - 사용자 요청 시: 사용자가 제공한 색상으로 `--primary-color`, `--primary-light`, `--primary-dark`, `--accent-color`를 동적으로 설정 42 | - 기본 스타일: 43 | - 폰트: `font-family: 'Pretendard', sans-serif;` 44 | - 배경색: `background-color: #F8F7FC;` (사용자가 요청 시 변경 가능) 45 | - 텍스트 색상: `color: var(--text-primary);` 46 | - 클래스 기반 스타일: 47 | - `.dashboard-card`: 흰색 배경, 둥근 모서리 (`rounded-lg`), 그림자 효과 (`shadow-md`), 호버 시 살짝 올라가는 애니메이션 (`hover:-translate-y-1`) 48 | - `.card-header`: 하단 경계선 (`border-b`), 플렉스 레이아웃 (`flex justify-between items-center`), 제목 스타일 49 | - `.analysis-section`: 왼쪽 테두리 (`border-l-4`) 색상은 `var(--primary-color)`, 배경색 `var(--accent-color)` 50 | - `.header-section`: 그라데이션 배경 (`bg-gradient-to-r from-[var(--primary-light)] to-[var(--primary-color)]`), 둥근 모서리, 그림자 효과 51 | - `.stat-card`: 중앙 정렬 (`text-center`), 그림자 포함 (`shadow-sm`) 52 | 53 | ## HTML 구조 규칙 54 | - 최상위 컨테이너: `
` 55 | - 헤더 섹션: 56 | - 클래스: `header-section` 57 | - 구조: 58 | ``` 59 |
60 |

[타이틀]

61 |

[서브타이틀]

62 |
63 | [stat-card 반복] 64 |
65 |
66 | ``` 67 | - 통계 카드: `
[값]
[라벨]
` 68 | - 차트 섹션: 69 | - 그리드 레이아웃: `
` 70 | - 각 차트 카드: 71 | ``` 72 |
73 |
74 |

[차트 제목]

75 |
76 |
77 |
78 |
79 |
80 |
81 |

분석 결과

82 |

[분석 내용]

83 |
84 |
85 |
86 | ``` 87 | 88 | ## 콘텐츠 규칙 89 | - 제목: `header-title`은 `text-3xl`, `header-subtitle`은 `text-lg` 90 | - 차트 제목: `.card-title`은 `font-weight: 600`, `font-size: 1.1rem` 91 | - 분석 섹션: `

`는 `font-medium text-[var(--primary-dark)] mb-2`, `

`는 `text-sm` 92 | - 반응형 디자인: 93 | - 모바일: `grid-cols-1` 94 | - 데스크톱: `lg:grid-cols-2` 또는 `md:grid-cols-4` (헤더 통계 카드) 95 | 96 | ## 색상 커스터마이징 규칙 97 | - 사용자가 색상을 요청하면: 98 | 1. 제공된 색상으로 `--primary-color`, `--primary-light`, `--primary-dark`, `--accent-color`를 ` 93 | 94 | 95 |

96 |
97 |

2019년 판매 분석 보고서

98 |

연간 판매 현황 및 트렌드 분석

99 | 100 |
101 |
102 |
₩960,191
103 |
총 판매액
104 |
105 |
106 |
23,711
107 |
총 상품 수
108 |
109 |
110 |
₩101,631
111 |
최고 매출 월(11월)
112 |
113 |
114 |
₩13,443
115 |
최고 판매 상품 매출
116 |
117 |
118 |
119 | 120 |
121 | 122 |
123 |
124 |

상품 유형별 판매액 (2019)

125 |
126 |
127 |
128 |
129 |
130 |
131 |

분석 결과

132 |

2019년 주류 판매에서 리큐어(LIQUOR)가 총 357,077원으로 가장 높은 매출을 기록했으며, 와인(WINE)과 맥주(BEER)가 그 뒤를 이었습니다. 주류 외 상품(NON-ALCOHOL)은 상대적으로 낮은 판매액을 보였습니다.

133 |
134 |
135 |
136 | 137 | 138 |
139 |
140 |

월별 판매 추이 (2019)

141 |
142 |
143 |
144 |
145 |
146 |
147 |

분석 결과

148 |

2019년 판매 추이는 연말로 갈수록 증가하는 경향을 보였으며, 특히 11월에 101,631원으로 가장 높은 매출을 기록했습니다. 계절적 요인으로 5월(94,953원)과 여름철(6-8월)에도 높은 판매액을 보였습니다.

149 |
150 |
151 |
152 | 153 | 154 |
155 |
156 |

상위 10개 판매 상품 (2019)

157 |
158 |
159 |
160 |
161 |
162 |
163 |

분석 결과

164 |

2019년 최고 판매 상품은 'TITO'S HANDMADE VODKA - 1.75L'로 13,443원의 매출을 기록했습니다. 상위 10개 상품 중 맥주 제품이 7개로 다수를 차지했으며, 그 중 'CORONA EXTRA LOOSE NR - 12OZ'가 12,100원으로 2위를 차지했습니다.

165 |
166 |
167 |
168 | 169 | 170 |
171 |
172 |

주요 공급업체별 판매액 (2019)

173 |
174 |
175 |
176 |
177 |
178 |
179 |

분석 결과

180 |

'E & J GALLO WINERY'가 73,057원으로 가장 높은 매출을 기록한 공급업체였으며, 'DIAGEO NORTH AMERICA INC'와 'CONSTELLATION BRANDS'가 그 뒤를 이었습니다. 상위 10개 공급업체가 전체 판매의 상당 부분을 차지하고 있습니다.

181 |
182 |
183 |
184 |
185 |
186 | 187 | 318 | 319 | -------------------------------------------------------------------------------- /showcase/report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 2018년 판매 상품 분석 보고서 7 | 8 | 9 | 10 | 11 | 88 | 89 | 90 |
91 |
92 |

2018년 판매 상품 분석 보고서

93 |

2018년 1-2월 판매 데이터 기반 분석

94 |
95 |
96 |
26,445
97 |
총 판매 기록
98 |
99 |
100 |
15,522
101 |
총 상품 수
102 |
103 |
104 |
153,596
105 |
총 소매 판매액
106 |
107 |
108 |
302
109 |
공급업체 수
110 |
111 |
112 |
113 | 114 |
115 |
116 |
117 |

제품 유형별 판매 비율

118 |
119 |
120 |
121 |
122 |
123 |
124 |

분석 결과

125 |

와인 제품이 38.5%로 가장 높은 매출 비중을 차지했으며, 주류(37.4%)와 맥주(23.0%)가 그 뒤를 이었습니다. 비알코올 제품은 1.0%로 매우 적은 비중을 차지했습니다. 와인과 주류가 전체 매출의 75% 이상을 차지하는 것으로 보아 이 두 카테고리에 마케팅 자원을 집중하는 것이 효과적일 것입니다.

126 |
127 |
128 |
129 | 130 |
131 |
132 |

월별 판매 추이

133 |
134 |
135 |
136 |
137 |
138 |
139 |

분석 결과

140 |

1월에서 2월로 넘어가면서 판매액이 75,792원에서 77,804원으로 2.7% 증가했습니다. 판매 기록 수는 소폭 감소했지만 판매 금액은 증가했다는 점에서 고가 상품의 141 | 판매 비중이 높아졌음을 알 수 있습니다. 이러한 증가 추세가 계속된다면 연간 목표 달성이 가능할 것으로 전망됩니다.

142 |
143 |
144 |
145 |
146 | 147 |
148 |
149 |
150 |

인기 상품 TOP 5

151 |
152 |
153 |
154 |
155 |
156 |
157 |

분석 결과

158 |

상위 5개 인기 상품 중 맥주 제품이 3개, 주류 제품이 2개를 차지했습니다. 특히 코로나와 하이네켄 맥주가 상위권을 차지하고 있어 수입 맥주에 대한 선호도가 높은 것으로 보입니다. 보드카 제품(티토스, 보우맨스)도 높은 판매액을 기록했습니다. 이 상위 제품들의 재고를 충분히 유지하는 것이 중요합니다.

159 |
160 |
161 |
162 | 163 |
164 |
165 |

주요 공급업체 TOP 5

166 |
167 |
168 |
169 |
170 |
171 |
172 |

분석 결과

173 |

E & J GALLO WINERY와 CONSTELLATION BRANDS가 각각 11,568원과 11,413원의 판매액으로 최상위 공급업체입니다. 상위 5개 공급업체가 전체 판매액의 약 31%를 차지하고 있어 이들과의 전략적 파트너십이 중요합니다. 특히 E & J GALLO는 가장 많은 464개의 제품 라인업을 보유하고 있어 다양한 소비자 취향을 충족시킬 수 있는 중요한 파트너입니다.

174 |
175 |
176 |
177 |
178 | 179 |
180 |
181 |

종합 분석 및 제언

182 |
183 |
184 |
185 |
186 |

주요 발견점

187 |
    188 |
  • 와인과 주류 제품이 전체 판매액의 약 76%를 차지
  • 189 |
  • 맥주 제품이 창고 판매량의 대부분(82%)을 차지
  • 190 |
  • 인기 상품 중 맥주 브랜드(코로나, 하이네켄)의 강세 두드러짐
  • 191 |
  • 소수의 대형 공급업체가 시장 점유율의 큰 부분을 차지
  • 192 |
  • 2월 판매 데이터는 1월 대비 판매액 증가 추세 보임
  • 193 |
194 |
195 |
196 |

전략적 제언

197 |
    198 |
  • 와인과 주류 카테고리에 마케팅 자원 집중 투자
  • 199 |
  • 인기 맥주 브랜드의 재고 최적화 및 프로모션 강화
  • 200 |
  • E & J GALLO, CONSTELLATION BRANDS와 같은 주요 공급업체와의 관계 강화
  • 201 |
  • 판매 증가 추세를 유지하기 위한 시즌별 프로모션 전략 개발
  • 202 |
  • 고객 선호도가 높은 보드카 제품군 확대 검토
  • 203 |
204 |
205 |
206 |
207 |
208 |
209 | 210 | 346 | 347 | -------------------------------------------------------------------------------- /showcase/sales_report_2020.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 2020년 영업 판매 실적 보고서 7 | 8 | 9 | 10 | 11 | 87 | 88 | 89 |
90 |
91 |

2020년 영업 판매 실적 분석

92 |

연간 주요 영업 실적 및 분석 결과

93 | 94 |
95 |
96 |
174.54만원
97 |
총 소매 판매액
98 |
99 |
100 |
138.50만원
101 |
창고 판매액
102 |
103 |
104 |
174.54만원
105 |
전체 판매액
106 |
107 |
108 |
4
109 |
기록 월 수
110 |
111 |
112 |
113 | 114 |
115 |
116 |
117 |

월별 매출 추이 (2020년)

118 |
119 |
120 |
121 |
122 |
123 |
124 |

분석 결과

125 |

2020년 7월에 가장 높은 매출을 기록했으며, 1월에 가장 낮은 매출을 기록했습니다. 3월과 9월은 중간 수준의 매출을 보여주고 있습니다. 판매 추세는 상반기에 지속적으로 상승하는 경향을 보이다가 9월에 소폭 하락했습니다.

126 |
127 |
128 |
129 | 130 |
131 |
132 |

매출 채널 비교 (소매 vs 창고)

133 |
134 |
135 |
136 |
137 |
138 |
139 |

분석 결과

140 |

2020년 전체적으로 창고 판매가 소매 판매보다 높은 비중을 차지했습니다. 창고 판매는 매월 소매 판매의 3-4배 수준을 유지하고 있으며, 특히 7월에 가장 큰 창고 판매 실적을 보였습니다. 이는 하계 시즌 대규모 주문에 기인한 것으로 분석됩니다.

141 |
142 |
143 |
144 |
145 | 146 |
147 |
148 |
149 |

상품 유형별 판매 비중

150 |
151 |
152 |
153 |
154 |
155 |
156 |

분석 결과

157 |

2020년 판매에서 맥주(BEER) 카테고리가 전체 매출의 약 80%를 차지하며 가장 높은 비중을 보였습니다. 와인(WINE)과 주류(LIQUOR)가 그 뒤를 이었으며, 비알콜 제품과 기타 카테고리는 상대적으로 낮은 매출 기여도를 보였습니다. 맥주 카테고리의 높은 매출은 창고 판매의 영향이 큰 것으로 분석됩니다.

158 |
159 |
160 |
161 | 162 |
163 |
164 |

소매 vs 창고 채널 판매 분석

165 |
166 |
167 |
168 |
169 |
170 |
171 |

분석 결과

172 |

맥주 카테고리는 창고 판매가 압도적으로 높았으며, 주류 카테고리는 반대로 소매 판매가 높게 나타났습니다. 와인은 창고와 소매 판매가 비교적 균형있게 분포되어 있으며, 비알콜 제품은 소매 판매 비중이 높게 나타났습니다. 이는 제품 유형별로 판매 채널 전략을 달리해야 함을 시사합니다.

173 |
174 |
175 |
176 |
177 | 178 |
179 |
180 |

2020년 종합 판매 분석 인사이트

181 |
182 |
183 |
184 |

핵심 인사이트

185 | 186 |
187 |
188 |

계절성 판매 패턴

189 |

여름 시즌(7월)에 가장 높은 판매량을 기록했으며, 겨울(1월)에 가장 낮은 판매량을 보였습니다. 이는 계절에 따른 소비 패턴을 고려한 재고 관리와 마케팅 전략이 필요함을 시사합니다.

190 |
191 |
192 |

채널별 판매 전략

193 |

창고 판매는 맥주 카테고리에서 강세를 보인 반면, 주류와 비알콜 제품은 소매 채널에서 더 좋은 성과를 거두었습니다. 제품 유형별로 판매 채널 접근법을 최적화할 필요가 있습니다.

194 |
195 |
196 | 197 |
198 |
199 |

상품 포트폴리오 분석

200 |

맥주 카테고리가 총 매출의 약 80%를 차지하여 매출 집중도가 높습니다. 와인과 주류 카테고리를 강화하여 포트폴리오 다각화가 필요합니다.

201 |
202 |
203 |

성장 기회 영역

204 |

비알콜 제품 카테고리는 현재 매출 기여도는 낮지만, 소매 채널에서 상대적으로 좋은 성과를 보이고 있어 향후 성장 잠재력이 있습니다. 건강 트렌드에 맞춘 제품 라인업 확대를 고려해볼 수 있습니다.

205 |
206 |
207 | 208 |

권장 조치사항

209 |
    210 |
  • 계절성을 고려한 재고 관리 시스템 도입 - 여름철 맥주 수요 증가에 대비한 선제적 재고 확보
  • 211 |
  • 주류 카테고리의 창고 판매 증대를 위한 B2B 영업 전략 강화
  • 212 |
  • 와인 카테고리 소매 마진 개선을 위한 프리미엄 제품 라인업 확대
  • 213 |
  • 비알콜 제품 라인 확대 및 마케팅 강화로 건강 음료 시장 공략
  • 214 |
  • 데이터 수집 빈도 개선 - 현재 분기별 데이터를 월별 데이터로 세분화하여 더 정확한 추세 분석 필요
  • 215 |
216 |
217 |
218 |
219 |
220 | 221 | 368 | 369 | -------------------------------------------------------------------------------- /test/test_execute.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | import sys 4 | import unittest 5 | 6 | # 현재 디렉토리를 Python 경로에 추가 7 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import pymysql.cursors 10 | 11 | from mysql_mcp_server.executors.select_query import execute_select_query 12 | from mysql_mcp_server.helper.db_conn_helper import DatabaseManager 13 | 14 | 15 | class TestExecute(unittest.TestCase): 16 | def setUp(self): 17 | self.db = DatabaseManager.get_instance() 18 | mysql_host: str = os.getenv("MYSQL_HOST", "localhost") 19 | mysql_port: int = int(os.getenv("MYSQL_PORT", 3306)) 20 | mysql_user: str = os.getenv("MYSQL_USER", "root") 21 | mysql_password: str = os.getenv("MYSQL_PASSWORD", "mcpTest1234!!!") 22 | mysql_database: str = os.getenv("MYSQL_DATABASE", "mcp_test") 23 | self.mysql_config = { 24 | "host": mysql_host, 25 | "port": mysql_port, 26 | "user": mysql_user, 27 | "password": mysql_password, 28 | "database": mysql_database, 29 | "cursorclass": pymysql.cursors.DictCursor, 30 | } 31 | self.db.connect(self.mysql_config) 32 | 33 | def test_select_query(self): 34 | pass 35 | # result = execute_select_query("SELECT * FROM users") 36 | 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /test/test_sales_insert.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from pprint import pprint 3 | 4 | import numpy as np 5 | import pandas as pd 6 | import pymysql 7 | import pymysql.cursors 8 | from pymysql.err import OperationalError 9 | from tqdm import tqdm 10 | 11 | 12 | # MySQL 연결 설정 13 | def create_db_connection(): 14 | try: 15 | connection = pymysql.connect( 16 | host="localhost", # 호스트명 17 | database="mcp_test", # 데이터베이스 이름 18 | user="root", # MySQL 사용자 이름 19 | password="mcpTest1234!!!", # MySQL 비밀번호 20 | charset="utf8mb4", # 문자 인코딩 21 | cursorclass=pymysql.cursors.DictCursor, 22 | ) 23 | print("Successfully connected to MySQL database") 24 | return connection 25 | except OperationalError as e: 26 | print(f"Error while connecting to MySQL: {e}") 27 | return None 28 | 29 | 30 | # 테이블 생성 함수 31 | def create_table(connection): 32 | try: 33 | with connection.cursor() as cursor: 34 | create_table_query = """ 35 | CREATE TABLE IF NOT EXISTS sales_data ( 36 | ID INT AUTO_INCREMENT PRIMARY KEY COMMENT '고유 식별자로 자동 증가하는 기본 키', 37 | YEAR INT NOT NULL COMMENT '데이터가 기록된 연도 (예: 2020)', 38 | MONTH INT NOT NULL CHECK (MONTH BETWEEN 1 AND 12) COMMENT '데이터가 기록된 월 (1~12, 예: 1은 1월)', 39 | SUPPLIER VARCHAR(255) DEFAULT NULL COMMENT '주류를 공급한 업체 이름 (예: REPUBLIC NATIONAL DISTRIBUTING CO), NULL 허용', 40 | ITEM_CODE VARCHAR(50) NOT NULL COMMENT '품목을 식별하는 고유 코드 (예: 100009)', 41 | ITEM_DESCRIPTION VARCHAR(255) NOT NULL COMMENT '품목의 상세 설명 및 용량 (예: BOOTLEG RED - 750ML)', 42 | ITEM_TYPE VARCHAR(50) NOT NULL COMMENT '주류 유형 (예: WINE, BEER, LIQUOR, NON-ALCOHOL)', 43 | RETAIL_SALES FLOAT DEFAULT NULL COMMENT '소매점에서 소비자에게 판매된 금액 또는 수량 (예: 0.82, 단위 미확인), NULL 허용', 44 | RETAIL_TRANSFERS FLOAT DEFAULT NULL COMMENT '소매점 간 이동하거나 재고 조정 수량 (예: 1), NULL 허용', 45 | WAREHOUSE_SALES FLOAT DEFAULT NULL COMMENT '창고에서 판매된 수량 (예: 2), NULL 허용' 46 | ); 47 | """ 48 | cursor.execute(create_table_query) 49 | connection.commit() 50 | print("Table created successfully") 51 | except Exception as e: 52 | print(f"Error while creating table: {e}") 53 | 54 | 55 | def preprocess_record(record): 56 | """NaN 및 NumPy 타입을 SQL 친화적으로 변환""" 57 | return tuple( 58 | ( 59 | None 60 | if pd.isna(x) 61 | else x.item() if isinstance(x, (np.integer, np.floating)) else x 62 | ) 63 | for x in record 64 | ) 65 | 66 | 67 | # 데이터 삽입 함수 68 | def insert_data(connection, df, chunk_size=1000): 69 | try: 70 | # DataFrame을 레코드 리스트로 변환 71 | records = df.to_records(index=False) 72 | total_rows = len(records) 73 | 74 | # chunk 단위로 데이터 삽입 75 | with connection.cursor() as cursor: 76 | insert_query = """ 77 | INSERT INTO sales_data ( 78 | YEAR, MONTH, SUPPLIER, ITEM_CODE, ITEM_DESCRIPTION, 79 | ITEM_TYPE, RETAIL_SALES, RETAIL_TRANSFERS, WAREHOUSE_SALES 80 | ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) 81 | """ 82 | 83 | with tqdm(total=total_rows, desc="Inserting data") as pbar: 84 | for i in range(0, total_rows, chunk_size): 85 | chunk = [ 86 | preprocess_record(records[j]) 87 | for j in range(i, min(i + chunk_size, total_rows)) 88 | ] 89 | cursor.executemany(insert_query, chunk) 90 | pbar.update(len(chunk)) 91 | connection.commit() 92 | 93 | print(f"Successfully inserted {total_rows} rows") 94 | 95 | except Exception as e: 96 | pprint(chunk) 97 | print(f"Error while inserting data: {e}") 98 | 99 | 100 | # 메인 실행 101 | def main(): 102 | file = "Warehouse_and_Retail_Sales.csv" 103 | df = pd.read_csv(file) 104 | 105 | object_columns = df.select_dtypes(include=["object"]).columns 106 | df[object_columns] = df[object_columns].astype(str) 107 | 108 | # 데이터베이스 연결 109 | connection = create_db_connection() 110 | if connection: 111 | create_table(connection) 112 | insert_data(connection, df) 113 | connection.close() 114 | print("Database connection closed") 115 | 116 | 117 | if __name__ == "__main__": 118 | main() 119 | -------------------------------------------------------------------------------- /test/test_tool_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | import sys 4 | import unittest 5 | from typing import Literal 6 | 7 | # 현재 디렉토리를 Python 경로에 추가 8 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 9 | 10 | from mysql_mcp_server.helper.tool_decorator import get_schema, tool 11 | 12 | 13 | @tool() 14 | def test_tool(query: Literal["a", "b", "c"]) -> int: 15 | """ 16 | test_tool 도구는 주어진 쿼리를 반환합니다. 17 | 18 | Args: 19 | query: 쿼리 문자열 20 | - 가능한 값: "a", "b", "c" 21 | 22 | 23 | """ 24 | return query 25 | 26 | 27 | class TestToolList(unittest.TestCase): 28 | def test_get_schema(self): 29 | schema = get_schema(test_tool) 30 | assert schema["name"] == "test_tool" 31 | assert schema["description"] == "test_tool 도구는 주어진 쿼리를 반환합니다." 32 | assert schema["inputSchema"] == { 33 | "type": "object", 34 | "properties": { 35 | "query": { 36 | "type": "string", 37 | "enum": ["a", "b", "c"], 38 | "description": '쿼리 문자열\n- 가능한 값: "a", "b", "c"', 39 | } 40 | }, 41 | "required": ["query"], 42 | } 43 | 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | --------------------------------------------------------------------------------