@cano721/mysql-mcp-server 0.1.14 → 0.3.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,16 +22,19 @@
22
22
 
23
23
  ## 보안 기능
24
24
 
25
- - **읽기 전용 접근**: SELECT, SHOW, DESCRIBE, EXPLAIN 문만 허용
25
+ - **읽기 전용 접근**: SELECT, SHOW, DESCRIBE 항상 허용
26
+ - **선택적 분석 도구**: EXPLAIN, ANALYZE 선택적 허용
26
27
  - **쿼리 검증**: SQL 인젝션 방지 및 데이터 수정 시도 차단
27
28
  - **쿼리 타임아웃**: 장시간 실행되는 쿼리로부터 리소스 보호
28
29
  - **행 제한**: 과도한 데이터 반환 방지 (최대 1000행)
30
+ - **세밀한 권한 제어**: 각 분석 도구별 개별 활성화/비활성화
29
31
 
30
32
  **지원되는 SQL 명령어**:
31
- - `SELECT` - 데이터 조회 및 분석
32
- - `SHOW` - 데이터베이스/테이블/인덱스 정보 조회
33
- - `DESCRIBE` / `DESC` - 테이블 구조 및 컬럼 정보
34
- - `EXPLAIN` - 쿼리 실행 계획 및 성능 분석
33
+ - `SELECT` - 데이터 조회 및 분석 (항상 허용)
34
+ - `SHOW` - 데이터베이스/테이블/인덱스 정보 조회 (항상 허용)
35
+ - `DESCRIBE` / `DESC` - 테이블 구조 및 컬럼 정보 (항상 허용)
36
+ - `EXPLAIN` - 쿼리 실행 계획 및 성능 분석 (선택적 허용, 기본값: 허용)
37
+ - `ANALYZE` - 테이블 통계 분석 및 업데이트 (선택적 허용, 기본값: 비허용)
35
38
 
36
39
  ## 요구사항
37
40
 
@@ -91,11 +94,18 @@ npx -y @smithery/cli install @cano721/mysql-mcp-server --client claude
91
94
 
92
95
  서버는 다음 환경 변수가 필요합니다:
93
96
 
97
+ **필수 환경 변수:**
94
98
  - `MYSQL_HOST`: 데이터베이스 서버 호스트명
95
- - `MYSQL_PORT`: 데이터베이스 서버 포트 (기본값: 3306)
96
99
  - `MYSQL_USER`: 데이터베이스 사용자명
97
- - `MYSQL_PASSWORD`: 데이터베이스 비밀번호 (선택사항, 보안 연결에 권장)
98
- - `MYSQL_DATABASE`: 기본 데이터베이스명 (선택사항)
100
+
101
+ **선택적 환경 변수:**
102
+ - `MYSQL_PORT`: 데이터베이스 서버 포트 (기본값: 3306)
103
+ - `MYSQL_PASSWORD`: 데이터베이스 비밀번호 (보안 연결에 권장)
104
+ - `MYSQL_DATABASE`: 기본 데이터베이스명
105
+
106
+ **보안 설정:**
107
+ - `MYSQL_ALLOW_EXPLAIN`: EXPLAIN 쿼리 허용 여부 (기본값: true, 비활성화: false)
108
+ - `MYSQL_ALLOW_ANALYZE`: ANALYZE 쿼리 허용 여부 (기본값: false, 활성화: true)
99
109
 
100
110
  ### 3. MCP 설정에 추가
101
111
 
@@ -257,6 +267,18 @@ MySQL 서버에서 접근 가능한 모든 데이터베이스를 나열합니다
257
267
  }
258
268
  ```
259
269
 
270
+ **ANALYZE 사용 예제** (MYSQL_ALLOW_ANALYZE=true 필요):
271
+ ```json
272
+ {
273
+ "server_name": "mysql",
274
+ "tool_name": "execute_query",
275
+ "arguments": {
276
+ "database": "my_database",
277
+ "query": "ANALYZE TABLE my_table"
278
+ }
279
+ }
280
+ ```
281
+
260
282
  **SHOW 명령어 예제**:
261
283
  ```json
262
284
  {
@@ -290,7 +312,9 @@ MySQL 연결 풀 동작을 더 세밀하게 제어하려면 추가 매개변수
290
312
  "MYSQL_QUEUE_LIMIT": "0",
291
313
  "MYSQL_CONNECT_TIMEOUT": "10000",
292
314
  "MYSQL_IDLE_TIMEOUT": "60000",
293
- "MYSQL_MAX_IDLE": "10"
315
+ "MYSQL_MAX_IDLE": "10",
316
+ "MYSQL_ALLOW_EXPLAIN": "true",
317
+ "MYSQL_ALLOW_ANALYZE": "false"
294
318
  },
295
319
  "disabled": false,
296
320
  "autoApprove": []
@@ -306,6 +330,8 @@ MySQL 연결 풀 동작을 더 세밀하게 제어하려면 추가 매개변수
306
330
  - `MYSQL_CONNECT_TIMEOUT`: 연결 타임아웃을 밀리초 단위로 조정 (기본값: 10000)
307
331
  - `MYSQL_IDLE_TIMEOUT`: 연결이 해제되기 전까지 유휴 상태로 있을 수 있는 시간 (밀리초 단위)
308
332
  - `MYSQL_MAX_IDLE`: 풀에 유지할 최대 유휴 연결 수 설정
