@sigma4life/mysql-mcp-server 1.0.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.
Files changed (149) hide show
  1. package/.env.example +35 -0
  2. package/.github/workflows/ci.yml +18 -0
  3. package/AUTHENTICATION.md +24 -0
  4. package/CLAUDE.md +37 -0
  5. package/CONTRIBUTING.md +19 -0
  6. package/LICENSE +21 -0
  7. package/QUERY_ACCESS_SETUP.md +65 -0
  8. package/README.md +144 -0
  9. package/SECURITY.md +10 -0
  10. package/TESTING.md +54 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +197 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/core/cache.d.ts +11 -0
  16. package/dist/core/cache.d.ts.map +1 -0
  17. package/dist/core/cache.js +30 -0
  18. package/dist/core/cache.js.map +1 -0
  19. package/dist/core/config.d.ts +24 -0
  20. package/dist/core/config.d.ts.map +1 -0
  21. package/dist/core/config.js +63 -0
  22. package/dist/core/config.js.map +1 -0
  23. package/dist/core/database-url.d.ts +11 -0
  24. package/dist/core/database-url.d.ts.map +1 -0
  25. package/dist/core/database-url.js +37 -0
  26. package/dist/core/database-url.js.map +1 -0
  27. package/dist/core/database-url.test.d.ts +2 -0
  28. package/dist/core/database-url.test.d.ts.map +1 -0
  29. package/dist/core/database-url.test.js +22 -0
  30. package/dist/core/database-url.test.js.map +1 -0
  31. package/dist/core/logger.d.ts +3 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +17 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/mcp.d.ts +14 -0
  36. package/dist/core/mcp.d.ts.map +1 -0
  37. package/dist/core/mcp.js +23 -0
  38. package/dist/core/mcp.js.map +1 -0
  39. package/dist/core/query-safety.d.ts +10 -0
  40. package/dist/core/query-safety.d.ts.map +1 -0
  41. package/dist/core/query-safety.js +50 -0
  42. package/dist/core/query-safety.js.map +1 -0
  43. package/dist/core/query-safety.test.d.ts +2 -0
  44. package/dist/core/query-safety.test.d.ts.map +1 -0
  45. package/dist/core/query-safety.test.js +30 -0
  46. package/dist/core/query-safety.test.js.map +1 -0
  47. package/dist/core/schema-types.d.ts +53 -0
  48. package/dist/core/schema-types.d.ts.map +1 -0
  49. package/dist/core/schema-types.js +2 -0
  50. package/dist/core/schema-types.js.map +1 -0
  51. package/dist/core/security/access-control.d.ts +32 -0
  52. package/dist/core/security/access-control.d.ts.map +1 -0
  53. package/dist/core/security/access-control.js +244 -0
  54. package/dist/core/security/access-control.js.map +1 -0
  55. package/dist/core/security/config-loader.d.ts +20 -0
  56. package/dist/core/security/config-loader.d.ts.map +1 -0
  57. package/dist/core/security/config-loader.js +227 -0
  58. package/dist/core/security/config-loader.js.map +1 -0
  59. package/dist/core/security/types.d.ts +64 -0
  60. package/dist/core/security/types.d.ts.map +1 -0
  61. package/dist/core/security/types.js +28 -0
  62. package/dist/core/security/types.js.map +1 -0
  63. package/dist/core/sql-parser.d.ts +23 -0
  64. package/dist/core/sql-parser.d.ts.map +1 -0
  65. package/dist/core/sql-parser.js +460 -0
  66. package/dist/core/sql-parser.js.map +1 -0
  67. package/dist/core/sql-parser.test.d.ts +2 -0
  68. package/dist/core/sql-parser.test.d.ts.map +1 -0
  69. package/dist/core/sql-parser.test.js +21 -0
  70. package/dist/core/sql-parser.test.js.map +1 -0
  71. package/dist/handlers/accessible-schema.d.ts +53 -0
  72. package/dist/handlers/accessible-schema.d.ts.map +1 -0
  73. package/dist/handlers/accessible-schema.js +192 -0
  74. package/dist/handlers/accessible-schema.js.map +1 -0
  75. package/dist/handlers/data.d.ts +17 -0
  76. package/dist/handlers/data.d.ts.map +1 -0
  77. package/dist/handlers/data.js +30 -0
  78. package/dist/handlers/data.js.map +1 -0
  79. package/dist/handlers/relationships.d.ts +14 -0
  80. package/dist/handlers/relationships.d.ts.map +1 -0
  81. package/dist/handlers/relationships.js +77 -0
  82. package/dist/handlers/relationships.js.map +1 -0
  83. package/dist/handlers/schema.d.ts +14 -0
  84. package/dist/handlers/schema.d.ts.map +1 -0
  85. package/dist/handlers/schema.js +104 -0
  86. package/dist/handlers/schema.js.map +1 -0
  87. package/dist/handlers/search.d.ts +14 -0
  88. package/dist/handlers/search.d.ts.map +1 -0
  89. package/dist/handlers/search.js +18 -0
  90. package/dist/handlers/search.js.map +1 -0
  91. package/dist/handlers/validation.d.ts +32 -0
  92. package/dist/handlers/validation.d.ts.map +1 -0
  93. package/dist/handlers/validation.js +116 -0
  94. package/dist/handlers/validation.js.map +1 -0
  95. package/dist/index.d.ts +3 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +294 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/mysql/connection.d.ts +15 -0
  100. package/dist/mysql/connection.d.ts.map +1 -0
  101. package/dist/mysql/connection.js +57 -0
  102. package/dist/mysql/connection.js.map +1 -0
  103. package/dist/mysql/identifiers.d.ts +3 -0
  104. package/dist/mysql/identifiers.d.ts.map +1 -0
  105. package/dist/mysql/identifiers.js +10 -0
  106. package/dist/mysql/identifiers.js.map +1 -0
  107. package/dist/mysql/identifiers.test.d.ts +2 -0
  108. package/dist/mysql/identifiers.test.d.ts.map +1 -0
  109. package/dist/mysql/identifiers.test.js +11 -0
  110. package/dist/mysql/identifiers.test.js.map +1 -0
  111. package/dist/mysql/queries.d.ts +49 -0
  112. package/dist/mysql/queries.d.ts.map +1 -0
  113. package/dist/mysql/queries.js +206 -0
  114. package/dist/mysql/queries.js.map +1 -0
  115. package/docs/clients/claude-code.md +28 -0
  116. package/docs/clients/claude-desktop.md +35 -0
  117. package/docs/clients/codex.md +28 -0
  118. package/docs/clients/cursor.md +26 -0
  119. package/docs/clients/opencode.md +35 -0
  120. package/docs/clients/vscode.md +25 -0
  121. package/package.json +49 -0
  122. package/query-access.example.json +21 -0
  123. package/src/cli.ts +221 -0
  124. package/src/core/cache.ts +41 -0
  125. package/src/core/config.ts +97 -0
  126. package/src/core/database-url.test.ts +28 -0
  127. package/src/core/database-url.ts +47 -0
  128. package/src/core/logger.ts +24 -0
  129. package/src/core/mcp.ts +23 -0
  130. package/src/core/query-safety.test.ts +36 -0
  131. package/src/core/query-safety.ts +63 -0
  132. package/src/core/schema-types.ts +58 -0
  133. package/src/core/security/access-control.ts +321 -0
  134. package/src/core/security/config-loader.ts +315 -0
  135. package/src/core/security/types.ts +114 -0
  136. package/src/core/sql-parser.test.ts +37 -0
  137. package/src/core/sql-parser.ts +572 -0
  138. package/src/handlers/accessible-schema.ts +314 -0
  139. package/src/handlers/data.ts +66 -0
  140. package/src/handlers/relationships.ts +114 -0
  141. package/src/handlers/schema.ts +154 -0
  142. package/src/handlers/search.ts +34 -0
  143. package/src/handlers/validation.ts +165 -0
  144. package/src/index.ts +337 -0
  145. package/src/mysql/connection.ts +68 -0
  146. package/src/mysql/identifiers.test.ts +12 -0
  147. package/src/mysql/identifiers.ts +10 -0
  148. package/src/mysql/queries.ts +285 -0
  149. package/tsconfig.json +24 -0
