@arvoretech/mysql-mcp 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.
package/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # MySQL MCP Server
2
+
3
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=mysql-mcp&registry=https://npm.pkg.github.com&packageName=@arvoretech/mysql-mcp)
4
+
5
+ A Model Context Protocol (MCP) server implementation for MySQL that enables read-only database operations through a standardized protocol for integration with LLMs and AI tools.
6
+
7
+ ## Features
8
+
9
+ - ✅ **Read-only**: Executes only SELECT, SHOW, DESCRIBE and EXPLAIN queries
10
+ - 🔒 **Secure**: Validates queries to prevent write operations
11
+ - 🚀 **Fast**: Direct MySQL connection using mysql2
12
+ - 📡 **MCP Protocol**: Communication via stdio transport
13
+ - 🛠️ **TypeScript**: Fully typed with Zod validation
14
+ - 🌍 **Environment Configuration**: Easy setup via environment variables
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install -g @arvoretech/mysql-mcp --registry=https://npm.pkg.github.com
20
+ ```
21
+
22
+ Or configure your `.npmrc`:
23
+
24
+ ```bash
25
+ echo "@arvoretech:registry=https://npm.pkg.github.com" >> ~/.npmrc
26
+ npm install -g @arvoretech/mysql-mcp
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ The server is configured via environment variables:
32
+
33
+ ```bash
34
+ export MYSQL_HOST=localhost
35
+ export MYSQL_PORT=3306
36
+ export MYSQL_USER=root
37
+ export MYSQL_PASSWORD=secret
38
+ export MYSQL_DATABASE=mydb
39
+ export MYSQL_SSL=false
40
+ export MYSQL_CONNECTION_TIMEOUT=30000
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```bash
46
+ # Development
47
+ pnpm dev
48
+
49
+ # Production
50
+ node dist/index.js
51
+ ```
52
+
53
+ ## Available MCP Tools
54
+
55
+ ### `read_query`
56
+
57
+ Executes SELECT queries on the MySQL database.
58
+
59
+ **Parameters:**
60
+
61
+ - `query` (string): SQL SELECT statement
62
+
63
+ **Example:**
64
+
65
+ ```json
66
+ {
67
+ "query": "SELECT * FROM users LIMIT 10"
68
+ }
69
+ ```
70
+
71
+ ### `list_tables`
72
+
73
+ Lists all tables in the current database.
74
+
75
+ **Parameters:** None
76
+
77
+ ### `describe_table`
78
+
79
+ Gets structure and schema information for a specific table.
80
+
81
+ **Parameters:**
82
+
83
+ - `tableName` (string): Name of the table
84
+
85
+ ### `show_databases`
86
+
87
+ Lists all available databases on the MySQL server.
88
+
89
+ **Parameters:** None
90
+
91
+ ## Programmatic Usage
92
+
93
+ ```typescript
94
+ import { MySQLMCPServer } from "mysql-mcp-server";
95
+
96
+ const server = new MySQLMCPServer({
97
+ host: "localhost",
98
+ port: 3306,
99
+ user: "root",
100
+ password: "secret",
101
+ database: "mydb",
102
+ ssl: false,
103
+ connectionTimeout: 30000,
104
+ });
105
+
106
+ // Setup graceful shutdown
107
+ server.setupGracefulShutdown();
108
+
109
+ // Start server
110
+ await server.start();
111
+ ```
112
+
113
+ ## Claude Desktop Integration
114
+
115
+ Add to your Claude Desktop configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "mysql": {
121
+ "command": "npx",
122
+ "args": ["-y", "@arvoretech/mysql-mcp"],
123
+ "env": {
124
+ "MYSQL_HOST": "localhost",
125
+ "MYSQL_PORT": "3306",
126
+ "MYSQL_USER": "root",
127
+ "MYSQL_PASSWORD": "secret",
128
+ "MYSQL_DATABASE": "mydb",
129
+ "MYSQL_SSL": "false",
130
+ "MYSQL_CONNECTION_TIMEOUT": "30000"
131
+ }
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ ## Security
138
+
139
+ - **Query validation**: Only read operations are allowed
140
+ - **Parameter escaping**: Uses prepared statements when possible
141
+ - **Connection timeout**: Prevents hanging connections
142
+ - **Error handling**: MySQL errors are caught and handled appropriately
143
+
144
+ ## Development
145
+
146
+ ```bash
147
+ # Install dependencies
148
+ pnpm install
149
+
150
+ # Run in development mode
151
+ pnpm dev
152
+
153
+ # Build
154
+ pnpm build
155
+
156
+ # Linting
157
+ pnpm lint
158
+ pnpm lint:fix
159
+ ```
160
+
161
+ ## Architecture
162
+
163
+ - **`MySQLConnection`**: Manages MySQL connection
164
+ - **`MySQLMCPTools`**: Implements MCP tools
165
+ - **`MySQLMCPServer`**: Main MCP server with stdio transport
166
+ - **Validation**: Uses Zod for type and parameter validation
167
+
168
+ ## Connection Management
169
+
170
+ The server uses a **connection-per-query** approach for optimal reliability:
171
+
172
+ - **Fresh connections**: Each query opens and closes its own connection
173
+ - **No persistent connections**: Prevents timeout and stale connection issues
174
+ - **Automatic cleanup**: Connections are always properly closed after each operation
175
+ - **Improved stability**: Eliminates bugs caused by long-running connections
176
+
177
+ ## Limitations
178
+
179
+ - Read-only operations only (SELECT, SHOW, DESCRIBE, EXPLAIN)
180
+ - Connection-per-query (no connection pooling)
181
+ - MySQL only (not PostgreSQL, SQLite, etc.)
182
+ - Stdio transport only (no HTTP/WebSocket)
@@ -0,0 +1,15 @@
1
+ import { MySQLConfigInput, QueryResult, TableInfo, ColumnInfo, DatabaseInfo } from "./types.js";
2
+ export declare class MySQLConnection {
3
+ private readonly config;
4
+ constructor(config: MySQLConfigInput);
5
+ private createConnection;
6
+ connect(): Promise<void>;
7
+ disconnect(): Promise<void>;
8
+ private validateReadOnlyQuery;
9
+ executeQuery(query: string): Promise<QueryResult>;
10
+ listTables(): Promise<TableInfo[]>;
11
+ describeTable(tableName: string): Promise<ColumnInfo[]>;
12
+ showDatabases(): Promise<DatabaseInfo[]>;
13
+ testConnection(): Promise<boolean>;
14
+ }
15
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,SAAS,EACT,UAAU,EACV,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,gBAAgB;YAItB,gBAAgB;IA0BxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,OAAO,CAAC,qBAAqB;IAsBvB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAkCjD,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAclC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAmBvD,aAAa,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAKxC,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;CAczC"}
@@ -0,0 +1,125 @@
1
+ import mysql from "mysql2/promise";
2
+ import { MySQLConfigSchema, MySQLMCPError, } from "./types.js";
3
+ export class MySQLConnection {
4
+ config;
5
+ constructor(config) {
6
+ this.config = MySQLConfigSchema.parse(config);
7
+ }
8
+ async createConnection() {
9
+ try {
10
+ const connectionOptions = {
11
+ host: this.config.host,
12
+ port: this.config.port,
13
+ user: this.config.user,
14
+ password: this.config.password,
15
+ database: this.config.database,
16
+ connectTimeout: this.config.connectionTimeout,
17
+ };
18
+ if (this.config.ssl) {
19
+ connectionOptions.ssl = {};
20
+ }
21
+ return await mysql.createConnection(connectionOptions);
22
+ }
23
+ catch (error) {
24
+ throw new MySQLMCPError(`Failed to connect to MySQL: ${error instanceof Error ? error.message : "Unknown error"}`, "CONNECTION_ERROR");
25
+ }
26
+ }
27
+ async connect() {
28
+ const connection = await this.createConnection();
29
+ await connection.end();
30
+ }
31
+ async disconnect() {
32
+ // No-op: connections are managed per-query
33
+ }
34
+ validateReadOnlyQuery(query) {
35
+ const trimmedQuery = query.trim().toLowerCase();
36
+ const readOnlyPatterns = [
37
+ /^select\s/,
38
+ /^show\s/,
39
+ /^describe\s/,
40
+ /^desc\s/,
41
+ /^explain\s/,
42
+ ];
43
+ const isReadOnly = readOnlyPatterns.some((pattern) => pattern.test(trimmedQuery));
44
+ if (!isReadOnly) {
45
+ throw new MySQLMCPError("Only read-only queries are allowed (SELECT, SHOW, DESCRIBE, EXPLAIN)", "WRITE_OPERATION_NOT_ALLOWED");
46
+ }
47
+ }
48
+ async executeQuery(query) {
49
+ this.validateReadOnlyQuery(query);
50
+ const startTime = Date.now();
51
+ let connection = null;
52
+ try {
53
+ connection = await this.createConnection();
54
+ const [rows] = await connection.execute(query);
55
+ const executionTime = Date.now() - startTime;
56
+ const data = Array.isArray(rows)
57
+ ? rows
58
+ : [];
59
+ return {
60
+ data,
61
+ rowCount: data.length,
62
+ executionTime,
63
+ };
64
+ }
65
+ catch (error) {
66
+ const mysqlError = error;
67
+ throw new MySQLMCPError(`Query execution failed: ${mysqlError.message}`, mysqlError.code, mysqlError.sqlState);
68
+ }
69
+ finally {
70
+ if (connection) {
71
+ await connection.end();
72
+ }
73
+ }
74
+ }
75
+ async listTables() {
76
+ const result = await this.executeQuery(`
77
+ SELECT
78
+ TABLE_NAME,
79
+ TABLE_TYPE,
80
+ TABLE_SCHEMA
81
+ FROM information_schema.TABLES
82
+ WHERE TABLE_SCHEMA = '${this.config.database}'
83
+ ORDER BY TABLE_NAME
84
+ `);
85
+ return result.data;
86
+ }
87
+ async describeTable(tableName) {
88
+ const result = await this.executeQuery(`
89
+ SELECT
90
+ COLUMN_NAME,
91
+ DATA_TYPE,
92
+ IS_NULLABLE,
93
+ COLUMN_DEFAULT,
94
+ COLUMN_KEY,
95
+ EXTRA,
96
+ COLUMN_COMMENT
97
+ FROM information_schema.COLUMNS
98
+ WHERE TABLE_SCHEMA = '${this.config.database}'
99
+ AND TABLE_NAME = '${tableName}'
100
+ ORDER BY ORDINAL_POSITION
101
+ `);
102
+ return result.data;
103
+ }
104
+ async showDatabases() {
105
+ const result = await this.executeQuery("SHOW DATABASES");
106
+ return result.data;
107
+ }
108
+ async testConnection() {
109
+ let connection = null;
110
+ try {
111
+ connection = await this.createConnection();
112
+ await connection.execute("SELECT 1 as test");
113
+ return true;
114
+ }
115
+ catch {
116
+ return false;
117
+ }
118
+ finally {
119
+ if (connection) {
120
+ await connection.end();
121
+ }
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAGL,iBAAiB,EAKjB,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,MAAM,OAAO,eAAe;IACT,MAAM,CAAc;IAErC,YAAY,MAAwB;QAClC,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,IAAI,CAAC;YACH,MAAM,iBAAiB,GAA4B;gBACjD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;gBACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;gBACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;gBACtB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC9B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC9B,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;aAC9C,CAAC;YAEF,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;gBACpB,iBAAiB,CAAC,GAAG,GAAG,EAAE,CAAC;YAC7B,CAAC;YAED,OAAO,MAAM,KAAK,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,aAAa,CACrB,+BACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAC3C,EAAE,EACF,kBAAkB,CACnB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACjD,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,UAAU;QACd,2CAA2C;IAC7C,CAAC;IAEO,qBAAqB,CAAC,KAAa;QACzC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,gBAAgB,GAAG;YACvB,WAAW;YACX,SAAS;YACT,aAAa;YACb,SAAS;YACT,YAAY;SACb,CAAC;QAEF,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CACnD,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAC3B,CAAC;QAEF,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,aAAa,CACrB,sEAAsE,EACtE,6BAA6B,CAC9B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAa;QAC9B,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAElC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,UAAU,GAA4B,IAAI,CAAC;QAE/C,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC/C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAE7C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC9B,CAAC,CAAE,IAAkC;gBACrC,CAAC,CAAC,EAAE,CAAC;YAEP,OAAO;gBACL,IAAI;gBACJ,QAAQ,EAAE,IAAI,CAAC,MAAM;gBACrB,aAAa;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,KAAyB,CAAC;YAC7C,MAAM,IAAI,aAAa,CACrB,2BAA2B,UAAU,CAAC,OAAO,EAAE,EAC/C,UAAU,CAAC,IAAI,EACf,UAAU,CAAC,QAAQ,CACpB,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC;;;;;;8BAMb,IAAI,CAAC,MAAM,CAAC,QAAQ;;KAE7C,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,IAA8B,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC;;;;;;;;;;8BAUb,IAAI,CAAC,MAAM,CAAC,QAAQ;4BACtB,SAAS;;KAEhC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,IAA+B,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC,IAAiC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,UAAU,GAA4B,IAAI,CAAC;QAC/C,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3C,MAAM,UAAU,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ export { MySQLMCPServer } from "./server.js";
3
+ export { MySQLConnection } from "./database.js";
4
+ export { MySQLMCPTools } from "./tools.js";
5
+ export * from "./types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsBA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { MySQLMCPServer } from "./server.js";
3
+ async function main() {
4
+ try {
5
+ const server = MySQLMCPServer.fromEnvironment();
6
+ server.setupGracefulShutdown();
7
+ await server.start();
8
+ }
9
+ catch (error) {
10
+ console.error("Failed to start MySQL MCP Server:", error);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ if (import.meta.url === `file://${process.argv[1]}`) {
15
+ main().catch((error) => {
16
+ console.error("Fatal error:", error);
17
+ process.exit(1);
18
+ });
19
+ }
20
+ export { MySQLMCPServer } from "./server.js";
21
+ export { MySQLConnection } from "./database.js";
22
+ export { MySQLMCPTools } from "./tools.js";
23
+ export * from "./types.js";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;QAChD,MAAM,CAAC,qBAAqB,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,cAAc,YAAY,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { MySQLConfig } from "./types.js";
2
+ export declare class MySQLMCPServer {
3
+ private server;
4
+ private db;
5
+ private tools;
6
+ constructor(config: MySQLConfig);
7
+ static fromEnvironment(): MySQLMCPServer;
8
+ private setupTools;
9
+ start(): Promise<void>;
10
+ cleanup(): Promise<void>;
11
+ setupGracefulShutdown(): void;
12
+ }
13
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EAOZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,KAAK,CAAgB;gBAEjB,MAAM,EAAE,WAAW;IAY/B,MAAM,CAAC,eAAe,IAAI,cAAc;IAiBxC,OAAO,CAAC,UAAU;IAmDZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,qBAAqB,IAAI,IAAI;CAoB9B"}
package/dist/server.js ADDED
@@ -0,0 +1,106 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { MySQLConnection } from "./database.js";
4
+ import { MySQLMCPTools } from "./tools.js";
5
+ import { MySQLConfigSchema, ReadQueryParamsSchema, DescribeTableParamsSchema, MySQLMCPError, } from "./types.js";
6
+ export class MySQLMCPServer {
7
+ server;
8
+ db;
9
+ tools;
10
+ constructor(config) {
11
+ this.server = new McpServer({
12
+ name: "mysql-mcp-server",
13
+ version: "1.0.0",
14
+ });
15
+ this.db = new MySQLConnection(config);
16
+ this.tools = new MySQLMCPTools(this.db);
17
+ this.setupTools();
18
+ }
19
+ static fromEnvironment() {
20
+ const config = MySQLConfigSchema.parse({
21
+ host: process.env.MYSQL_HOST || "localhost",
22
+ port: parseInt(process.env.MYSQL_PORT || "3306", 10),
23
+ user: process.env.MYSQL_USER || "root",
24
+ password: process.env.MYSQL_PASSWORD || "",
25
+ database: process.env.MYSQL_DATABASE || "test",
26
+ ssl: process.env.MYSQL_SSL === "true",
27
+ connectionTimeout: parseInt(process.env.MYSQL_CONNECTION_TIMEOUT || "30000", 10),
28
+ });
29
+ return new MySQLMCPServer(config);
30
+ }
31
+ setupTools() {
32
+ this.server.registerTool("read_query", {
33
+ title: "Execute Read Query",
34
+ description: "Execute a SELECT query on the MySQL database",
35
+ inputSchema: { query: ReadQueryParamsSchema.shape.query },
36
+ }, async (params) => {
37
+ return this.tools.readQuery(params);
38
+ });
39
+ this.server.registerTool("list_tables", {
40
+ title: "List Tables",
41
+ description: "List all tables in the current database",
42
+ inputSchema: {},
43
+ }, async () => {
44
+ return this.tools.listTables();
45
+ });
46
+ this.server.registerTool("describe_table", {
47
+ title: "Describe Table",
48
+ description: "Get the structure and schema information of a specific table",
49
+ inputSchema: { tableName: DescribeTableParamsSchema.shape.tableName },
50
+ }, async (params) => {
51
+ return this.tools.describeTable(params);
52
+ });
53
+ this.server.registerTool("show_databases", {
54
+ title: "Show Databases",
55
+ description: "List all available databases on the MySQL server",
56
+ inputSchema: {},
57
+ }, async () => {
58
+ return this.tools.showDatabases();
59
+ });
60
+ }
61
+ async start() {
62
+ try {
63
+ await this.db.connect();
64
+ const isConnected = await this.db.testConnection();
65
+ if (!isConnected) {
66
+ throw new MySQLMCPError("Database connection test failed", "CONNECTION_TEST_FAILED");
67
+ }
68
+ const transport = new StdioServerTransport();
69
+ await this.server.connect(transport);
70
+ console.error("MySQL MCP Server started successfully");
71
+ }
72
+ catch (error) {
73
+ console.error("Failed to start MySQL MCP Server:", error instanceof Error ? error.message : error);
74
+ await this.cleanup();
75
+ process.exit(1);
76
+ }
77
+ }
78
+ async cleanup() {
79
+ try {
80
+ await this.db.disconnect();
81
+ }
82
+ catch (error) {
83
+ console.error("Error during cleanup:", error instanceof Error ? error.message : error);
84
+ }
85
+ }
86
+ setupGracefulShutdown() {
87
+ const shutdown = async (signal) => {
88
+ console.error(`Received ${signal}, shutting down gracefully...`);
89
+ await this.cleanup();
90
+ process.exit(0);
91
+ };
92
+ process.on("SIGINT", () => shutdown("SIGINT"));
93
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
94
+ process.on("uncaughtException", async (error) => {
95
+ console.error("Uncaught exception:", error);
96
+ await this.cleanup();
97
+ process.exit(1);
98
+ });
99
+ process.on("unhandledRejection", async (reason) => {
100
+ console.error("Unhandled rejection:", reason);
101
+ await this.cleanup();
102
+ process.exit(1);
103
+ });
104
+ }
105
+ }
106
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { MySQLConnection } from "./database.js";
2
+ import { ReadQueryParams, DescribeTableParams, McpToolResult } from "./types.js";
3
+ export declare class MySQLMCPTools {
4
+ private db;
5
+ constructor(db: MySQLConnection);
6
+ readQuery(params: ReadQueryParams): Promise<McpToolResult>;
7
+ listTables(): Promise<McpToolResult>;
8
+ describeTable(params: DescribeTableParams): Promise<McpToolResult>;
9
+ showDatabases(): Promise<McpToolResult>;
10
+ }
11
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EAEd,MAAM,YAAY,CAAC;AAEpB,qBAAa,aAAa;IACZ,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,eAAe;IAEjC,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAiD1D,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC;IAkDpC,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;IAwDlE,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC;CA6C9C"}
package/dist/tools.js ADDED
@@ -0,0 +1,153 @@
1
+ import { MySQLMCPError, } from "./types.js";
2
+ export class MySQLMCPTools {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ async readQuery(params) {
8
+ try {
9
+ const result = await this.db.executeQuery(params.query);
10
+ const resultText = JSON.stringify({
11
+ query: params.query,
12
+ rowCount: result.rowCount,
13
+ executionTime: `${result.executionTime}ms`,
14
+ data: result.data,
15
+ }, null, 2);
16
+ return {
17
+ content: [
18
+ {
19
+ type: "text",
20
+ text: resultText,
21
+ },
22
+ ],
23
+ };
24
+ }
25
+ catch (error) {
26
+ const errorMessage = error instanceof MySQLMCPError
27
+ ? `MySQL Error: ${error.message}`
28
+ : `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`;
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: JSON.stringify({
34
+ error: errorMessage,
35
+ query: params.query,
36
+ }, null, 2),
37
+ },
38
+ ],
39
+ };
40
+ }
41
+ }
42
+ async listTables() {
43
+ try {
44
+ const tables = await this.db.listTables();
45
+ const resultText = JSON.stringify({
46
+ tableCount: tables.length,
47
+ tables: tables.map((table) => ({
48
+ name: table.TABLE_NAME,
49
+ type: table.TABLE_TYPE,
50
+ schema: table.TABLE_SCHEMA,
51
+ })),
52
+ }, null, 2);
53
+ return {
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text: resultText,
58
+ },
59
+ ],
60
+ };
61
+ }
62
+ catch (error) {
63
+ const errorMessage = error instanceof MySQLMCPError
64
+ ? `MySQL Error: ${error.message}`
65
+ : `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`;
66
+ return {
67
+ content: [
68
+ {
69
+ type: "text",
70
+ text: JSON.stringify({
71
+ error: errorMessage,
72
+ }, null, 2),
73
+ },
74
+ ],
75
+ };
76
+ }
77
+ }
78
+ async describeTable(params) {
79
+ try {
80
+ const columns = await this.db.describeTable(params.tableName);
81
+ const resultText = JSON.stringify({
82
+ tableName: params.tableName,
83
+ columnCount: columns.length,
84
+ columns: columns.map((column) => ({
85
+ name: column.COLUMN_NAME,
86
+ type: column.DATA_TYPE,
87
+ nullable: column.IS_NULLABLE === "YES",
88
+ default: column.COLUMN_DEFAULT,
89
+ key: column.COLUMN_KEY,
90
+ extra: column.EXTRA,
91
+ comment: column.COLUMN_COMMENT,
92
+ })),
93
+ }, null, 2);
94
+ return {
95
+ content: [
96
+ {
97
+ type: "text",
98
+ text: resultText,
99
+ },
100
+ ],
101
+ };
102
+ }
103
+ catch (error) {
104
+ const errorMessage = error instanceof MySQLMCPError
105
+ ? `MySQL Error: ${error.message}`
106
+ : `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`;
107
+ return {
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: JSON.stringify({
112
+ error: errorMessage,
113
+ tableName: params.tableName,
114
+ }, null, 2),
115
+ },
116
+ ],
117
+ };
118
+ }
119
+ }
120
+ async showDatabases() {
121
+ try {
122
+ const databases = await this.db.showDatabases();
123
+ const resultText = JSON.stringify({
124
+ databaseCount: databases.length,
125
+ databases: databases.map((db) => db.Database),
126
+ }, null, 2);
127
+ return {
128
+ content: [
129
+ {
130
+ type: "text",
131
+ text: resultText,
132
+ },
133
+ ],
134
+ };
135
+ }
136
+ catch (error) {
137
+ const errorMessage = error instanceof MySQLMCPError
138
+ ? `MySQL Error: ${error.message}`
139
+ : `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`;
140
+ return {
141
+ content: [
142
+ {
143
+ type: "text",
144
+ text: JSON.stringify({
145
+ error: errorMessage,
146
+ }, null, 2),
147
+ },
148
+ ],
149
+ };
150
+ }
151
+ }
152
+ }
153
+ //# sourceMappingURL=tools.js.map