@yanzhang123/readonly-db-mcp 0.8.0 → 0.8.2

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.
@@ -59,7 +59,7 @@ allowed_databases = ["application_database"]
59
59
  # Unrecognized stored functions/UDFs are blocked. This list should normally
60
60
  # remain empty. Add only reviewed deterministic functions with no side effects.
61
61
  allowed_functions = []
62
- mask_columns = ["password", "passwd", "*secret*", "*token*", "*api_key*", "*private_key*", "ssn", "id_card", "phone", "email"]
62
+ mask_columns = ["password", "*password*", "*passwd*", "*pwd*", "*secret*", "token", "*token", "*_token", "token_*", "*tokenvalue*", "*accesstoken*", "*refreshtoken*", "*idtoken*", "*appsec*", "*api_key*", "*private_key*", "*ssn*", "*id_card*", "*phone*", "*mobile*", "*email*"]
63
63
 
64
64
  ssl_mode = "VERIFY_IDENTITY"
65
65
  ssl_ca = "/etc/mysql-mcp/company-ca.pem"
@@ -171,7 +171,31 @@ files.
171
171
  For strong human identity, an authenticated gateway must bind its verified
172
172
  principal to the MCP request and protect access to the MCP endpoint.
173
173
 
174
- ## SSE Deployment
174
+ ## Streamable HTTP Deployment
175
+
176
+ New remote MCP clients, including DeepSeek Harness, should use the standard
177
+ Streamable HTTP endpoint:
178
+
179
+ ```dotenv
180
+ MCP_TRANSPORT=streamable-http
181
+ MCP_HTTP_HOST=127.0.0.1
182
+ MCP_HTTP_PORT=8000
183
+ MCP_HTTP_PATH=/mcp
184
+ MCP_HTTP_SESSION_IDLE_TIMEOUT_SECONDS=1800
185
+ ```
186
+
187
+ For built-in bearer authentication:
188
+
189
+ ```dotenv
190
+ MCP_HTTP_BEARER_TOKEN=<secret-manager-value-of-at-least-32-characters>
191
+ ```
192
+
193
+ The token protects `/mcp`; `/` remains a non-sensitive health endpoint. Keep
194
+ the service on loopback behind an authenticated reverse proxy. A container
195
+ that must bind `0.0.0.0` may set `MCP_HTTP_TRUST_PROXY_AUTH=true` only when
196
+ network policy prevents direct access to the container port.
197
+
198
+ ## Legacy SSE Deployment
175
199
 
176
200
  The safe default is:
177
201
 
package/README.md CHANGED
@@ -4,117 +4,92 @@
4
4
 
5
5
  # MySQL MCP Server
6
6
 
7
- An enterprise-auditable, strictly read-only Model Context Protocol (MCP) server
8
- for exploring multiple MySQL environments and databases from one process.
7
+ 一个可审计、严格只读的 MySQL Model Context Protocol(MCP)服务。单个进程即可安全访问多个 MySQL 环境和数据库。
9
8
 