package/.env.example ADDED
@@ -0,0 +1,35 @@
1
+ # MySQL connection. Prefer DATABASE_URL for the easiest setup.
2
+ DATABASE_URL=mysql://mcp_reader:change_me@localhost:3306/app_db
3
+
4
+ # Or configure individual fields instead. These override DATABASE_URL when set.
5
+ DB_HOST=localhost
6
+ DB_PORT=3306
7
+ DB_NAME=app_db
8
+ DB_USER=mcp_reader
9
+ DB_PASSWORD=change_me
10
+
11
+ # Optional: set to true for managed MySQL providers that require TLS.
12
+ DB_SSL=false
13
+
14
+ # MCP server metadata
15
+ MCP_SERVER_NAME=mysql-mcp-server
16
+ MCP_SERVER_VERSION=1.0.0
17
+
18
+ # Safer first-run default: expose schema/introspection tools only.
19
+ # Set to false to enable execute_query.
20
+ SCHEMA_ONLY_MODE=true
21
+
22
+ # Read query safety
23
+ MAX_QUERY_ROWS=100
24
+ QUERY_TIMEOUT_MS=30000
25
+
26
+ # Optional access-control config path. Required before execute_query can run.
27
+ # QUERY_ACCESS_CONFIG=/absolute/path/to/query-access.json
28
+
29
+ # Cache
30
+ CACHE_TTL=3600
31
+ CACHE_ENABLED=true
32
+
33
+ # Logging
34
+ LOG_LEVEL=info
35
+ LOG_FILE=mcp-server.log
@@ -0,0 +1,18 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ build-test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-node@v4
14
+ with:
15
+ node-version: 22
16
+ - run: npm ci
17
+ - run: npm run build
18
+ - run: npm test
@@ -0,0 +1,24 @@
1
+ # Authentication
2
+
3
+ This server uses standard MySQL username/password authentication through `mysql2`.
4
+
5
+ Required environment variables:
6
+
7
+ ```dotenv
8
+ DB_HOST=localhost
9
+ DB_PORT=3306
10
+ DB_NAME=app_db
11
+ DB_USER=mcp_reader
12
+ DB_PASSWORD=change_me
13
+ DB_SSL=false
14
+ ```
15
+
16
+ For managed MySQL providers that require TLS, set `DB_SSL=true`. If your provider requires custom certificate configuration, extend `src/mysql/connection.ts` to load the provider CA certificate and pass it in the mysql2 `ssl` option.
17
+
18
+ Recommended least-privilege grants:
19
+
20
+ ```sql
21
+ CREATE USER 'mcp_reader'@'%' IDENTIFIED BY 'change_me';
22
+ GRANT SELECT, SHOW VIEW ON app_db.* TO 'mcp_reader'@'%';
23
+ FLUSH PRIVILEGES;
24
+ ```
package/CLAUDE.md ADDED
@@ -0,0 +1,37 @@
1
+ # CLAUDE.md
2
+
3
+ This repository is `mysql-mcp-server`, a MySQL-specific MCP server for schema introspection and guarded read-only queries.
4
+
5
+ ## Architecture
6
+
7
+ - `src/index.ts` registers MCP tools and starts the stdio server.
8
+ - `src/core/*` contains reusable code intended for future extraction:
9
+ - config, logging, cache, MCP response helpers
10
+ - schema metadata types
11
+ - read-only query safety and `LIMIT` enforcement
12
+ - SQL parsing and access-control validation
13
+ - `src/mysql/*` contains MySQL-specific code:
14
+ - `mysql2` connection pool
15
+ - identifier helpers
16
+ - `information_schema` catalog queries
17
+ - `src/handlers/*` adapts MCP tool input/output to the MySQL implementation.
18
+
19
+ ## Design Notes
20
+
21
+ - One MCP server instance connects to one configured MySQL database (`DB_NAME`).
22
+ - Tool `database` and `schema` inputs are compatibility fields and must match `DB_NAME` when provided.
23
+ - MySQL catalog metadata comes from `information_schema`.
24
+ - `execute_query` accepts read-only `SELECT`/`WITH` queries, validates access control, and enforces `LIMIT`.
25
+ - Routine/function definition tools are intentionally deferred for v1.
26
+
27
+ ## Development Commands
28
+
29
+ ```bash
30
+ npm run build
31
+ npm test
32
+ npm start
33
+ ```
34
+
35
+ ## Example Domain
36
+
37
+ Use neutral examples such as `app_db`, `customers`, `orders`, `products`, and `order_items`. Do not add company-specific databases, internal hostnames, personal usernames, or private schema names to public docs or tests.
@@ -0,0 +1,19 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve `mysql-mcp-server`.
4
+
5
+ ## Development
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ npm test
11
+ ```
12
+
13
+ Keep MySQL-specific catalog behavior in `src/mysql/*`. Shared behavior that could apply to future database-specific MCP servers belongs in `src/core/*`.
14
+
15
+ ## Pull Requests
16
+
17
+ - Keep examples generic and public-safe.
18
+ - Add tests for query-safety, parser, access-control, or MySQL catalog changes where practical.
19
+ - Do not commit real credentials, hostnames, customer data, private database names, or personal usernames.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,65 @@
1
+ # Query Access Setup
2
+
3
+ `execute_query` is disabled until `QUERY_ACCESS_CONFIG` points to a JSON access-control file. This is intentionally restrictive so schema introspection can be enabled separately from data reads.
4
+
5
+ ## Example Policy
6
+
7
+ ```json
8
+ {
9
+ "requireExplicitColumns": true,
10
+ "databases": {
11
+ "app_db": {
12
+ "tables": {
13
+ "mode": "whitelist",
14
+ "list": ["customers", "orders", "products", "order_items"],
15
+ "columnAccess": {
16
+ "customers": {
17
+ "mode": "exclusion",
18
+ "columns": ["password_hash", "api_token"]
19
+ },
20
+ "orders": {
21
+ "mode": "inclusion",
22
+ "columns": ["id", "customer_id", "status", "total", "created_at"]
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ ## Modes
32
+
33
+ - `whitelist`: only listed tables can be queried.
34
+ - `blacklist`: listed tables are blocked.
35
+ - `none`: table-level access is unrestricted, while column rules may still apply.
36
+ - `columnAccess.mode = inclusion`: only listed columns can be selected.
37
+ - `columnAccess.mode = exclusion`: listed columns are blocked.
38
+
39
+ When `requireExplicitColumns` is `true`, `SELECT *` and `table.*` are rejected. Prefer queries such as:
40
+
41
+ ```sql
42
+ SELECT id, email, created_at FROM customers ORDER BY id LIMIT 20;
43
+ ```
44
+
45
+ ## MCP Env Example
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "mysql-mcp-server": {
51
+ "command": "node",
52
+ "args": ["/absolute/path/to/mysql-mcp-server/dist/index.js"],
53
+ "env": {
54
+ "DB_HOST": "localhost",
55
+ "DB_PORT": "3306",
56
+ "DB_NAME": "app_db",
57
+ "DB_USER": "mcp_reader",
58
+ "DB_PASSWORD": "change_me",
59
+ "SCHEMA_ONLY_MODE": "false",
60
+ "QUERY_ACCESS_CONFIG": "/absolute/path/to/query-access.json"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # MySQL MCP Server
2
+
3
+ An open-source Model Context Protocol (MCP) server for MySQL schema introspection and guarded read-only queries. It helps MCP clients discover tables, columns, indexes, relationships, and safe queryable data from one configured MySQL database.
4
+
5
+ ## Features
6
+
7
+ - Schema tools for tables, views, columns, primary keys, foreign keys, indexes, and approximate table statistics
8
+ - Table and column search with simple `*` and `?` wildcards
9
+ - Relationship discovery for join-path exploration
10
+ - Optional read-only `execute_query` tool with SELECT-only validation, access control, and automatic `LIMIT`
11
+ - Internal `src/core` boundary for code that can later be shared with other database-specific MCP servers
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 18+
16
+ - MySQL 8.x or a compatible MySQL service
17
+ - A MySQL user with read access to the configured database
18
+
19
+ ## Setup
20
+
21
+ Fast path for users installing from npm:
22
+
23
+ ```bash
24
+ npx -y @sigma4life/mysql-mcp-server init
25
+ ```
26
+
27
+ Then edit `.env` and check the connection:
28
+
29
+ ```bash
30
+ npx -y @sigma4life/mysql-mcp-server doctor
31
+ ```
32
+
33
+ Local development from this repository:
34
+
35
+ ```bash
36
+ npm install
37
+ cp .env.example .env
38
+ npm run build
39
+ npm start
40
+ ```
41
+
42
+ Configure `.env`:
43
+
44
+ ```dotenv
45
+ DATABASE_URL=mysql://mcp_reader:change_me@localhost:3306/app_db
46
+ SCHEMA_ONLY_MODE=true
47
+ ```
48
+
49
+ You can also use individual `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`, and `DB_SSL` variables. Individual variables override `DATABASE_URL`.
50
+
51
+ This server connects to one configured database per process. Tool inputs may include `database` for compatibility, but it must match the configured database.
52
+
53
+ ## Least-Privilege MySQL User
54
+
55
+ Schema-only usage needs metadata visibility and table access. Read-query usage also needs `SELECT` on allowed tables.
56
+
57
+ ```sql
58
+ CREATE USER 'mcp_reader'@'%' IDENTIFIED BY 'change_me';
59
+ GRANT SELECT, SHOW VIEW ON app_db.* TO 'mcp_reader'@'%';
60
+ FLUSH PRIVILEGES;
61
+ ```
62
+
63
+ Use a stronger host restriction and password in production.
64
+
65
+ ## MCP Client Example
66
+
67
+ Client-specific docs:
68
+
69
+ - [Claude Code](docs/clients/claude-code.md)
70
+ - [Claude Desktop](docs/clients/claude-desktop.md)
71
+ - [Codex](docs/clients/codex.md)
72
+ - [OpenCode](docs/clients/opencode.md)
73
+ - [Cursor](docs/clients/cursor.md)
74
+ - [VS Code](docs/clients/vscode.md)
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "mysql": {
80
+ "command": "npx",
81
+ "args": ["-y", "@sigma4life/mysql-mcp-server"],
82
+ "env": {
83
+ "DATABASE_URL": "mysql://mcp_reader:change_me@localhost:3306/app_db",
84
+ "SCHEMA_ONLY_MODE": "true"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ To enable `execute_query`, set `SCHEMA_ONLY_MODE=false` and provide `QUERY_ACCESS_CONFIG`.
92
+
93
+ ## Access Control
94
+
95
+ `execute_query` is blocked unless `QUERY_ACCESS_CONFIG` points to a JSON policy file. Example:
96
+
97
+ ```json
98
+ {
99
+ "requireExplicitColumns": true,
100
+ "databases": {
101
+ "app_db": {
102
+ "tables": {
103
+ "mode": "whitelist",
104
+ "list": ["customers", "orders", "products", "order_items"],
105
+ "columnAccess": {
106
+ "customers": {
107
+ "mode": "exclusion",
108
+ "columns": ["password_hash", "api_token"]
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ ## Tools
118
+
119
+ - `get_schema`
120
+ - `get_table_info`
121
+ - `find_tables`
122
+ - `search_objects`
123
+ - `get_relationships`
124
+ - `validate_objects`
125
+ - `get_accessible_schema`
126
+ - `get_accessible_table_info`
127
+ - `execute_query` when schema-only mode is disabled
128
+
129
+ Example prompts:
130
+
131
+ - "Show me the schema for the customers and orders tables."
132
+ - "Find tables with a column matching `*email*`."
133
+ - "Show relationships from orders to customers."
134
+ - "Run `SELECT id, email FROM customers ORDER BY id LIMIT 20`."
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ npm run build
140
+ npm test
141
+ npm run lint
142
+ ```
143
+
144
+ The first implementation is MySQL-specific. Shared logic lives under `src/core` so future database-specific repos can extract or reuse it without carrying MySQL catalog code.
package/SECURITY.md ADDED
@@ -0,0 +1,10 @@
1
+ # Security Policy
2
+
3
+ Please report security issues privately through the repository owner's preferred security contact once the GitHub repository is created. Do not open public issues for vulnerabilities.
4
+
5
+ ## Notes
6
+
7
+ - `execute_query` is blocked unless `QUERY_ACCESS_CONFIG` is set.
8
+ - Use least-privilege MySQL users.
9
+ - Prefer `SCHEMA_ONLY_MODE=true` when an MCP client only needs metadata.
10
+ - Never commit real credentials or production access-control policies.
package/TESTING.md ADDED
@@ -0,0 +1,54 @@
1
+ # Testing
2
+
3
+ Run local checks:
4
+
5
+ ```bash
6
+ npm install
7
+ npm run build
8
+ npm test
9
+ ```
10
+
11
+ ## Optional MySQL Integration Test
12
+
13
+ Start a local MySQL instance:
14
+
15
+ ```bash
16
+ docker run --name mysql-mcp-test \
17
+ -e MYSQL_ROOT_PASSWORD=root_password \
18
+ -e MYSQL_DATABASE=app_db \
19
+ -e MYSQL_USER=mcp_reader \
20
+ -e MYSQL_PASSWORD=change_me \
21
+ -p 3306:3306 \
22
+ -d mysql:8
23
+ ```
24
+
25
+ Create sample tables:
26
+
27
+ ```sql
28
+ CREATE TABLE customers (
29
+ id INT AUTO_INCREMENT PRIMARY KEY,
30
+ email VARCHAR(255) NOT NULL,
31
+ name VARCHAR(255),
32
+ password_hash VARCHAR(255),
33
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
34
+ );
35
+
36
+ CREATE TABLE orders (
37
+ id INT AUTO_INCREMENT PRIMARY KEY,
38
+ customer_id INT NOT NULL,
39
+ status VARCHAR(32) NOT NULL,
40
+ total DECIMAL(10, 2) NOT NULL,
41
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
42
+ CONSTRAINT fk_orders_customer
43
+ FOREIGN KEY (customer_id) REFERENCES customers(id)
44
+ );
45
+ ```
46
+
47
+ Configure `.env` with `DB_NAME=app_db`, then run:
48
+
49
+ ```bash
50
+ npm run build
51
+ npm start
52
+ ```
53
+
54
+ Use an MCP client to call `get_schema`, `find_tables`, `get_relationships`, and, when access control is configured, `execute_query`.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { maskDatabaseUrl } from './core/database-url.js';
5
+ const DEFAULT_DATABASE_URL = 'mysql://mcp_reader:change_me@localhost:3306/app_db';
6
+ function usage() {
7
+ console.log(`mysql-mcp-server
8
+
9
+ Usage:
10
+ mysql-mcp-server Start the MCP stdio server
11
+ mysql-mcp-server serve Start the MCP stdio server
12
+ mysql-mcp-server doctor Check configuration and database connectivity
13
+ mysql-mcp-server init [--force] Create starter .env, query-access.json, and client snippets
14
+
15
+ Environment:
16
+ DATABASE_URL=mysql://user:password@host:3306/database
17
+ `);
18
+ }
19
+ function writeFileIfMissing(filePath, content, force) {
20
+ if (fs.existsSync(filePath) && !force) {
21
+ return 'skipped';
22
+ }
23
+ fs.writeFileSync(filePath, content);
24
+ return 'created';
25
+ }
26
+ function envTemplate() {
27
+ return `# One-line setup. Individual DB_* variables may override parts of this URL.
28
+ DATABASE_URL=${DEFAULT_DATABASE_URL}
29
+
30
+ # Safer first-run default: schema tools only. Set false to enable execute_query.
31
+ SCHEMA_ONLY_MODE=true
32
+
33
+ # Required only when SCHEMA_ONLY_MODE=false.
34
+ # QUERY_ACCESS_CONFIG=${path.resolve('query-access.json')}
35
+
36
+ MAX_QUERY_ROWS=100
37
+ QUERY_TIMEOUT_MS=30000
38
+ MCP_SERVER_NAME=mysql-mcp-server
39
+ MCP_SERVER_VERSION=1.0.0
40
+ LOG_LEVEL=info
41
+ LOG_FILE=mcp-server.log
42
+ `;
43
+ }
44
+ function queryAccessTemplate() {
45
+ return JSON.stringify({
46
+ requireExplicitColumns: true,
47
+ databases: {
48
+ app_db: {
49
+ tables: {
50
+ mode: 'whitelist',
51
+ list: ['customers', 'orders', 'products', 'order_items'],
52
+ columnAccess: {
53
+ customers: {
54
+ mode: 'exclusion',
55
+ columns: ['password_hash', 'api_token'],
56
+ },
57
+ },
58
+ },
59
+ },
60
+ },
61
+ }, null, 2) + '\n';
62
+ }
63
+ function clientSnippetsTemplate() {
64
+ return `# mysql-mcp-server client snippets
65
+
66
+ Replace the sample DATABASE_URL with your real MySQL connection string.
67
+
68
+ ## Claude Code
69
+
70
+ \`\`\`bash
71
+ claude mcp add --transport stdio mysql \\
72
+ --env DATABASE_URL=${DEFAULT_DATABASE_URL} \\
73
+ -- npx -y @sigma4life/mysql-mcp-server
74
+ \`\`\`
75
+
76
+ ## Codex
77
+
78
+ \`\`\`bash
79
+ codex mcp add mysql \\
80
+ --env DATABASE_URL=${DEFAULT_DATABASE_URL} \\
81
+ -- npx -y @sigma4life/mysql-mcp-server
82
+ \`\`\`
83
+
84
+ ## Claude Desktop
85
+
86
+ \`\`\`json
87
+ {
88
+ "mcpServers": {
89
+ "mysql": {
90
+ "command": "npx",
91
+ "args": ["-y", "@sigma4life/mysql-mcp-server"],
92
+ "env": {
93
+ "DATABASE_URL": "${DEFAULT_DATABASE_URL}"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ \`\`\`
99
+
100
+ ## OpenCode
101
+
102
+ \`\`\`json
103
+ {
104
+ "$schema": "https://opencode.ai/config.json",
105
+ "mcp": {
106
+ "mysql": {
107
+ "type": "local",
108
+ "command": ["npx", "-y", "@sigma4life/mysql-mcp-server"],
109
+ "enabled": true,
110
+ "environment": {
111
+ "DATABASE_URL": "${DEFAULT_DATABASE_URL}"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ \`\`\`
117
+ `;
118
+ }
119
+ async function runInit(args) {
120
+ const force = args.includes('--force');
121
+ const files = [
122
+ ['.env', envTemplate()],
123
+ ['query-access.json', queryAccessTemplate()],
124
+ ['mysql-mcp-clients.md', clientSnippetsTemplate()],
125
+ ];
126
+ for (const [file, content] of files) {
127
+ const status = writeFileIfMissing(path.resolve(file), content, force);
128
+ console.log(`${status === 'created' ? 'created' : 'skipped'} ${file}`);
129
+ }
130
+ console.log('\nNext steps:');
131
+ console.log('1. Edit .env with your MySQL credentials.');
132
+ console.log('2. Run: mysql-mcp-server doctor');
133
+ console.log('3. Add one of the snippets from mysql-mcp-clients.md to your MCP client.');
134
+ }
135
+ async function runDoctor() {
136
+ console.log('mysql-mcp-server doctor\n');
137
+ try {
138
+ const { appConfig } = await import('./core/config.js');
139
+ const { db } = await import('./mysql/connection.js');
140
+ console.log(`server: ${appConfig.server.name} v${appConfig.server.version}`);
141
+ console.log(`database: ${appConfig.db.user}@${appConfig.db.host}:${appConfig.db.port}/${appConfig.db.name}`);
142
+ console.log(`ssl: ${appConfig.db.ssl ? 'enabled' : 'disabled'}`);
143
+ console.log(`schema-only mode: ${appConfig.server.schemaOnlyMode ? 'enabled' : 'disabled'}`);
144
+ if (process.env.DATABASE_URL) {
145
+ console.log(`DATABASE_URL: ${maskDatabaseUrl(process.env.DATABASE_URL)}`);
146
+ }
147
+ const versionRows = await db.query('SELECT VERSION() AS version');
148
+ console.log(`mysql: ${versionRows[0]?.version || 'connected'}`);
149
+ const tableRows = await db.query(`SELECT COUNT(*) AS tableCount
150
+ FROM information_schema.TABLES
151
+ WHERE TABLE_SCHEMA = :database
152
+ AND TABLE_TYPE IN ('BASE TABLE', 'VIEW')`, { database: appConfig.db.name });
153
+ console.log(`tables/views visible: ${tableRows[0]?.tableCount ?? 0}`);
154
+ if (!appConfig.server.schemaOnlyMode) {
155
+ if (process.env.QUERY_ACCESS_CONFIG) {
156
+ const { loadAccessControlConfig } = await import('./core/security/access-control.js');
157
+ loadAccessControlConfig();
158
+ console.log('query access config: ok');
159
+ }
160
+ else {
161
+ console.log('query access config: missing; execute_query will be blocked');
162
+ }
163
+ }
164
+ await db.close();
165
+ console.log('\nDoctor passed.');
166
+ }
167
+ catch (error) {
168
+ console.error('Doctor failed.');
169
+ console.error(error instanceof Error ? error.message : String(error));
170
+ process.exitCode = 1;
171
+ }
172
+ }
173
+ async function main() {
174
+ const [command = 'serve', ...args] = process.argv.slice(2);
175
+ switch (command) {
176
+ case 'serve':
177
+ await import('./index.js');
178
+ break;
179
+ case 'doctor':
180
+ await runDoctor();
181
+ break;
182
+ case 'init':
183
+ await runInit(args);
184
+ break;
185
+ case '--help':
186
+ case '-h':
187
+ case 'help':
188
+ usage();
189
+ break;
190
+ default:
191
+ console.error(`Unknown command: ${command}\n`);
192
+ usage();
193
+ process.exitCode = 1;
194
+ }
195
+ }
196
+ main();
197
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ declare class SchemaCache {
2
+ private cache;
3
+ private ttl;
4
+ private enabled;
5
+ get<T>(key: string): T | null;
6
+ set<T>(key: string, data: T): void;
7
+ clear(): void;
8
+ }
9
+ export declare const cache: SchemaCache;
10
+ export {};
11
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"AAKA,cAAM,WAAW;IACf,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,GAAG,CAA+D;IAC1E,OAAO,CAAC,OAAO,CAAyC;IAExD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAkB7B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI;IAOlC,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,KAAK,aAAoB,CAAC"}