@cano721/mysql-mcp-server 0.1.13 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,10 +22,17 @@
22
22
 
23
23
  ## 보안 기능
24
24
 
25
- - **읽기 전용 접근**: SELECT, SHOW, DESCRIBE, EXPLAIN 문만 허용
25
+ - **읽기 전용 접근**: SELECT, SHOW, DESCRIBE 문 항상 허용, EXPLAIN 선택적 허용
26
26
  - **쿼리 검증**: SQL 인젝션 방지 및 데이터 수정 시도 차단
27
27
  - **쿼리 타임아웃**: 장시간 실행되는 쿼리로부터 리소스 보호
28
28
  - **행 제한**: 과도한 데이터 반환 방지 (최대 1000행)
29
+ - **선택적 기능 제어**: EXPLAIN 쿼리 허용/차단 설정 가능
30
+
31
+ **지원되는 SQL 명령어**:
32
+ - `SELECT` - 데이터 조회 및 분석 (항상 허용)
33
+ - `SHOW` - 데이터베이스/테이블/인덱스 정보 조회 (항상 허용)
34
+ - `DESCRIBE` / `DESC` - 테이블 구조 및 컬럼 정보 (항상 허용)
35
+ - `EXPLAIN` - 쿼리 실행 계획 및 성능 분석 (선택적 허용, 기본값: 허용)
29
36
 
30
37
  ## 요구사항
31
38
 
@@ -85,11 +92,17 @@ npx -y @smithery/cli install @cano721/mysql-mcp-server --client claude
85
92
 
86
93
  서버는 다음 환경 변수가 필요합니다:
87
94
 
95
+ **필수 환경 변수:**
88
96
  - `MYSQL_HOST`: 데이터베이스 서버 호스트명
89
- - `MYSQL_PORT`: 데이터베이스 서버 포트 (기본값: 3306)
90
97
  - `MYSQL_USER`: 데이터베이스 사용자명
91
- - `MYSQL_PASSWORD`: 데이터베이스 비밀번호 (선택사항, 보안 연결에 권장)
92
- - `MYSQL_DATABASE`: 기본 데이터베이스명 (선택사항)
98
+
99
+ **선택적 환경 변수:**
100
+ - `MYSQL_PORT`: 데이터베이스 서버 포트 (기본값: 3306)
101
+ - `MYSQL_PASSWORD`: 데이터베이스 비밀번호 (보안 연결에 권장)
102
+ - `MYSQL_DATABASE`: 기본 데이터베이스명
103
+
104
+ **보안 설정:**
105
+ - `MYSQL_ALLOW_EXPLAIN`: EXPLAIN 쿼리 허용 여부 (기본값: true, 비활성화: false)
93
106
 
94
107
  ### 3. MCP 설정에 추가
95
108
 
@@ -221,6 +234,12 @@ MySQL 서버에서 접근 가능한 모든 데이터베이스를 나열합니다
221
234
  - `query` (필수): SQL 쿼리 (SELECT, SHOW, DESCRIBE, EXPLAIN 문만 허용)
222
235
  - `database` (선택사항): 데이터베이스명 (지정하지 않으면 기본값 사용)
223
236
 
237
+ **지원되는 쿼리 유형**:
238
+ - `SELECT` - 데이터 조회
239
+ - `SHOW` - 데이터베이스/테이블 정보 표시
240
+ - `DESCRIBE` / `DESC` - 테이블 구조 설명
241
+ - `EXPLAIN` - 쿼리 실행 계획 분석
242
+
224
243
  **예제**:
225
244
  ```json
226
245
  {
@@ -233,6 +252,30 @@ MySQL 서버에서 접근 가능한 모든 데이터베이스를 나열합니다
233
252
  }
234
253
  ```
235
254
 
