@yanzhang123/readonly-db-mcp 0.7.5 → 0.8.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/ENTERPRISE_DEPLOYMENT.md
CHANGED
|
@@ -79,6 +79,26 @@ audit_fail_closed = true
|
|
|
79
79
|
audit_fsync = true
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
For desktop deployments, replace `password_env` with an OS keyring reference so
|
|
83
|
+
user-level MCP configuration contains no database password:
|
|
84
|
+
|
|
85
|
+
```toml
|
|
86
|
+
credential_provider = "keyring"
|
|
87
|
+
credential_ref = "prod"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Provision it interactively with `readonly-db-mcp credentials --profiles-file
|
|
91
|
+
C:/absolute/path/mysql-connections.toml set prod`. The tool never prints the
|
|
92
|
+
credential. For non-interactive services, the `command` provider accepts a
|
|
93
|
+
reviewed argv array for a secret-manager CLI and executes it without a shell.
|
|
94
|
+
Restrict the profiles file because command names and non-secret arguments can
|
|
95
|
+
still reveal infrastructure topology.
|
|
96
|
+
|
|
97
|
+
If an environment spans physical profiles, use `[routes.ENVIRONMENT]` with
|
|
98
|
+
exact database aliases. Each audit event records `requested_connection`,
|
|
99
|
+
`requested_database`, the resolved physical `connection`/`database`, and
|
|
100
|
+
`route_applied`; unknown aliases fail rather than being guessed.
|
|
101
|
+
|
|
82
102
|
All database tools accept:
|
|
83
103
|
|
|
84
104
|
```json
|
|
@@ -196,6 +216,10 @@ read-only operations were sent:
|
|
|
196
216
|
- configuration validation, connection health and grants inspection
|
|
197
217
|
- allowed-database discovery and system-schema filtering
|
|
198
218
|
- table/schema metadata reads
|
|
219
|
+
- controlled catalog projections for tables, columns, indexes, constraints,
|
|
220
|
+
foreign keys and views while arbitrary system-schema SQL remains denied
|
|
221
|
+
- deterministic keyring, command-provider and explicit-route tests with no
|
|
222
|
+
production credential material
|
|
199
223
|
- normal query execution and pagination
|
|
200
224
|
- pre-connection rejection of DML, DDL, multiple statements, locking reads and
|
|
201
225
|
system-schema access
|
package/README.md
CHANGED
|
@@ -176,6 +176,49 @@ MYSQL_ALLOWED_FUNCTIONS= # optional reviewed function names
|
|
|
176
176
|
|
|
177
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.
|
|
178
178
|
|
|
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:
|
|
181
|
+
|
|
182
|
+
```toml
|
|
183
|
+
[connections.dev]
|
|
184
|
+
host = "127.0.0.1"
|
|
185
|
+
user = "readonly_user"
|
|
186
|
+
credential_provider = "keyring"
|
|
187
|
+
credential_ref = "dev"
|
|
188
|
+
database = "app_dev"
|
|
189
|
+
allowed_databases = ["app_dev"]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Store or manage it locally with a masked prompt:
|
|
193
|
+
|
|
194
|
+
```powershell
|
|
195
|
+
readonly-db-mcp credentials --profiles-file C:/absolute/path/mysql-connections.toml set dev
|
|
196
|
+
readonly-db-mcp credentials --profiles-file C:/absolute/path/mysql-connections.toml status dev
|
|
197
|
+
readonly-db-mcp credentials --profiles-file C:/absolute/path/mysql-connections.toml delete dev
|
|
198
|
+
```
|
|
199
|
+
|
|
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.
|
|
207
|
+
|
|
208
|
+
When one logical environment is physically split, declare exact aliases:
|
|
209
|
+
|
|
210
|
+
```toml
|
|
211
|
+
[routes.shared-test]
|
|
212
|
+
ubp = { connection = "test-ubp", database = "ubp" }
|
|
213
|
+
gts = { connection = "test-gts", database = "gts" }
|
|
214
|
+
eam = { connection = "test-eam", database = "eam" }
|
|
215
|
+
```
|
|
216
|
+
|
|
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.
|
|
221
|
+
|
|
179
222
|
Each tool call can now target an environment without restarting the server:
|
|
180
223
|
|
|
181
224
|
```json
|
|
@@ -287,7 +330,7 @@ All tools are declared with `readOnlyHint=true` and `destructiveHint=false`.
|
|
|
287
330
|
Lists named profiles, default database, policy limits and readiness. It never returns hosts, usernames, passwords, or SSH key paths.
|
|
288
331
|
It also returns a `database_routes` index so clients can select the correct
|
|
289
332
|
profile when one environment is split across several database-specific
|
|
290
|
-
connections.
|
|
333
|
+
connections, plus `logical_routes` for exact configured environment aliases.
|
|
291
334
|
|
|
292
335
|
### `validate_connections`
|
|
293
336
|
Forces a configuration reload and reports valid/invalid profiles plus missing
|
|
@@ -303,6 +346,8 @@ not provide read-only defense in depth.
|
|
|
303
346
|
### `list_databases`
|
|
304
347
|
Lists accessible non-system databases.
|
|
305
348
|
- **Arguments:** `connection` (optional)
|
|
349
|
+
- For a configured logical connection, returns its declared database aliases
|
|
350
|
+
without opening a database connection.
|
|
306
351
|
|
|
307
352
|
### `list_tables`
|
|
308
353
|
Lists tables and views in a database.
|
|
@@ -348,6 +393,12 @@ Fetches a representative sample of data.
|
|
|
348
393
|
MySQL and fully consumes that small result. It does not open an unbounded
|
|
349
394
|
table scan merely to return a few rows.
|
|
350
395
|
|
|
396
|
+
### `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
|
+
|
|
351
402
|
## Available Prompts
|
|
352
403
|
|
|
353
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.
|
|
@@ -445,9 +496,12 @@ pytest
|
|
|
445
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`.
|
|
446
497
|
- **Database allowlist:** `allowed_databases` is enforced with a MySQL AST parser for tools and resource URIs; system schemas are blocked by default.
|
|
447
498
|
- **Controlled metadata:** User SQL cannot query `information_schema` by
|
|
448
|
-
default. Dedicated `list_tables` and `
|
|
449
|
-
scoped internal metadata queries, so catalog access
|
|
450
|
-
system-schema bypass.
|
|
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.
|
|
451
505
|
- **Resource limits:** Calls have full-operation, socket and server statement timeouts; at most 1000 rows are returned and oversized cells are truncated.
|
|
452
506
|
- **Cancellation:** Cancelling the MCP request closes the active connector socket. The worker does not continue silently after the response is abandoned.
|
|
453
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.
|
package/SECURITY.md
CHANGED
|
@@ -59,6 +59,13 @@ FLUSH PRIVILEGES;
|
|
|
59
59
|
|
|
60
60
|
When setting up the MCP server, use these restricted credentials in your environment:
|
|
61
61
|
|
|
62
|
+
Prefer `credential_provider = "keyring"` for desktop MCP clients so passwords
|
|
63
|
+
do not appear in user-level MCP JSON or TOML. The local credentials command uses
|
|
64
|
+
a masked prompt and never returns stored values. Enterprise secret-manager
|
|
65
|
+
commands must be configured as an argv array; they run with `shell=false`, no
|
|
66
|
+
stdin, discarded stderr, a bounded timeout/output size, and a one-line output
|
|
67
|
+
contract. Do not embed secrets in command arguments.
|
|
68
|
+
|
|
62
69
|
```bash
|
|
63
70
|
MYSQL_USER=mcp_user
|
|
64
71
|
MYSQL_PASSWORD=your_secure_password
|
package/npm/requirements.lock
CHANGED
|
@@ -18,6 +18,10 @@ attrs==26.1.0 \
|
|
|
18
18
|
# via
|
|
19
19
|
# jsonschema
|
|
20
20
|
# referencing
|
|
21
|
+
backports-tarfile==1.2.0 ; python_full_version < '3.12' \
|
|
22
|
+
--hash=sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34 \
|
|
23
|
+
--hash=sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991
|
|
24
|
+
# via jaraco-context
|
|
21
25
|
certifi==2026.5.20 \
|
|
22
26
|
--hash=sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897 \
|
|
23
27
|
--hash=sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d
|
|
@@ -144,7 +148,9 @@ cryptography==48.0.0 \
|
|
|
144
148
|
--hash=sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb \
|
|
145
149
|
--hash=sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c \
|
|
146
150
|
--hash=sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b
|
|
147
|
-
# via
|
|
151
|
+
# via
|
|
152
|
+
# pyjwt
|
|
153
|
+
# secretstorage
|
|
148
154
|
h11==0.16.0 \
|
|
149
155
|
--hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \
|
|
150
156
|
--hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
|
|
@@ -169,6 +175,28 @@ idna==3.18 \
|
|
|
169
175
|
# via
|
|
170
176
|
# anyio
|
|
171
177
|
# httpx
|
|
178
|
+
importlib-metadata==9.0.0 ; python_full_version < '3.12' \
|
|
179
|
+
--hash=sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7 \
|
|
180
|
+
--hash=sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc
|
|
181
|
+
# via keyring
|
|
182
|
+
jaraco-classes==3.4.0 \
|
|
183
|
+
--hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \
|
|
184
|
+
--hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790
|
|
185
|
+
# via keyring
|
|
186
|
+
jaraco-context==6.1.2 \
|
|
187
|
+
--hash=sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535 \
|
|
188
|
+
--hash=sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3
|
|
189
|
+
# via keyring
|
|
190
|
+
jaraco-functools==4.6.0 \
|
|
191
|
+
--hash=sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280 \
|
|
192
|
+
--hash=sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30
|
|
193
|
+
# via keyring
|
|
194
|
+
jeepney==0.9.0 ; sys_platform == 'linux' \
|
|
195
|
+
--hash=sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683 \
|
|
196
|
+
--hash=sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732
|
|
197
|
+
# via
|
|
198
|
+
# keyring
|
|
199
|
+
# secretstorage
|
|
172
200
|
jsonschema==4.26.0 \
|
|
173
201
|
--hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \
|
|
174
202
|
--hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce
|
|
@@ -177,10 +205,20 @@ jsonschema-specifications==2025.9.1 \
|
|
|
177
205
|
--hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \
|
|
178
206
|
--hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d
|
|
179
207
|
# via jsonschema
|
|
208
|
+
keyring==25.7.0 \
|
|
209
|
+
--hash=sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f \
|
|
210
|
+
--hash=sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b
|
|
211
|
+
# via mysql-mcp-server
|
|
180
212
|
mcp==1.27.2 \
|
|
181
213
|
--hash=sha256:8e02db104096d1c25b28e64bde29a5c32b31bc241710213e12fd4d84985bdfef \
|
|
182
214
|
--hash=sha256:d6ff5160c6ca65d93013626efb3fc249de683c30b2d8570755ceddd490344de5
|
|
183
215
|
# via mysql-mcp-server
|
|
216
|
+
more-itertools==11.1.0 \
|
|
217
|
+
--hash=sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d \
|
|
218
|
+
--hash=sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192
|
|
219
|
+
# via
|
|
220
|
+
# jaraco-classes
|
|
221
|
+
# jaraco-functools
|
|
184
222
|
mysql-connector-python==9.7.0 \
|
|
185
223
|
--hash=sha256:079ce68d617250ef5cefffd5243056dc9f87c6034c804235fd6f45a0daf5a6ae \
|
|
186
224
|
--hash=sha256:15106ed73d74487f86de6b1859ff7f362efca7c7f9c494497ccb7439d3139fe6 \
|
|
@@ -343,6 +381,10 @@ pywin32==312 ; sys_platform == 'win32' \
|
|
|
343
381
|
--hash=sha256:d11417d84412f859b722fad0841b3614459ed0047f7542d8362e77884f6b6e8a \
|
|
344
382
|
--hash=sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b
|
|
345
383
|
# via mcp
|
|
384
|
+
pywin32-ctypes==0.2.3 ; sys_platform == 'win32' \
|
|
385
|
+
--hash=sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8 \
|
|
386
|
+
--hash=sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755
|
|
387
|
+
# via keyring
|
|
346
388
|
referencing==0.37.0 \
|
|
347
389
|
--hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \
|
|
348
390
|
--hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8
|
|
@@ -483,6 +525,10 @@ rpds-py==2026.5.1 \
|
|
|
483
525
|
# via
|
|
484
526
|
# jsonschema
|
|
485
527
|
# referencing
|
|
528
|
+
secretstorage==3.5.0 ; sys_platform == 'linux' \
|
|
529
|
+
--hash=sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137 \
|
|
530
|
+
--hash=sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be
|
|
531
|
+
# via keyring
|
|
486
532
|
sqlglot==30.14.0 \
|
|
487
533
|
--hash=sha256:df2ef5d2b8ca814313781f4ff35bf63e58f821ef517eeddbd523c19a61fa9bb9 \
|
|
488
534
|
--hash=sha256:fc768e24889d63a5e1237dea7ad305e5ffb4356a98b0bed828f89591ebcd3636
|
|
@@ -519,3 +565,7 @@ uvicorn==0.49.0 ; sys_platform != 'emscripten' \
|
|
|
519
565
|
--hash=sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f \
|
|
520
566
|
--hash=sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3
|
|
521
567
|
# via mcp
|
|
568
|
+
zipp==4.1.0 ; python_full_version < '3.12' \
|
|
569
|
+
--hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \
|
|
570
|
+
--hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602
|
|
571
|
+
# via importlib-metadata
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yanzhang123/readonly-db-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Enterprise-auditable, strictly read-only MySQL MCP server with multi-environment profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"url": "git+https://github.com/12zhangyan/mysql_mcp_server.git"
|
|
15
15
|
},
|
|
16
16
|
"bin": {
|
|
17
|
+
"readonly-db-mcp": "npm/bin/mysql-mcp-server-readonly.cjs",
|
|
17
18
|
"mysql-mcp-server-readonly": "npm/bin/mysql-mcp-server-readonly.cjs"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
Binary file
|