333
+ - `MYSQL_ALLOW_EXPLAIN`: EXPLAIN 쿼리 허용 여부 (기본값: true)
334
+ - `MYSQL_ALLOW_ANALYZE`: ANALYZE 쿼리 허용 여부 (기본값: false)
309
335
 
310
336
  ## 테스트
311
337
 
@@ -102,6 +102,9 @@ 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
107
+ const allowAnalyze = process.env.MYSQL_ALLOW_ANALYZE === 'true'; // Default: false
105
108
  if (!host)
106
109
  throw new Error('MYSQL_HOST environment variable is required');
107
110
  if (!user)
@@ -113,6 +116,10 @@ export function getConfigFromEnv() {
113
116
  const connectTimeout = connectTimeoutStr ? parseInt(connectTimeoutStr, 10) : undefined;
114
117
  const idleTimeout = idleTimeoutStr ? parseInt(idleTimeoutStr, 10) : undefined;
115
118
  const maxIdle = maxIdleStr ? parseInt(maxIdleStr, 10) : undefined;
119
+ console.error('[Setup] Security settings:', {
120
+ allowExplain,
121
+ allowAnalyze
122
+ });
116
123
  return {
117
124
  host,
118
125
  port,
package/build/index.js CHANGED
@@ -46,6 +46,18 @@ 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 optional commands are enabled
50
+ const allowExplain = process.env.MYSQL_ALLOW_EXPLAIN !== 'false';
51
+ const allowAnalyze = process.env.MYSQL_ALLOW_ANALYZE === 'true';
52
+ // Build allowed commands description
53
+ const allowedCommands = ['SELECT', 'SHOW', 'DESCRIBE'];
54
+ if (allowExplain) {
55
+ allowedCommands.push('EXPLAIN');
56
+ }
57
+ if (allowAnalyze) {
58
+ allowedCommands.push('ANALYZE');
59
+ }
60
+ const commandsDescription = `SQL query (only ${allowedCommands.join(', ')} statements are allowed)`;
49
61
  return {
50
62
  tools: [
51
63
  {
@@ -97,7 +109,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
97
109
  properties: {
98
110
  query: {
99
111
  type: "string",
100
- description: "SQL query (only SELECT, SHOW, DESCRIBE, and EXPLAIN statements are allowed)"
112
+ description: commandsDescription
101
113
  },
102
114
  database: {
103
115
  type: "string",
@@ -8,7 +8,11 @@ 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',
15
+ 'ANALYZE',
12
16
  ];
13
17
  // List of disallowed SQL commands (write operations)
14
18
  const DISALLOWED_COMMANDS = [
@@ -34,6 +38,23 @@ const DISALLOWED_COMMANDS = [
34
38
  'COMMIT',
35
39
  'ROLLBACK',
36
40
  ];
41
+ /**
42
+ * Get allowed commands based on environment configuration
43
+ */
44
+ function getAllowedCommands() {
45
+ const allowedCommands = [...ALLOWED_COMMANDS];
46
+ // Check if EXPLAIN is enabled (default: true)
47
+ const allowExplain = process.env.MYSQL_ALLOW_EXPLAIN !== 'false';
48
+ if (allowExplain) {
49
+ allowedCommands.push('EXPLAIN');
50
+ }
51
+ // Check if ANALYZE is enabled (default: false)
52
+ const allowAnalyze = process.env.MYSQL_ALLOW_ANALYZE === 'true';
53
+ if (allowAnalyze) {
54
+ allowedCommands.push('ANALYZE');
55
+ }
56
+ return allowedCommands;
57
+ }
37
58
  /**
38
59
  * Validates if a SQL query is read-only
39
60
  * @param query SQL query to validate
@@ -47,8 +68,10 @@ export function isReadOnlyQuery(query) {
47
68
  .replace(/\s+/g, ' ') // Normalize whitespace
48
69
  .trim()
49
70
  .toUpperCase();
71
+ // Get currently allowed commands
72
+ const allowedCommands = getAllowedCommands();
50
73
  // Check if query starts with an allowed command
51
- const startsWithAllowed = ALLOWED_COMMANDS.some(cmd => normalizedQuery.startsWith(cmd + ' ') || normalizedQuery === cmd);
74
+ const startsWithAllowed = allowedCommands.some(cmd => normalizedQuery.startsWith(cmd + ' ') || normalizedQuery === cmd);
52
75
  // Check if query contains any disallowed commands
53
76
  const containsDisallowed = DISALLOWED_COMMANDS.some(cmd => {
54
77
  const regex = new RegExp(`(^|\\s)${cmd}(\\s|$)`);
@@ -73,7 +96,8 @@ export function validateQuery(query) {
73
96
  }
74
97
  if (!isReadOnlyQuery(query)) {
75
98
  console.error('[Validator] Query rejected: not read-only');
76
- throw new Error('Only read-only queries are allowed (SELECT, SHOW, DESCRIBE, EXPLAIN)');
99
+ const allowedCommands = getAllowedCommands();
100
+ throw new Error(`Only read-only queries are allowed (${allowedCommands.join(', ')})`);
77
101
  }
78
102
  console.error('[Validator] Query validated as read-only');
79
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cano721/mysql-mcp-server",
3
- "version": "0.1.14",
3
+ "version": "0.3.0",
4
4
  "description": "An MCP server that provides read-only access to MySQL databases.",
5
5
  "type": "module",
6
6
  "bin": {