255
+ **EXPLAIN 사용 예제**:
256
+ ```json
257
+ {
258
+ "server_name": "mysql",
259
+ "tool_name": "execute_query",
260
+ "arguments": {
261
+ "database": "my_database",
262
+ "query": "EXPLAIN SELECT * FROM my_table WHERE id = 1"
263
+ }
264
+ }
265
+ ```
266
+
267
+ **SHOW 명령어 예제**:
268
+ ```json
269
+ {
270
+ "server_name": "mysql",
271
+ "tool_name": "execute_query",
272
+ "arguments": {
273
+ "database": "my_database",
274
+ "query": "SHOW CREATE TABLE my_table"
275
+ }
276
+ }
277
+ ```
278
+
236
279
  ## 고급 연결 풀 설정
237
280
 
238
281
  MySQL 연결 풀 동작을 더 세밀하게 제어하려면 추가 매개변수를 설정할 수 있습니다:
@@ -254,7 +297,8 @@ MySQL 연결 풀 동작을 더 세밀하게 제어하려면 추가 매개변수
254
297
  "MYSQL_QUEUE_LIMIT": "0",
255
298
  "MYSQL_CONNECT_TIMEOUT": "10000",
256
299
  "MYSQL_IDLE_TIMEOUT": "60000",
257
- "MYSQL_MAX_IDLE": "10"
300
+ "MYSQL_MAX_IDLE": "10",
301
+ "MYSQL_ALLOW_EXPLAIN": "true"
258
302
  },
259
303
  "disabled": false,
260
304
  "autoApprove": []
@@ -270,6 +314,7 @@ MySQL 연결 풀 동작을 더 세밀하게 제어하려면 추가 매개변수
270
314
  - `MYSQL_CONNECT_TIMEOUT`: 연결 타임아웃을 밀리초 단위로 조정 (기본값: 10000)
271
315
  - `MYSQL_IDLE_TIMEOUT`: 연결이 해제되기 전까지 유휴 상태로 있을 수 있는 시간 (밀리초 단위)
272
316
  - `MYSQL_MAX_IDLE`: 풀에 유지할 최대 유휴 연결 수 설정
317
+ - `MYSQL_ALLOW_EXPLAIN`: EXPLAIN 쿼리 허용 여부 (기본값: true)
273
318
 
274
319
  ## 테스트
275
320
 
@@ -417,6 +462,8 @@ Cursor IDE에서 MCP 서버를 사용하려면 `.cursor/mcp.json` 파일을 생
417
462
  - ✅ **테이블 목록 조회**: MySQL 시스템 데이터베이스의 40개 테이블 조회
418
463
  - ✅ **테이블 스키마 조회**: user 테이블의 46개 컬럼 정보 조회
419
464
  - ✅ **SQL 쿼리 실행**: 사용자 정보 조회 쿼리 성공적으로 실행
465
+ - ✅ **EXPLAIN 쿼리**: 쿼리 실행 계획 분석 지원
466
+ - ✅ **SHOW 명령어**: 데이터베이스 메타데이터 조회 지원
420
467
 
421
468
  ## 문제 해결
422
469
 
