@yanzhang123/readonly-db-mcp 0.7.6 → 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.
@@ -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 `get_schema_info` operations use narrowly
449
- scoped internal metadata queries, so catalog access does not become a general
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
@@ -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 pyjwt
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanzhang123/readonly-db-mcp",
3
- "version": "0.7.6",
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",