10
- The supported npm distribution for this fork is
11
- [`@yanzhang123/readonly-db-mcp`](https://www.npmjs.com/package/@yanzhang123/readonly-db-mcp).
12
- It includes the matching Python wheel and a cross-platform launcher, so MCP
13
- clients do not need a repository checkout or a separate package installation.
9
+ 本分支推荐通过 npm 包 [`@yanzhang123/readonly-db-mcp`](https://www.npmjs.com/package/@yanzhang123/readonly-db-mcp) 使用。npm 包内置匹配版本的 Python wheel 和跨平台启动器,MCP 客户端无需检出仓库或单独安装 Python 包。
14
10
 
15
- > **Note**: MySQL MCP Server supports both standard input/output (STDIO) and Streamable HTTP (SSE) transport modes. The SSE mode is recommended for remote/self-hosted deployments.
11
+ > [!NOTE]
12
+ > 服务支持标准输入输出(STDIO)、标准 Streamable HTTP 和旧版 SSE 传输。远程或自托管场景推荐使用 Streamable HTTP,并应按本文配置认证和网络边界。
16
13
 
17
14
  > [!IMPORTANT]
18
- > Read-only enforcement does not trust the MySQL account grants. Even if the
19
- > supplied account has `INSERT`, `UPDATE`, `DELETE`, or DDL privileges, the MCP
20
- > SQL gate only accepts reviewed read statement families, runs them in a
21
- > read-only transaction, and always rolls back. A database-level `SELECT`-only
22
- > account is still strongly recommended as independent defense in depth.
15
+ > 只读约束不依赖 MySQL 账号权限。即使账号拥有 `INSERT`、`UPDATE`、`DELETE` 或 DDL 权限,SQL 闸门也只接受经过审查的只读语句,并在只读事务中执行后统一回滚。生产环境仍强烈建议使用仅授予 `SELECT` 的数据库账号,形成独立的纵深防御。
23
16
 
24
- ## Features
25
- - **Named connection profiles** for dev/test/staging/prod and multiple servers
26
- - Select a `connection` and `database` independently on every tool call
27
- - Database-to-profile routing hints without implicit cross-environment switching
28
- - **Strictly read-only SQL** with fail-closed validation, a MySQL read-only transaction, and unconditional rollback
29
- - Discover connections, databases, tables, schemas, and sample rows
30
- - Return-size protection (`MYSQL_MAX_ROWS`, hard maximum 1000)
31
- - Legacy single-connection `MYSQL_*` configuration remains supported
32
- - **SSE/HTTP transport support** (`MCP_TRANSPORT=sse`)
33
- - Per-profile SSL/TLS and SSH tunneling
34
- - Passwords can stay in environment variables instead of the profiles file
35
- - Compatibility `query` alias for clients migrating from older MySQL MCP servers
17
+ ## 功能特性
36
18
 
37
- ## Installation
19
+ - 使用命名连接配置管理开发、测试、预发、生产等多个环境
20
+ - 每次工具调用都显式选择 `connection` 和 `database`,避免并发客户端相互污染状态
21
+ - 支持逻辑数据库到物理连接的精确路由,不会隐式跨环境切换
22
+ - 严格只读 SQL:失败即拒绝的语法校验、MySQL 只读事务、无条件回滚
23
+ - 可发现连接、数据库、表、字段、索引、约束和样例数据
24
+ - 查询分页下推到 MySQL,避免客户端截断结果引发 Connector/Python `errno=-1`
25
+ - 内置结果行数和单元格长度限制,最大返回 1000 行
26
+ - 按源字段精确脱敏,支持别名、CTE 以及 JSON 内敏感键
27
+ - 支持 SSL/TLS、SSH 隧道、Streamable HTTP 和旧版 SSE 传输
28
+ - 密码可保存在环境变量、操作系统凭据库或受控的密钥命令中
29
+ - 保留旧版单连接 `MYSQL_*` 环境变量和 `query` 工具别名
38
30
 
39
- ### npm / npx
31
+ ## 安装
40
32
 
41
- Requirements: Node.js 18+, npm 9+, and Python 3.11+ available on `PATH`.
33
+ ### npm / npx(推荐)
42
34
 
43
- Run the current release without installing it globally:
35
+ 环境要求:Node.js 18+、npm 9+,并确保 Python 3.11+ 可从 `PATH` 找到。
36
+
37
+ 直接运行最新版本:
44
38
 
45
39
  ```bash
46
40
  npx -y @yanzhang123/readonly-db-mcp
47
41
  ```
48
42
 
49
- For controlled production rollouts, pin the reviewed version:
43
+ 生产环境建议固定经过审核的版本:
50
44
 
51
45
  ```bash
52
- npx -y @yanzhang123/readonly-db-mcp@x.y.z
46
+ npx -y @yanzhang123/readonly-db-mcp@0.8.2
53
47
  ```
54
48
 
55
- Replace `x.y.z` with the version approved by your organization. The unversioned
56
- examples below intentionally resolve the npm `latest` tag.
57
-
58
- On first use, the launcher creates a versioned virtual environment in the user
59
- cache and installs the bundled wheel plus exact Python dependencies from a
60
- version-controlled SHA-256 lock file. Downloads use the configured pip index
61
- and the completed environment is cached by wheel-and-lock fingerprint. Override
62
- Python with `MYSQL_MCP_PYTHON` or the cache location with
63
- `MYSQL_MCP_NPM_CACHE_DIR`.
49
+ 首次运行时,启动器会在用户缓存目录创建版本化虚拟环境,并安装包内 wheel 与带 SHA-256 锁定的 Python 依赖。完成后的环境按 wheel 和依赖锁指纹复用。可通过 `MYSQL_MCP_PYTHON` 指定 Python,通过 `MYSQL_MCP_NPM_CACHE_DIR` 修改缓存位置。
64
50
 
65
- For MCP clients, use `npx` as the command and
66
- `["-y", "@yanzhang123/readonly-db-mcp"]` as its arguments.
51
+ ### 最小 MCP 配置
67
52
 
68
- ### Minimal MCP client configuration
69
-
70
- Create `mysql-connections.toml` from
71
- [`mysql-connections.example.toml`](mysql-connections.example.toml), keep
72
- passwords in environment variables, and use an absolute path:
53
+ 复制 [`mysql-connections.example.toml`](mysql-connections.example.toml) 为本地配置文件,把密码放入环境变量,并在 MCP 配置中使用绝对路径:
73
54
 
74
55
  ```json
75
56
  {
76
57
  "mcpServers": {
77
58
  "mysql-readonly": {
78
59
  "command": "npx",
79
- "args": ["-y", "@yanzhang123/readonly-db-mcp"],
60
+ "args": ["-y", "@yanzhang123/readonly-db-mcp@0.8.2"],
80
61
  "env": {
81
62
  "MYSQL_PROFILES_FILE": "C:/absolute/path/mysql-connections.toml",
82
- "MYSQL_DEV_PASSWORD": "set-in-the-client-secret-store"
63
+ "MYSQL_DEV_PASSWORD": "由客户端密钥存储提供"
83
64
  }
84
65
  }
85
66
  }
86
67
  }
87
68
  ```
88
69
 
89
- On Windows clients that do not resolve npm command shims correctly, set
90
- `"command": "npx.cmd"`. Do not commit the populated profile, passwords, audit
91
- HMAC keys, connection strings, or client configuration containing secrets.
70
+ 部分 Windows MCP 客户端无法正确解析 npm 命令垫片,此时将 `command` 改为 `npx.cmd`。不要提交真实连接配置、密码、审计 HMAC 密钥、连接串或含有敏感信息的 MCP 客户端配置。
92
71
 
93
- After startup, call `validate_connections`, then `check_connection`, before
94
- running discovery queries. `check_connection` reports an irreversible account
95
- fingerprint and privilege classification without returning the username or raw
96
- grant statements.
72
+ 服务启动后,建议依次调用 `validate_connections` 和 `check_connection`,确认配置有效、网络可达、账号权限符合预期。`check_connection` 只返回不可逆账号指纹和权限分类,不会暴露用户名或原始授权语句。
97
73
 
98
- ### Manual Python installation
74
+ ### 手动安装 Python 包
99
75
 
100
76
  ```bash
101
77
  pip install mysql-mcp-server
102
78
  ```
103
79
 
104
- The Python package remains available for source-oriented deployments, but the
105
- scoped npm package is the documented distribution path for this fork.
80
+ Python 包仍适用于源码部署,但本分支的主要分发方式是上述 npm 包。
106
81
 
107
- ## Configuration
82
+ ## 配置
108
83
 
109
- ### Recommended: named connections
84
+ ### 推荐:命名连接
110
85
 
111
- Copy [`mysql-connections.example.toml`](mysql-connections.example.toml) to an ignored local file named `mysql-connections.toml`, then define all environments in one place:
86
+ 将 [`mysql-connections.example.toml`](mysql-connections.example.toml) 复制为已被 Git 忽略的 `mysql-connections.toml`,然后集中声明各环境:
112
87
 
113
88
  ```toml
114
89
  default = "dev"
115
90
 
116
91
  [connections.dev]
117
- description = "Local development"
92
+ description = "本地开发环境"
118
93
  host = "127.0.0.1"
119
94
  port = 3306
120
95
  user = "readonly_user"
@@ -125,27 +100,26 @@ query_timeout_ms = 30000
125
100
  max_rows = 500
126
101
  max_cell_length = 20000
127
102
  result_format = "json"
128
- mask_columns = ["password", "passwd", "*secret*", "*token*", "*api_key*", "*private_key*", "ssn", "id_card", "phone", "email"]
103
+ mask_columns = ["password", "*password*", "*passwd*", "*pwd*", "*secret*", "token", "*token", "*_token", "token_*", "*tokenvalue*", "*accesstoken*", "*refreshtoken*", "*idtoken*", "*appsec*", "*api_key*", "*private_key*", "*ssn*", "*id_card*", "*phone*", "*mobile*", "*email*"]
129
104
  pool_size = 3
130
105
  audit_enabled = true
131
106
 
132
107
  [connections.prod]
133
- description = "Production read replica"
108
+ description = "生产只读副本"
134
109
  host = "prod-read.example.internal"
135
110
  user = "readonly_user"
136
111
  password_env = "MYSQL_PROD_PASSWORD"
137
112
  database = "app"
138
113
  allowed_databases = ["app", "reporting"]
139
- allowed_functions = [] # reviewed deterministic UDF/stored-function names only
114
+ allowed_functions = [] # 仅填写已经审核、确定无副作用的 UDF/存储函数
140
115
  query_timeout_ms = 15000
141
116
  max_rows = 200
142
117
  result_format = "json"
143
- mask_columns = ["password", "passwd", "*secret*", "*token*", "*api_key*", "*private_key*", "ssn", "id_card", "phone", "email"]
144
118
  pool_size = 5
145
119
  ssl_mode = "VERIFY_CA"
146
120
  ssl_ca = "C:/certs/company-ca.pem"
147
121
 
148
- # Enterprise audit controls
122
+ # 企业审计配置
149
123
  audit_enabled = true
150
124
  audit_log_file = "C:/var/log/mysql-mcp/audit.jsonl"
151
125
  audit_log_max_bytes = 10000000
@@ -156,28 +130,28 @@ audit_fail_closed = true
156
130
  audit_fsync = true
157
131
  ```
158
132
 
159
- Point the MCP process at that file and supply only the secrets through environment variables:
133
+ 通过环境变量提供配置路径和密钥:
160
134
 
161
135
  ```bash
162
136
  MYSQL_PROFILES_FILE=C:/absolute/path/mysql-connections.toml
163
137
  MYSQL_DEV_PASSWORD=...
164
138
  MYSQL_PROD_PASSWORD=...
165
- MYSQL_MCP_AUDIT_SIGNING_KEY=... # secret-manager supplied; never commit
166
- MYSQL_DEFAULT_CONNECTION=dev # optional; overrides `default` in TOML
167
- MYSQL_MAX_ROWS=500 # optional; 1-1000, default 500
168
- MYSQL_QUERY_TIMEOUT_MS=30000 # full-call timeout, 100-300000 ms
169
- MYSQL_MAX_CELL_LENGTH=20000 # truncate oversized values
170
- MYSQL_RESULT_FORMAT=json # csv or json
171
- MYSQL_MASK_COLUMNS=password,passwd,*secret*,*token*,*api_key*,*private_key*,ssn,id_card,phone,email
172
- MYSQL_POOL_SIZE=0 # legacy mode; named profiles default to 5
139
+ MYSQL_MCP_AUDIT_SIGNING_KEY=... # 由密钥管理系统提供,禁止提交
140
+ MYSQL_DEFAULT_CONNECTION=dev # 可选,覆盖 TOML 中的 default
141
+ MYSQL_MAX_ROWS=500 # 可选,1-1000,默认 500
142
+ MYSQL_QUERY_TIMEOUT_MS=30000 # 整次调用超时,100-300000 毫秒
143
+ MYSQL_MAX_CELL_LENGTH=20000 # 超长单元格截断长度
144
+ MYSQL_RESULT_FORMAT=json # json 或 csv
145
+ MYSQL_POOL_SIZE=0 # 旧单连接模式;命名配置默认 5
173
146
  MYSQL_ALLOWED_DATABASES=app,reporting
174
- MYSQL_ALLOWED_FUNCTIONS= # optional reviewed function names
147
+ MYSQL_ALLOWED_FUNCTIONS= # 可选,经过审核的函数名
175
148
  ```
176
149
 
177
- `password_env` is recommended. A `password` field is accepted for local-only setups, but `mysql-connections.toml` is gitignored because it may contain secrets. Use an absolute `MYSQL_PROFILES_FILE` path in desktop MCP clients because their working directory is not guaranteed.
150
+ 推荐使用 `password_env`。本地环境也支持 `password` 字段,但 `mysql-connections.toml` 已加入 `.gitignore`,因为它可能包含敏感信息。桌面 MCP 客户端的工作目录不固定,因此 `MYSQL_PROFILES_FILE` 必须使用绝对路径。
151
+
152
+ ### 使用操作系统凭据库
178
153
 
179
- For user-level `mcp.json` files, prefer the OS credential store so the password
180
- does not appear in either MCP configuration or TOML:
154
+ 用户级 MCP 配置可使用系统凭据库,让密码不出现在 MCP JSON 或 TOML 中:
181
155
 
182
156
  ```toml
183
157
  [connections.dev]
@@ -189,7 +163,7 @@ database = "app_dev"
189
163
  allowed_databases = ["app_dev"]
190
164
  ```
191
165
 
192
- Store or manage it locally with a masked prompt:
166
+ 使用遮罩输入在本机管理凭据:
193
167
 
194
168
  ```powershell
195
169
  readonly-db-mcp credentials --profiles-file C:/absolute/path/mysql-connections.toml set dev
@@ -197,15 +171,11 @@ readonly-db-mcp credentials --profiles-file C:/absolute/path/mysql-connections.t
197
171
  readonly-db-mcp credentials --profiles-file C:/absolute/path/mysql-connections.toml delete dev
198
172
  ```
199
173
 
200
- The keyring backend uses the platform credential service (Windows Credential
201
- Manager on Windows). Headless services can instead set
202
- `credential_provider = "command"` and a `credential_command = ["executable",
203
- "arg1"]` array for an approved secret-manager CLI. It is executed directly,
204
- never through a shell; stderr is discarded, output is bounded, and exactly one
205
- non-empty UTF-8 line is accepted. Provider failures are explicit but never
206
- include the secret, command arguments, or keyring reference.
174
+ Windows 下 `keyring` 使用 Windows 凭据管理器。无桌面的服务环境可设置 `credential_provider = "command"`,并通过 `credential_command = ["executable", "arg1"]` 调用经过批准的密钥管理 CLI。程序不会通过 shell 执行命令;stderr 会被丢弃,输出长度受限,且只接受一行非空 UTF-8 文本。失败信息不会包含密钥、命令参数或凭据引用。
207
175
 
208
- When one logical environment is physically split, declare exact aliases:
176
+ ### 逻辑环境路由
177
+
178
+ 一个逻辑环境分布在多个物理实例时,可声明精确别名:
209
179
 
210
180
  ```toml
211
181
  [routes.shared-test]
@@ -214,12 +184,9 @@ gts = { connection = "test-gts", database = "gts" }
214
184
  eam = { connection = "test-eam", database = "eam" }
215
185
  ```
216
186
 
217
- A call with `connection="shared-test", database="eam"` resolves only that
218
- declared route. Unknown aliases fail with the configured choices; the server
219
- does not search or guess another environment. JSON results and audit records
220
- contain both requested and resolved targets.
187
+ 调用 `connection="shared-test", database="eam"` 时,只会解析到声明的目标。未知别名会明确失败,服务不会搜索或猜测其他环境。JSON 结果和审计记录会同时保留请求目标与实际目标。
221
188
 
222
- Each tool call can now target an environment without restarting the server:
189
+ 每次调用都可以选择环境,无需重启:
223
190
 
224
191
  ```json
225
192
  {
@@ -228,337 +195,331 @@ Each tool call can now target an environment without restarting the server:
228
195
  "query": "SELECT COUNT(*) FROM orders",
229
196
  "audit_context": {
230
197
  "actor": "reporting-service",
231
- "purpose": "month-end reconciliation",
198
+ "purpose": "月末对账",
232
199
  "ticket_id": "FIN-2026-042"
233
200
  }
234
201
  }
235
202
  ```
236
203
 
237
- There is deliberately no process-global “switch” tool: explicit per-call selection prevents one concurrent MCP client from silently changing another client's active environment.
204
+ 服务刻意不提供进程级“切换连接/数据库”工具。显式逐次选择可防止一个并发客户端悄悄改变另一个客户端的活动环境。
238
205
 
239
- The profiles file is hot-reloaded when its size or modification time changes.
240
- One invalid profile is reported by `validate_connections` without disabling other
241
- valid profiles. Password environment variables are resolved only when their
242
- profile is used, so an unavailable production credential does not break local
243
- development.
206
+ 配置文件的大小或修改时间变化后会自动热加载。单个配置无效时,`validate_connections` 会报告该配置,但不会停用其他有效配置。密码环境变量只在使用对应连接时解析,因此生产凭据暂时不可用不会影响本地开发环境。
244
207
 
245
- ### Legacy single connection
208
+ ### 旧版单连接环境变量
246
209
 
247
- If `MYSQL_PROFILES_FILE` is not set, the original environment variables still work:
210
+ 未设置 `MYSQL_PROFILES_FILE` 时,仍可使用原有环境变量:
248
211
 
249
212
  ```bash
250
- MYSQL_HOST=localhost # Database host
251
- MYSQL_PORT=3306 # Optional: Database port (defaults to 3306 if not specified)
213
+ MYSQL_HOST=localhost
214
+ MYSQL_PORT=3306
252
215
  MYSQL_USER=readonly_user
253
216
  MYSQL_PASSWORD=your_password
254
- MYSQL_DATABASE=your_database # Optional: Omit for multi-database mode
217
+ MYSQL_DATABASE=your_database
218
+ MYSQL_ALLOWED_DATABASES=your_database
255
219
 
256
- # Advanced Configuration
257
- MYSQL_SSL_MODE=REQUIRED # DISABLED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY
258
- MYSQL_SSL_CA= # required for VERIFY_CA / VERIFY_IDENTITY
259
- MYSQL_CONNECT_TIMEOUT=10 # Timeout in seconds
220
+ MYSQL_SSL_MODE=REQUIRED # DISABLED、REQUIRED、VERIFY_CA、VERIFY_IDENTITY
221
+ MYSQL_SSL_CA= # VERIFY_CA / VERIFY_IDENTITY 必填
222
+ MYSQL_CONNECT_TIMEOUT=10
260
223
  MYSQL_QUERY_TIMEOUT_MS=30000
261
224
  MYSQL_MAX_ROWS=500
262
225
  MYSQL_MAX_CELL_LENGTH=20000
263
226
  MYSQL_RESULT_FORMAT=json
264
227
  MYSQL_POOL_SIZE=0
265
- MYSQL_AUDIT_ENABLED=true
266
- MYSQL_AUDIT_LOG_FILE=C:/var/log/mysql-mcp/audit.jsonl
267
- MYSQL_AUDIT_LOG_MAX_BYTES=10000000
268
- MYSQL_AUDIT_LOG_BACKUP_COUNT=10
269
- MYSQL_AUDIT_HMAC_KEY_ENV=MYSQL_MCP_AUDIT_SIGNING_KEY
270
- MYSQL_MCP_AUDIT_SIGNING_KEY=replace-with-secret-manager-value
271
- MYSQL_AUDIT_REQUIRED_CONTEXT=actor,purpose,ticket_id
272
- MYSQL_AUDIT_FAIL_CLOSED=true
273
- MYSQL_AUDIT_FSYNC=true
274
- MYSQL_ALLOWED_DATABASES=your_database
275
-
276
- # Connection behaviour (Optional)
277
- MYSQL_SQL_MODE=TRADITIONAL # SQL mode applied to the connection (default: TRADITIONAL)
278
-
279
- # Compatibility (Optional)
228
+ MYSQL_SQL_MODE=TRADITIONAL
280
229
  MYSQL_CHARSET=utf8mb4
281
230
  MYSQL_COLLATION=utf8mb4_unicode_ci
282
- MYSQL_AUTH_PLUGIN= # e.g., mysql_native_password for older MySQL versions
283
- MYSQL_USE_PURE=false # Force the pure-Python connector (default: false)
284
- MYSQL_RAISE_ON_WARNINGS=false # Raise on SQL warnings (default: false)
285
-
286
- # SSE Transport (Optional)
287
- MCP_TRANSPORT=stdio # stdio or sse
288
- MCP_SSE_HOST=127.0.0.1 # Safe default: loopback only
289
- PORT=8000 # HTTP port (fallback for MCP_SSE_PORT)
290
- MCP_SSE_ALLOWED_HOSTS= # Comma-separated allowed Host headers (default: localhost:{port},127.0.0.1:{port})
291
- MCP_SSE_BEARER_TOKEN= # Optional; at least 32 characters
292
- # Set only when an authenticated reverse proxy is the sole network entry point:
293
- MCP_SSE_TRUST_PROXY_AUTH=false
294
-
295
- # SSH Tunneling (Optional)
296
- MYSQL_SSH_ENABLE=false # Set to true to enable
297
- MYSQL_SSH_HOST= # SSH jump host
298
- MYSQL_SSH_PORT=22 # SSH port
299
- MYSQL_SSH_USER= # SSH username
300
- MYSQL_SSH_KEY_PATH= # Path to SSH private key
301
- MYSQL_SSH_REMOTE_HOST=localhost # Host from the perspective of the jump host
231
+ MYSQL_USE_PURE=false
232
+ MYSQL_RAISE_ON_WARNINGS=false
233
+ ```
234
+
235
+ ### Streamable HTTP 传输(推荐)
236
+
237
+ DeepSeek Harness 等新客户端可直接连接标准 MCP Streamable HTTP 端点:
238
+
239
+ ```bash
240
+ MCP_TRANSPORT=streamable-http
241
+ MCP_HTTP_HOST=127.0.0.1
242
+ MCP_HTTP_PORT=8000
243
+ MCP_HTTP_PATH=/mcp
244
+ MCP_HTTP_ALLOWED_HOSTS=localhost:8000,127.0.0.1:8000
245
+ MCP_HTTP_BEARER_TOKEN= # 可选,至少 32 个字符
246
+ MCP_HTTP_TRUST_PROXY_AUTH=false # 仅在认证反向代理是唯一入口时启用
247
+ MCP_HTTP_SESSION_IDLE_TIMEOUT_SECONDS=1800
248
+ ```
249
+
250
+ 默认端点是 `http://127.0.0.1:8000/mcp`。非回环地址只有在启用 Bearer
251
+ 认证,或显式确认由认证反向代理保护时才允许启动。
252
+
253
+ ### 旧版 SSE 传输
254
+
255
+ ```bash
256
+ MCP_TRANSPORT=sse
257
+ MCP_SSE_HOST=127.0.0.1 # 默认仅监听回环地址
258
+ PORT=8000 # MCP_SSE_PORT 的后备值
259
+ MCP_SSE_ALLOWED_HOSTS= # 允许的 Host,逗号分隔
260
+ MCP_SSE_BEARER_TOKEN= # 可选,至少 32 个字符
261
+ MCP_SSE_TRUST_PROXY_AUTH=false # 仅在认证反向代理是唯一入口时启用
262
+ ```
263
+
264
+ 旧版端点为 `/sse` 和 `/messages/`,仅用于兼容现有客户端。部署细节见
265
+ [`ENTERPRISE_DEPLOYMENT.md`](ENTERPRISE_DEPLOYMENT.md)。
266
+
267
+ ### SSH 隧道
268
+
269
+ ```bash
270
+ MYSQL_SSH_ENABLE=false
271
+ MYSQL_SSH_HOST=
272
+ MYSQL_SSH_PORT=22
273
+ MYSQL_SSH_USER=
274
+ MYSQL_SSH_KEY_PATH=
275
+ MYSQL_SSH_REMOTE_HOST=localhost
302
276
  MYSQL_SSH_REMOTE_PORT=3306
303
- MYSQL_LOCAL_PORT=0 # 0 selects a free port; the tunnel is reused
277
+ MYSQL_LOCAL_PORT=0 # 0 表示自动选择端口,隧道会复用
304
278
  ```
305
279
 
306
- ### `.env` file loading
280
+ ### `.env` 文件
307
281
 
308
- On startup the server automatically loads a `.env` file via `python-dotenv`, so for local use you can simply:
282
+ 服务启动时会通过 `python-dotenv` 从进程工作目录及父目录加载 `.env`:
309
283
 
310
284
  ```bash
311
- cp .env.example .env # then edit with your credentials
285
+ cp .env.example .env
312
286
  ```
313
287
 
314
- The file is read from the **process working directory** (and parent directories), which works when you run the server yourself from the project folder.
288
+ Claude Desktop、Codex 等宿主通常从自己的目录启动 MCP 服务,未必能找到项目内 `.env`。这类场景应将 `MYSQL_*` 配置放入 MCP 配置的 `env` 节点,或使用命名配置文件和凭据库。
315
289
 
316
- > ⚠️ **Claude Code / Claude Desktop:** these hosts launch the server from their own working directory, so the project's `.env` will **not** be found and you'll see `Missing required database configuration`. Put your `MYSQL_*` values in the `env` block of the MCP config (shown in the Usage section below) rather than relying on `.env`.
290
+ ### 多数据库模式
317
291
 
318
- ### Multi-database mode
292
+ 未设置 `MYSQL_DATABASE` 时,服务进入多数据库模式:
319
293
 
320
- When `MYSQL_DATABASE` is not set, the server operates in multi-database mode:
321
- - `list_resources` returns all user databases (system databases are filtered out)
322
- - Pass `database` to any data tool, or use fully qualified names such as `mydb.mytable`
323
- - `USE` and multiple statements are intentionally blocked
294
+ - `list_resources` 返回过滤系统库后的可访问数据库
295
+ - 数据工具通过 `database` 参数或 `database.table` 全限定名选择数据库
296
+ - `USE` 和多语句始终被拦截
324
297
 
325
- ## Available Tools
298
+ ## 可用工具
326
299
 
327
- All tools are declared with `readOnlyHint=true` and `destructiveHint=false`.
300
+ 所有工具都声明 `readOnlyHint=true` 和 `destructiveHint=false`。
328
301
 
329
302
  ### `list_connections`
330
- Lists named profiles, default database, policy limits and readiness. It never returns hosts, usernames, passwords, or SSH key paths.
331
- It also returns a `database_routes` index so clients can select the correct
332
- profile when one environment is split across several database-specific
333
- connections, plus `logical_routes` for exact configured environment aliases.
303
+
304
+ 列出命名配置、默认数据库、策略限制和就绪状态,不返回主机、用户名、密码或 SSH 私钥路径。还会返回数据库路由索引和逻辑环境别名。
334
305
 
335
306
  ### `validate_connections`
336
- Forces a configuration reload and reports valid/invalid profiles plus missing
337
- password environment variables without opening database connections.
307
+
308
+ 强制重新加载配置,报告有效/无效配置及缺少的密码环境变量,不建立数据库连接。
338
309
 
339
310
  ### `check_connection`
340
- Runs `SELECT`/`SHOW GRANTS` health checks and returns MySQL version, current
341
- database, an irreversible account fingerprint, global read-only state, grant
342
- count, latency and active profile policy. Raw usernames and grant statements are
343
- not returned. It classifies non-read privileges and warns when the account does
344
- not provide read-only defense in depth.
311
+
312
+ 通过只读健康检查返回 MySQL 版本、当前数据库、不可逆账号指纹、全局只读状态、授权数量、延迟和当前策略。原始用户名和 `SHOW GRANTS` 内容不会返回。
345
313
 
346
314
  ### `list_databases`
347
- Lists accessible non-system databases.
348
- - **Arguments:** `connection` (optional)
349
- - For a configured logical connection, returns its declared database aliases
350
- without opening a database connection.
315
+
316
+ 列出可访问的非系统数据库。
317
+
318
+ - 参数:`connection`(可选)
319
+ - 对逻辑连接返回声明的数据库别名,不建立数据库连接
351
320
 
352
321
  ### `list_tables`
353
- Lists tables and views in a database.
354
- - **Arguments:** `connection`, `database` (both optional)
322
+
323
+ 列出指定数据库中的表和视图。
324
+
325
+ - 参数:`connection`、`database`、`table_name`/`table`(精确匹配)、`table_pattern`(支持 `*`/`?`)、`max_rows`、`offset`、`timeout_ms`、`result_format`,均可选
326
+ - 大库使用与 `execute_sql` 相同的 `truncated`/`next_offset` 分页契约
355
327
 
356
328
  ### `execute_sql`
357
- Executes exactly one read-only statement.
358
- - **Arguments:** `query` (required); `connection`, `database`, `max_rows`, `offset`, `timeout_ms`, `result_format`, `audit_context` (optional unless required by the profile)
359
- - **Allowed statement families:** `SELECT`, `WITH`, `SHOW`, `DESCRIBE`, `DESC`, `EXPLAIN`, `TABLE`
360
- - **Always blocked:** DML, DDL, `USE`, transaction control, locks, `SELECT ... INTO`, session-variable assignment, MySQL executable comments, and multiple statements
361
- - **Function policy:** unresolved stored functions/UDFs are blocked by default because `SELECT function(...)` can hide database or external side effects. `allowed_functions` is an explicit reviewed-function exception.
362
- - **Defense in depth:** validation occurs before connecting; MySQL then executes inside `START TRANSACTION READ ONLY`. Completed calls roll back; truncated/cancelled calls close the socket so MySQL rolls back and stops producing unread rows.
363
- - **Cross-database:** use the `database` argument or a qualified `database.table` name
364
- - **Timeout/cancellation:** profile timeout, Connector socket timeout and MySQL/MariaDB statement timeout are combined. MCP cancellation closes the active socket; no `KILL` privilege is required.
365
- - **Pagination:** JSON output includes `truncated` and `next_offset`; pass that value back as `offset`.
366
- - **Formats:** `json` preserves structured rows and metadata; `csv` uses RFC-style quoting and explicit `NULL`.
367
- - **Attribution:** `audit_context` supports `actor`, `purpose`, and `ticket_id`. A profile can require any of these fields before a connection is opened.
368
- - **Transient connector recovery:** client-side Connector/Python failures with
369
- `errno=-1` are retried once. Persistent failures report only a safe exception
370
- type and lifecycle phase; connector messages are not exposed.
329
+
330
+ 执行且只执行一条只读语句。
331
+
332
+ - 必填参数:`query`
333
+ - 可选参数:`connection`、`database`、`max_rows`、`offset`、`timeout_ms`、`result_format`、`audit_context`
334
+ - 允许:`SELECT`、`WITH`、受数据库范围约束的 `SHOW`(包括 `SHOW CREATE TABLE/VIEW`)、`DESCRIBE`、`DESC`、`EXPLAIN`、`TABLE`
335
+ - 始终拦截:DML、DDL、`USE`、事务控制、锁、`SELECT ... INTO`、会话变量赋值、MySQL 可执行注释和多语句
336
+ - 函数策略:默认拦截无法识别的存储函数/UDF;只有 `allowed_functions` 中经过审核的确定性函数可放行
337
+ - 纵深防御:连接前先校验;随后在 `START TRANSACTION READ ONLY` 中执行并回滚
338
+ - 分页:对适用的 `SELECT`/CTE/UNION 下推 `LIMIT max_rows+1 OFFSET offset`,完整消费该有界结果后再回滚;JSON 结果返回 `truncated` 和 `next_offset`
339
+ - 跨库:使用 `database` 参数或 `database.table` 全限定名
340
+ - 超时/取消:统一限制完整操作、Connector socket 和 MySQL/MariaDB 语句时间;取消请求会关闭活动连接,无需 `KILL` 权限
341
+ - 格式:`json` 保留结构化数据和元信息;`csv` 使用标准引号规则,并明确表示 `NULL`
342
+ - 审计归因:`audit_context` 支持 `actor`、`purpose`、`ticket_id`,配置可要求调用前必须提供指定字段
343
+ - 临时故障恢复:Connector/Python 客户端侧 `errno=-1` 会重试一次;持续失败只返回安全的异常类型和生命周期阶段
371
344
 
372
345
  ### `query`
373
- Compatibility alias for `execute_sql` with the same schema and all the same
374
- read-only, database allowlist, masking, timeout, pagination, and audit controls.
375
- Stateful legacy tools such as `use_connection` and `use_database` are
376
- intentionally not restored because process-global selection is unsafe with
377
- concurrent MCP clients.
346
+
347
+ `execute_sql` 的兼容别名,使用相同的只读、白名单、脱敏、超时、分页和审计策略。不会恢复不安全的进程级 `use_connection` 或 `use_database`。
378
348
 
379
349
  ### `get_schema_info`
380
- Provides detailed metadata about database structures.
381
- - **Arguments:** `table_name`, `connection`, `database` (all optional)
382
- - **Output:** Column names, types, nullability, default values, and comments.
383
- - **Cross-database:** Pass `database.table` to query a table outside `MYSQL_DATABASE`; bare names use the configured database.
384
- - **Identifier rules:** Names must contain only alphanumeric characters, underscores, and `$` (dots are allowed as a separator between database and table names).
350
+
351
+ 返回数据库结构详情,包括字段名、类型、可空性、默认值和注释。
352
+
353
+ - 参数:`table_name` 或兼容别名 `table`,以及 `connection`、`database`
354
+ - 可用 `database.table` 访问允许列表中的其他数据库
355
+ - 标识符只允许字母、数字、下划线、`$`,数据库与表之间允许一个点
385
356
 
386
357
  ### `get_table_sample`
387
- Fetches a representative sample of data.
388
- - **Arguments:** `table_name` (required), `limit` (optional integer, max 100), `offset`, `connection`, `database`, `timeout_ms`, `result_format`
389
- - **Use Case:** Quickly understand data formats and content without fetching large result sets.
390
- - **Cross-database:** Pass `database.table` to sample a table outside `MYSQL_DATABASE`; bare names use the configured database.
391
- - **Identifier rules:** Names must contain only alphanumeric characters, underscores, and `$` (dots are allowed as a separator between database and table names).
392
- - **Bounded execution:** Sampling applies `LIMIT limit+1 OFFSET offset` in
393
- MySQL and fully consumes that small result. It does not open an unbounded
394
- table scan merely to return a few rows.
358
+
359
+ 返回具有代表性的少量样例数据。
360
+
361
+ - `table_name` 或兼容别名 `table` 必须提供一个
362
+ - 可选参数:`limit`(最大 100)、`offset`、`connection`、`database`、`timeout_ms`、`result_format`
363
+ - 采样会在 MySQL 中应用 `LIMIT limit+1 OFFSET offset` 并完整消费小结果集,不会为了返回几行数据而开启无界扫描结果
395
364
 
396
365
  ### `inspect_catalog`
397
- Returns one fixed metadata projection for `tables`, `columns`, `indexes`,
398
- `constraints`, `foreign_keys`, or `views`, optionally filtered by a validated
399
- bare table name. `connection` and `database` follow the same explicit routing
400
- rules as other tools. This does not permit arbitrary `information_schema` SQL.
401
366
 
402
- ## Available Prompts
367
+ 返回 `tables`、`columns`、`indexes`、`constraints`、`foreign_keys` 或 `views` 的固定元数据投影。可通过 `table_name` 或 `table` 精确过滤。该工具不会开放任意 `information_schema` SQL。
403
368
 
404
- In addition to tools, the server exposes **MCP prompts** — guided, multi-step workflows that a client can launch on demand. In Claude Code they appear as slash commands (`/mcp__<server>__<prompt>`); in Claude Desktop they appear in the prompts (`+`) menu.
369
+ ## 可用提示词
405
370
 
406
- | Prompt | Arguments | Description |
371
+ | 提示词 | 参数 | 说明 |
407
372
  | --- | --- | --- |
408
- | `explore_database` | `connection`, `database` *(optional)* | Systematically explore the selected database: discover tables, inspect schemas, sample data, and summarize it. |
409
- | `analyze_table` | `table_name` *(required)*; `connection`, `database` *(optional)* | Deep-dive into a specific table. Accepts `database.table` notation. |
373
+ | `explore_database` | `connection`、`database`(可选) | 依次发现表、检查结构、采样并总结数据库 |
374
+ | `analyze_table` | `table_name`(必填)、`connection`、`database`(可选) | 深入分析指定表,支持 `database.table` |
410
375
 
411
- **Example (Claude Code):**
412
- ```
413
- /mcp__mysql__explore_database
414
- /mcp__mysql__analyze_table customers
415
- ```
376
+ 这些提示词只编排只读的发现、结构检查和采样工具。
377
+
378
+ ## MCP 客户端配置
416
379
 
417
- Both prompts orchestrate the read-only discovery, schema, and sampling tools.
380
+ ### Claude Desktop
381
+
382
+ 在 `claude_desktop_config.json` 中加入:
418
383
 
419
- ## Usage
420
- ### With Claude Desktop
421
- Add this to your `claude_desktop_config.json`:
422
384
  ```json
423
385
  {
424
386
  "mcpServers": {
425
387
  "mysql-readonly": {
426
388
  "command": "npx",
427
- "args": ["-y", "@yanzhang123/readonly-db-mcp"],
389
+ "args": ["-y", "@yanzhang123/readonly-db-mcp@0.8.2"],
428
390
  "env": {
429
391
  "MYSQL_PROFILES_FILE": "C:/absolute/path/mysql-connections.toml",
430
- "MYSQL_DEV_PASSWORD": "your_dev_password",
431
- "MYSQL_PROD_PASSWORD": "your_prod_password"
392
+ "MYSQL_DEV_PASSWORD": "your_dev_password"
432
393
  }
433
394
  }
434
395
  }
435
396
  }
436
397
  ```
437
398
 
438
- For more detailed examples and agent-specific guidance, see [MCP_USECASES.md](MCP_USECASES.md).
399
+ ### Visual Studio Code / Codex
400
+
401
+ 在 `mcp.json` 或对应 MCP 配置中加入:
439
402
 
440
- ### With Visual Studio Code
441
- Add this to your `mcp.json`:
442
403
  ```json
443
404
  {
444
405
  "mcpServers": {
445
406
  "mysql-readonly": {
446
407
  "type": "stdio",
447
408
  "command": "npx",
448
- "args": ["-y", "@yanzhang123/readonly-db-mcp"],
409
+ "args": ["-y", "@yanzhang123/readonly-db-mcp@0.8.2"],
449
410
  "env": {
450
411
  "MYSQL_PROFILES_FILE": "C:/absolute/path/mysql-connections.toml",
451
- "MYSQL_DEV_PASSWORD": "your_dev_password",
452
- "MYSQL_PROD_PASSWORD": "your_prod_password"
412
+ "MYSQL_DEV_PASSWORD": "your_dev_password"
453
413
  }
454
414
  }
455
415
  }
456
416
  }
457
417
  ```
458
418
 
459
- Use `npx.cmd` instead of `npx` if required by your Windows MCP host.
460
-
461
- ### Debugging with MCP Inspector
462
- While MySQL MCP Server isn't intended to be run standalone or directly from the command line with Python, you can use the MCP Inspector to debug it.
463
-
464
- The MCP Inspector provides a convenient way to test and debug your MCP implementation:
419
+ Windows 宿主如无法解析 `npx`,改用 `npx.cmd`。更多调用场景见 [`MCP_USECASES.md`](MCP_USECASES.md)。
420
+
421
+ ### DeepSeek Harness
422
+
423
+ Harness 自带 `@deepseek-ai/dsh-mcp-client`。本地使用推荐挂载 npm 包的
424
+ STDIO 入口;在 Windows 上使用 `npx.cmd`:
425
+
426
+ ```yaml
427
+ - insert:
428
+ - id: mcp-mysql-readonly
429
+ name: '@deepseek-ai/dsh-mcp-client'
430
+ config:
431
+ serverName: mysql-readonly
432
+ transport: stdio
433
+ command: npx.cmd
434
+ args: ['-y', '@yanzhang123/readonly-db-mcp@0.8.2']
435
+ env:
436
+ MYSQL_PROFILES_FILE: 'C:/absolute/path/mysql-connections.toml'
437
+ toolCallTimeoutMs: 60000
438
+ failOnStartupError: true
439
+ ```
465
440
 
466
- ```bash
467
- # Install dependencies
468
- pip install -r requirements.txt
469
- # Use the MCP Inspector for debugging (do not run directly with Python)
441
+ 远程或集中部署时使用 Streamable HTTP。先按上文启动服务,然后在 Harness
442
+ 配置中挂载端点;`MYSQL_MCP_AUTHORIZATION` 的值应为 `Bearer <token>`,并
443
+ 从进程环境或密钥管理系统注入,不要提交到仓库:
444
+
445
+ ```yaml
446
+ - insert:
447
+ - id: mcp-mysql-readonly
448
+ name: '@deepseek-ai/dsh-mcp-client'
449
+ config:
450
+ serverName: mysql-readonly
451
+ transport: streamable-http
452
+ url: 'https://mcp.example.com/mcp'
453
+ headers:
454
+ Authorization: !!js process.env.MYSQL_MCP_AUTHORIZATION
455
+ toolCallTimeoutMs: 60000
456
+ failOnStartupError: true
470
457
  ```
471
458
 
472
- The MySQL MCP Server is designed to be integrated with AI applications like Claude Desktop and should not be run directly as a standalone Python program.
459
+ ## 开发与测试
473
460
 
474
- ## Development
475
461
  ```bash
476
- # Clone the repository
477
462
  git clone https://github.com/12zhangyan/mysql_mcp_server.git
478
463
  cd mysql_mcp_server
479
- # Create virtual environment
480
- python -m venv venv
481
- # Linux/macOS: source venv/bin/activate
482
- # Windows PowerShell: .\venv\Scripts\Activate.ps1
483
- # Install development dependencies
464
+ python -m venv .venv
465
+
466
+ # Linux/macOS
467
+ source .venv/bin/activate
468
+
469
+ # Windows PowerShell
470
+ .\.venv\Scripts\Activate.ps1
471
+
484
472
  pip install -r requirements-dev.txt
485
- # Copy the example config and edit with your credentials
486
- # Linux/macOS: cp .env.example .env
487
- # Windows PowerShell: Copy-Item .env.example .env
488
- # Edit .env with your MySQL connection details
489
- # Run tests
490
473
  pytest
474
+ npm test
491
475
  ```
492
476
 
493
- ## Security Considerations
494
- - **Read-only SQL gate:** Only result-producing read statements are accepted; write/DDL/transaction/locking constructs are rejected before a connection is opened.
495
- - **Database enforcement:** Every exposed query runs with `autocommit=false` in a read-only transaction. Normal completion rolls back; cancellation or truncation closes the socket and forces server-side rollback.
496
- - **Write-capable credentials:** Read-only behavior does not depend on account grants. Unrecognized stored functions/UDFs, side-effecting functions, sequence advancement and locking reads are rejected because they can hide effects inside `SELECT`.
497
- - **Database allowlist:** `allowed_databases` is enforced with a MySQL AST parser for tools and resource URIs; system schemas are blocked by default.
498
- - **Controlled metadata:** User SQL cannot query `information_schema` by
499
- default. Dedicated `list_tables`, `get_schema_info`, and `inspect_catalog`
500
- operations use narrowly scoped internal metadata queries, so catalog access
501
- does not become a general system-schema bypass.
502
- - **Credential isolation:** Named profiles can resolve passwords from an OS
503
- keyring or an approved no-shell command. Discovery, logs, errors and audit
504
- events omit credential values and keyring references.
505
- - **Resource limits:** Calls have full-operation, socket and server statement timeouts; at most 1000 rows are returned and oversized cells are truncated.
506
- - **Cancellation:** Cancelling the MCP request closes the active connector socket. The worker does not continue silently after the response is abandoned.
507
- - **Enterprise audit:** Versioned UTC JSONL events include an event ID, MCP request ID, operation, caller-supplied attribution, policy decision, target database, literal-free query fingerprint, duration, result size and outcome. SQL text and result data are never logged. Optional rotation, fsync, HMAC signatures, required context and fail-closed behavior are supported.
508
- - **Two-phase audit:** In fail-closed mode, an fsynced `started` event is persisted before opening a database connection, followed by the terminal outcome event.
509
- - **Result masking:** Sensitive output names are redacted by default. Queries whose expression tree references a sensitive source column are conservatively redacted even when aliases or CTEs hide the original name. Review `mask_columns` for each schema; setting it to an empty list explicitly disables masking.
510
- - **Encrypted by default:** Database TLS defaults to `REQUIRED` and the connected session is rejected if the connector reports plaintext. Production profiles should use `VERIFY_CA` or `VERIFY_IDENTITY` with `ssl_ca`.
511
- - **Parser hardening:** MySQL `/*!...*/` and MariaDB `/*M!...*/` executable comments are rejected, and server-wide `SHOW` variants are blocked unless they can be scoped to an allowed database.
512
- - **Identifier Validation:** Table and database names passed to `get_schema_info` and `get_table_sample` are validated against a strict whitelist (alphanumeric, underscore, and `$` only; a single dot is allowed as a `database.table` separator). Other special characters are rejected to prevent SQL injection.
513
- - **Encrypted Access:** Full support for SSL/TLS and SSH Tunneling for secure remote connections.
514
- - **Log Privacy:** SQL text, passwords, hosts, usernames and SSH private-key paths are not included in tool discovery or audit events.
515
- - **Diagnostic Privacy:** Connection diagnostics expose an account fingerprint and privilege summary, not raw usernames or `SHOW GRANTS`; configuration paths and database-supplied error text are redacted.
516
- - **Least Privilege:** A dedicated `SELECT` account is recommended as additional defense, but it is not assumed by the MCP read-only enforcement. `check_connection` warns about broader grants without disabling the profile.
517
- - **SSE is loopback-only by default.** Configure `MCP_SSE_BEARER_TOKEN` with at least 32 characters to enable built-in bearer authentication. A non-loopback bind is refused unless bearer authentication is enabled or `MCP_SSE_TRUST_PROXY_AUTH=true` explicitly confirms that an authenticated reverse proxy is the only network entry point. Example with nginx and HTTP Basic Auth:
518
-
519
- ```nginx
520
- location /sse {
521
- auth_basic "MCP";
522
- auth_basic_user_file /etc/nginx/.htpasswd;
523
- proxy_pass http://127.0.0.1:8000;
524
- proxy_set_header Host $host;
525
- proxy_buffering off;
526
- }
527
- location /messages/ {
528
- auth_basic "MCP";
529
- auth_basic_user_file /etc/nginx/.htpasswd;
530
- proxy_pass http://127.0.0.1:8000;
531
- proxy_set_header Host $host;
532
- }
533
- ```
534
-
535
- Keep `MCP_SSE_HOST=127.0.0.1` so the proxy is the sole public entry point. If a container requires `0.0.0.0`, set `MCP_SSE_TRUST_PROXY_AUTH=true` only after network policy prevents direct access. Set `MCP_SSE_ALLOWED_HOSTS` to the hostname forwarded by the proxy.
536
-
537
- See [SECURITY.md](SECURITY.md) and [ENTERPRISE_DEPLOYMENT.md](ENTERPRISE_DEPLOYMENT.md) for deployment and audit guidance.
538
- Maintainers should follow [RELEASING.md](RELEASING.md) for synchronized Python/npm version releases.
539
-
540
- ## Security Best Practices
541
- This MCP implementation requires database access to function. For security:
542
- 1. **Prefer a dedicated MySQL user** with `SELECT` only as defense in depth; MCP enforcement remains read-only even when broader credentials are supplied
543
- 2. **Never use root credentials** or administrative accounts
544
- 3. **Restrict `allowed_databases`** to the schemas each profile needs
545
- 4. **Require audit attribution** and protect the optional HMAC key in a secret manager
546
- 5. **Review masking patterns, audit events, dependency updates, and account grants regularly**
547
-
548
- See [MySQL Security Configuration Guide](https://github.com/12zhangyan/mysql_mcp_server/blob/main/SECURITY.md) for detailed instructions on:
549
- - Creating a restricted MySQL user
550
- - Setting appropriate permissions
551
- - Monitoring database access
552
- - Security best practices
553
-
554
- ⚠️ IMPORTANT: Always follow the principle of least privilege when configuring database access.
555
-
556
- ## License
557
- MIT License - see LICENSE file for details.
558
-
559
- ## Contributing
560
- 1. Fork the repository
561
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
562
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
563
- 4. Push to the branch (`git push origin feature/amazing-feature`)
564
- 5. Open a Pull Request
477
+ 调试 MCP 协议交互时可使用 MCP Inspector,不建议直接把 Python 进程当作普通命令行程序交互。
478
+
479
+ ## 安全设计
480
+
481
+ - **只读 SQL 闸门**:仅允许产生结果的只读语句;写入、DDL、事务、锁和有副作用的结构在连接前被拒绝
482
+ - **数据库侧约束**:所有查询以 `autocommit=false` 运行在只读事务中,正常结束统一回滚,取消时关闭连接触发服务端回滚
483
+ - **可写账号防护**:MCP 的只读行为不依赖账号授权;仍建议用专用 `SELECT` 账号作为独立防线
484
+ - **数据库白名单**:工具和资源 URI 均通过 MySQL AST 解析执行 `allowed_databases`,系统库默认禁止
485
+ - **受控元数据**:用户 SQL 默认不能直接查询 `information_schema`;使用 `list_tables`、`get_schema_info`、`inspect_catalog` 获取限定范围的元数据
486
+ - **凭据隔离**:密码可来自环境变量、操作系统凭据库或无 shell 的受控命令;发现接口、日志和错误均不返回凭据值或引用
487
+ - **资源限制**:整次调用、socket 和服务端语句均有超时;最多返回 1000 行,超长单元格会截断
488
+ - **精确脱敏**:依据表达式源字段脱敏,别名不能绕过规则,也不会连带遮罩无关列;JSON 中 `clientSecret`、`AppSec` 等敏感键递归脱敏
489
+ - **安全默认规则**:覆盖常见 password/PWD、mobile/phone、secret、凭据 token、API key、private key 命名;不会用宽泛的 `*token*` 误伤通道代码或 token 有效时长
490
+ - **企业审计**:UTC JSONL 事件包含事件 ID、MCP 请求 ID、操作、调用方归因、策略决定、目标库、无字面量查询指纹、耗时、结果大小和结果状态;不记录 SQL 原文和结果数据
491
+ - **两阶段审计**:失败关闭模式下,建立连接前先 fsync 写入 `started` 事件,随后记录终态事件
492
+ - **默认加密**:数据库 TLS 默认 `REQUIRED`;生产配置建议使用 `VERIFY_CA` 或 `VERIFY_IDENTITY`
493
+ - **解析器加固**:拒绝 MySQL `/*!...*/` 和 MariaDB `/*M!...*/` 可执行注释;未限定到允许数据库的全局 `SHOW` 被拦截
494
+ - **诊断隐私**:连接诊断只公开不可逆账号指纹和权限摘要,不返回用户名、授权原文、配置路径或数据库错误原文
495
+ - **SSE 安全边界**:默认仅绑定回环地址;非回环部署必须配置 Bearer 认证或由唯一入口的认证反向代理保护
496
+
497
+ 详细安全和企业部署说明:
498
+
499
+ - [`SECURITY.md`](SECURITY.md)
500
+ - [`ENTERPRISE_DEPLOYMENT.md`](ENTERPRISE_DEPLOYMENT.md)
501
+
502
+ ## 安全最佳实践
503
+
504
+ 1. 使用仅授予 `SELECT` 的专用 MySQL 用户,不使用 root 或管理员账号
505
+ 2. 通过 `allowed_databases` 将每个连接限制到实际需要的数据库
506
+ 3. 对生产连接启用证书校验,并限制网络访问来源
507
+ 4. 对重要环境要求完整的 `audit_context`,审计 HMAC 密钥由密钥管理系统提供
508
+ 5. 定期复核脱敏规则、审计事件、依赖版本和账号授权
509
+
510
+ ## 发布
511
+
512
+ Python 与 npm 使用同一版本号。维护者应按 [`RELEASING.md`](RELEASING.md) 完成同步版本、测试、打包和发布。
513
+
514
+ ## 参与贡献
515
+
516
+ 1. Fork 仓库
517
+ 2. 创建功能分支
518
+ 3. 提交修改并运行相关测试
519
+ 4. 推送分支并创建 Pull Request
520
+
521
+ 提交安全问题前请先阅读 [`SECURITY.md`](SECURITY.md)。
522
+
523
+ ## 许可证
524
+
525
+ 本项目使用 MIT License,详见 [`LICENSE`](LICENSE)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanzhang123/readonly-db-mcp",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Enterprise-auditable, strictly read-only MySQL MCP server with multi-environment profiles.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",