@@ -102,6 +102,8 @@ export function getConfigFromEnv() {
102
102
  const connectTimeoutStr = process.env.MYSQL_CONNECT_TIMEOUT;
103
103
  const idleTimeoutStr = process.env.MYSQL_IDLE_TIMEOUT;
104
104
  const maxIdleStr = process.env.MYSQL_MAX_IDLE;
105
+ // Security options
106
+ const allowExplain = process.env.MYSQL_ALLOW_EXPLAIN !== 'false'; // Default: true
105
107
  if (!host)
106
108
  throw new Error('MYSQL_HOST environment variable is required');
107
109
  if (!user)
@@ -113,6 +115,9 @@ export function getConfigFromEnv() {
113
115
  const connectTimeout = connectTimeoutStr ? parseInt(connectTimeoutStr, 10) : undefined;
114
116
  const idleTimeout = idleTimeoutStr ? parseInt(idleTimeoutStr, 10) : undefined;
115
117
  const maxIdle = maxIdleStr ? parseInt(maxIdleStr, 10) : undefined;
118
+ console.error('[Setup] Security settings:', {
119
+ allowExplain
120
+ });
116
121
  return {
117
122
  host,
118
123
  port,
package/build/index.js CHANGED
@@ -46,6 +46,14 @@ const server = new Server({
46
46
  * Handler that lists available tools for MySQL database access
47
47
  */
48
48
  server.setRequestHandler(ListToolsRequestSchema, async () => {
49
+ // Check if EXPLAIN is enabled
50
+ const allowExplain = process.env.MYSQL_ALLOW_EXPLAIN !== 'false';
51
+ // Build allowed commands description
52
+ const allowedCommands = ['SELECT', 'SHOW', 'DESCRIBE'];
53
+ if (allowExplain) {
54
+ allowedCommands.push('EXPLAIN');
55
+ }
56
+ const commandsDescription = `SQL query (only ${allowedCommands.join(', ')} statements are allowed)`;
49
57
  return {
50
58
  tools: [
51
59
  {
@@ -97,7 +105,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
97
105
  properties: {
98
106
  query: {
99
107
  type: "string",
100
- description: "SQL query (only SELECT, SHOW, DESCRIBE, and EXPLAIN statements are allowed)"
108
+ description: commandsDescription
101
109
  },
102
110
  database: {
103
111
  type: "string",
@@ -8,6 +8,9 @@ const ALLOWED_COMMANDS = [
8
8
  'SHOW',
9
9
  'DESCRIBE',
10
10
  'DESC',
11
+ ];
12
+ // Optional commands that can be enabled/disabled
13
+ const OPTIONAL_COMMANDS = [
11
14
  'EXPLAIN',
12
15
  ];
13
16
  // List of disallowed SQL commands (write operations)
@@ -34,6 +37,18 @@ const DISALLOWED_COMMANDS = [
34
37
  'COMMIT',
35
38
  'ROLLBACK',
36
39
  ];
40
+ /**
41
+ * Get allowed commands based on environment configuration
42
+ */
43
+ function getAllowedCommands() {
44
+ const allowedCommands = [...ALLOWED_COMMANDS];
45
+ // Check if EXPLAIN is enabled (default: true)
46
+ const allowExplain = process.env.MYSQL_ALLOW_EXPLAIN !== 'false';
47
+ if (allowExplain) {
48
+ allowedCommands.push(...OPTIONAL_COMMANDS);
49
+ }
50
+ return allowedCommands;
51
+ }
37
52
  /**
38
53
  * Validates if a SQL query is read-only
39
54
  * @param query SQL query to validate
@@ -47,8 +62,10 @@ export function isReadOnlyQuery(query) {
47
62
  .replace(/\s+/g, ' ') // Normalize whitespace
48
63
  .trim()
49
64
  .toUpperCase();
65
+ // Get currently allowed commands
66
+ const allowedCommands = getAllowedCommands();
50
67
  // Check if query starts with an allowed command
51
- const startsWithAllowed = ALLOWED_COMMANDS.some(cmd => normalizedQuery.startsWith(cmd + ' ') || normalizedQuery === cmd);
68
+ const startsWithAllowed = allowedCommands.some(cmd => normalizedQuery.startsWith(cmd + ' ') || normalizedQuery === cmd);
52
69
  // Check if query contains any disallowed commands
53
70
  const containsDisallowed = DISALLOWED_COMMANDS.some(cmd => {
54
71
  const regex = new RegExp(`(^|\\s)${cmd}(\\s|$)`);
@@ -73,7 +90,8 @@ export function validateQuery(query) {
73
90
  }
74
91
  if (!isReadOnlyQuery(query)) {
75
92
  console.error('[Validator] Query rejected: not read-only');
76
- throw new Error('Only read-only queries are allowed (SELECT, SHOW, DESCRIBE, EXPLAIN)');
93
+ const allowedCommands = getAllowedCommands();
94
+ throw new Error(`Only read-only queries are allowed (${allowedCommands.join(', ')})`);
77
95
  }
78
96
  console.error('[Validator] Query validated as read-only');
79
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cano721/mysql-mcp-server",
3
- "version": "0.1.13",
3
+ "version": "0.2.0",
4
4
  "description": "An MCP server that provides read-only access to MySQL databases.",
5
5
  "type": "module",
6
6
  "bin": {