@cmd233/mcp-database-server 1.1.1 → 1.1.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.
- package/README.md.en.bak +328 -0
- package/dist/src/db/adapter.js +3 -3
- package/dist/src/db/index.js +23 -23
- package/dist/src/db/mysql-adapter.js +17 -17
- package/dist/src/db/postgresql-adapter.js +24 -24
- package/dist/src/db/sqlite-adapter.js +19 -19
- package/dist/src/db/sqlserver-adapter.js +187 -86
- package/dist/src/handlers/resourceHandlers.js +8 -8
- package/dist/src/handlers/toolHandlers.js +7 -7
- package/dist/src/index.js +25 -25
- package/dist/src/tools/insightTools.js +9 -9
- package/dist/src/tools/queryTools.js +10 -10
- package/dist/src/tools/schemaTools.js +15 -15
- package/dist/src/utils/formatUtils.js +14 -14
- package/package.json +3 -2
- package/readme.md +100 -100
package/dist/src/index.js
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
//
|
|
5
|
+
// 导入数据库工具模块
|
|
6
6
|
import { initDatabase, closeDatabase, getDatabaseMetadata } from './db/index.js';
|
|
7
|
-
//
|
|
7
|
+
// 导入处理器
|
|
8
8
|
import { handleListResources, handleReadResource } from './handlers/resourceHandlers.js';
|
|
9
9
|
import { handleListTools, handleToolCall } from './handlers/toolHandlers.js';
|
|
10
|
-
//
|
|
10
|
+
// 设置使用 stderr 而不是 stdout 的日志记录器,避免干扰 MCP 通信
|
|
11
11
|
const logger = {
|
|
12
12
|
log: (...args) => console.error('[INFO]', ...args),
|
|
13
13
|
error: (...args) => console.error('[ERROR]', ...args),
|
|
14
14
|
warn: (...args) => console.error('[WARN]', ...args),
|
|
15
15
|
info: (...args) => console.error('[INFO]', ...args),
|
|
16
16
|
};
|
|
17
|
-
//
|
|
17
|
+
// 配置服务器
|
|
18
18
|
const server = new Server({
|
|
19
19
|
name: "executeautomation/database-server",
|
|
20
20
|
version: "1.1.0",
|
|
@@ -24,7 +24,7 @@ const server = new Server({
|
|
|
24
24
|
tools: {},
|
|
25
25
|
},
|
|
26
26
|
});
|
|
27
|
-
//
|
|
27
|
+
// 解析命令行参数
|
|
28
28
|
const args = process.argv.slice(2);
|
|
29
29
|
if (args.length === 0) {
|
|
30
30
|
logger.error("Please provide database connection information");
|
|
@@ -35,10 +35,10 @@ if (args.length === 0) {
|
|
|
35
35
|
logger.error("Usage for MySQL with AWS IAM: node index.js --mysql --aws-iam-auth --host <rds-endpoint> --database <database> --user <aws-username> --aws-region <region>");
|
|
36
36
|
process.exit(1);
|
|
37
37
|
}
|
|
38
|
-
//
|
|
38
|
+
// 解析参数以确定数据库类型和连接信息
|
|
39
39
|
let dbType = 'sqlite';
|
|
40
40
|
let connectionInfo = null;
|
|
41
|
-
//
|
|
41
|
+
// 检查是否使用 SQL Server
|
|
42
42
|
if (args.includes('--sqlserver')) {
|
|
43
43
|
dbType = 'sqlserver';
|
|
44
44
|
connectionInfo = {
|
|
@@ -47,7 +47,7 @@ if (args.includes('--sqlserver')) {
|
|
|
47
47
|
user: undefined,
|
|
48
48
|
password: undefined
|
|
49
49
|
};
|
|
50
|
-
//
|
|
50
|
+
// 解析 SQL Server 连接参数
|
|
51
51
|
for (let i = 0; i < args.length; i++) {
|
|
52
52
|
if (args[i] === '--server' && i + 1 < args.length) {
|
|
53
53
|
connectionInfo.server = args[i + 1];
|
|
@@ -65,13 +65,13 @@ if (args.includes('--sqlserver')) {
|
|
|
65
65
|
connectionInfo.port = parseInt(args[i + 1], 10);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
//
|
|
68
|
+
// 验证 SQL Server 连接信息
|
|
69
69
|
if (!connectionInfo.server || !connectionInfo.database) {
|
|
70
70
|
logger.error("Error: SQL Server requires --server and --database parameters");
|
|
71
71
|
process.exit(1);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
//
|
|
74
|
+
// 检查是否使用 PostgreSQL
|
|
75
75
|
else if (args.includes('--postgresql') || args.includes('--postgres')) {
|
|
76
76
|
dbType = 'postgresql';
|
|
77
77
|
connectionInfo = {
|
|
@@ -83,7 +83,7 @@ else if (args.includes('--postgresql') || args.includes('--postgres')) {
|
|
|
83
83
|
ssl: undefined,
|
|
84
84
|
connectionTimeout: undefined
|
|
85
85
|
};
|
|
86
|
-
//
|
|
86
|
+
// 解析 PostgreSQL 连接参数
|
|
87
87
|
for (let i = 0; i < args.length; i++) {
|
|
88
88
|
if (args[i] === '--host' && i + 1 < args.length) {
|
|
89
89
|
connectionInfo.host = args[i + 1];
|
|
@@ -107,13 +107,13 @@ else if (args.includes('--postgresql') || args.includes('--postgres')) {
|
|
|
107
107
|
connectionInfo.connectionTimeout = parseInt(args[i + 1], 10);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
//
|
|
110
|
+
// 验证 PostgreSQL 连接信息
|
|
111
111
|
if (!connectionInfo.host || !connectionInfo.database) {
|
|
112
112
|
logger.error("Error: PostgreSQL requires --host and --database parameters");
|
|
113
113
|
process.exit(1);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
//
|
|
116
|
+
// 检查是否使用 MySQL
|
|
117
117
|
else if (args.includes('--mysql')) {
|
|
118
118
|
dbType = 'mysql';
|
|
119
119
|
connectionInfo = {
|
|
@@ -127,7 +127,7 @@ else if (args.includes('--mysql')) {
|
|
|
127
127
|
awsIamAuth: false,
|
|
128
128
|
awsRegion: undefined
|
|
129
129
|
};
|
|
130
|
-
//
|
|
130
|
+
// 解析 MySQL 连接参数
|
|
131
131
|
for (let i = 0; i < args.length; i++) {
|
|
132
132
|
if (args[i] === '--host' && i + 1 < args.length) {
|
|
133
133
|
connectionInfo.host = args[i + 1];
|
|
@@ -163,12 +163,12 @@ else if (args.includes('--mysql')) {
|
|
|
163
163
|
connectionInfo.awsRegion = args[i + 1];
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
//
|
|
166
|
+
// 验证 MySQL 连接信息
|
|
167
167
|
if (!connectionInfo.host || !connectionInfo.database) {
|
|
168
168
|
logger.error("Error: MySQL requires --host and --database parameters");
|
|
169
169
|
process.exit(1);
|
|
170
170
|
}
|
|
171
|
-
//
|
|
171
|
+
// AWS IAM 认证的额外验证
|
|
172
172
|
if (connectionInfo.awsIamAuth) {
|
|
173
173
|
if (!connectionInfo.user) {
|
|
174
174
|
logger.error("Error: AWS IAM authentication requires --user parameter");
|
|
@@ -178,18 +178,18 @@ else if (args.includes('--mysql')) {
|
|
|
178
178
|
logger.error("Error: AWS IAM authentication requires --aws-region parameter");
|
|
179
179
|
process.exit(1);
|
|
180
180
|
}
|
|
181
|
-
//
|
|
181
|
+
// 为 AWS IAM 认证自动启用 SSL (必需)
|
|
182
182
|
connectionInfo.ssl = true;
|
|
183
183
|
logger.info("AWS IAM authentication enabled - SSL automatically configured");
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
else {
|
|
187
|
-
// SQLite
|
|
187
|
+
// SQLite 模式(默认)
|
|
188
188
|
dbType = 'sqlite';
|
|
189
|
-
connectionInfo = args[0]; //
|
|
189
|
+
connectionInfo = args[0]; // 第一个参数是 SQLite 文件路径
|
|
190
190
|
logger.info(`Using SQLite database at path: ${connectionInfo}`);
|
|
191
191
|
}
|
|
192
|
-
//
|
|
192
|
+
// 设置请求处理器
|
|
193
193
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
194
194
|
return await handleListResources();
|
|
195
195
|
});
|
|
@@ -202,7 +202,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
202
202
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
203
203
|
return await handleToolCall(request.params.name, request.params.arguments);
|
|
204
204
|
});
|
|
205
|
-
//
|
|
205
|
+
// 优雅处理关闭信号
|
|
206
206
|
process.on('SIGINT', async () => {
|
|
207
207
|
logger.info('Shutting down gracefully...');
|
|
208
208
|
await closeDatabase();
|
|
@@ -213,7 +213,7 @@ process.on('SIGTERM', async () => {
|
|
|
213
213
|
await closeDatabase();
|
|
214
214
|
process.exit(0);
|
|
215
215
|
});
|
|
216
|
-
//
|
|
216
|
+
// 添加全局错误处理器
|
|
217
217
|
process.on('uncaughtException', (error) => {
|
|
218
218
|
logger.error('Uncaught exception:', error);
|
|
219
219
|
});
|
|
@@ -221,7 +221,7 @@ process.on('unhandledRejection', (reason, promise) => {
|
|
|
221
221
|
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
222
222
|
});
|
|
223
223
|
/**
|
|
224
|
-
*
|
|
224
|
+
* 启动服务器
|
|
225
225
|
*/
|
|
226
226
|
async function runServer() {
|
|
227
227
|
try {
|
|
@@ -238,7 +238,7 @@ async function runServer() {
|
|
|
238
238
|
else if (dbType === 'mysql') {
|
|
239
239
|
logger.info(`Host: ${connectionInfo.host}, Database: ${connectionInfo.database}`);
|
|
240
240
|
}
|
|
241
|
-
//
|
|
241
|
+
// 初始化数据库
|
|
242
242
|
await initDatabase(connectionInfo, dbType);
|
|
243
243
|
const dbInfo = getDatabaseMetadata();
|
|
244
244
|
logger.info(`Connected to ${dbInfo.name} database`);
|
|
@@ -252,7 +252,7 @@ async function runServer() {
|
|
|
252
252
|
process.exit(1);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
|
-
//
|
|
255
|
+
// 启动服务器
|
|
256
256
|
runServer().catch(error => {
|
|
257
257
|
logger.error("Server initialization failed:", error);
|
|
258
258
|
process.exit(1);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { dbAll, dbExec, dbRun } from '../db/index.js';
|
|
2
2
|
import { formatSuccessResponse } from '../utils/formatUtils.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param insight
|
|
6
|
-
* @returns
|
|
4
|
+
* 添加业务洞察到备忘录
|
|
5
|
+
* @param insight 业务洞察文本
|
|
6
|
+
* @returns 操作结果
|
|
7
7
|
*/
|
|
8
8
|
export async function appendInsight(insight) {
|
|
9
9
|
try {
|
|
10
10
|
if (!insight) {
|
|
11
11
|
throw new Error("Insight text is required");
|
|
12
12
|
}
|
|
13
|
-
//
|
|
13
|
+
// 如果 insights 表不存在则创建
|
|
14
14
|
await dbExec(`
|
|
15
15
|
CREATE TABLE IF NOT EXISTS mcp_insights (
|
|
16
16
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -18,7 +18,7 @@ export async function appendInsight(insight) {
|
|
|
18
18
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
19
19
|
)
|
|
20
20
|
`);
|
|
21
|
-
//
|
|
21
|
+
// 插入洞察记录
|
|
22
22
|
await dbRun("INSERT INTO mcp_insights (insight) VALUES (?)", [insight]);
|
|
23
23
|
return formatSuccessResponse({ success: true, message: "Insight added" });
|
|
24
24
|
}
|
|
@@ -27,15 +27,15 @@ export async function appendInsight(insight) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
* @returns
|
|
30
|
+
* 列出备忘录中的所有洞察
|
|
31
|
+
* @returns 洞察数组
|
|
32
32
|
*/
|
|
33
33
|
export async function listInsights() {
|
|
34
34
|
try {
|
|
35
|
-
//
|
|
35
|
+
// 检查 insights 表是否存在
|
|
36
36
|
const tableExists = await dbAll("SELECT name FROM sqlite_master WHERE type='table' AND name = 'mcp_insights'");
|
|
37
37
|
if (tableExists.length === 0) {
|
|
38
|
-
//
|
|
38
|
+
// 如果表不存在则创建
|
|
39
39
|
await dbExec(`
|
|
40
40
|
CREATE TABLE IF NOT EXISTS mcp_insights (
|
|
41
41
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { dbAll, dbRun } from '../db/index.js';
|
|
2
2
|
import { formatSuccessResponse, convertToCSV } from '../utils/formatUtils.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param query SQL
|
|
6
|
-
* @returns
|
|
4
|
+
* 执行只读 SQL 查询
|
|
5
|
+
* @param query 要执行的 SQL 查询
|
|
6
|
+
* @returns 查询结果
|
|
7
7
|
*/
|
|
8
8
|
export async function readQuery(query) {
|
|
9
9
|
try {
|
|
@@ -18,9 +18,9 @@ export async function readQuery(query) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @param query SQL
|
|
23
|
-
* @returns
|
|
21
|
+
* 执行数据修改 SQL 查询
|
|
22
|
+
* @param query 要执行的 SQL 查询
|
|
23
|
+
* @returns 受影响行的信息
|
|
24
24
|
*/
|
|
25
25
|
export async function writeQuery(query) {
|
|
26
26
|
try {
|
|
@@ -39,10 +39,10 @@ export async function writeQuery(query) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
* @param query SQL
|
|
44
|
-
* @param format
|
|
45
|
-
* @returns
|
|
42
|
+
* 将查询结果导出为 CSV 或 JSON 格式
|
|
43
|
+
* @param query 要执行的 SQL 查询
|
|
44
|
+
* @param format 输出格式(csv 或 json)
|
|
45
|
+
* @returns 格式化的查询结果
|
|
46
46
|
*/
|
|
47
47
|
export async function exportQuery(query, format) {
|
|
48
48
|
try {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { dbAll, dbExec, getListTablesQuery, getDescribeTableQuery } from '../db/index.js';
|
|
2
2
|
import { formatSuccessResponse } from '../utils/formatUtils.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param query CREATE TABLE SQL
|
|
6
|
-
* @returns
|
|
4
|
+
* 在数据库中创建新表
|
|
5
|
+
* @param query CREATE TABLE SQL 语句
|
|
6
|
+
* @returns 操作结果
|
|
7
7
|
*/
|
|
8
8
|
export async function createTable(query) {
|
|
9
9
|
try {
|
|
@@ -18,9 +18,9 @@ export async function createTable(query) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @param query ALTER TABLE SQL
|
|
23
|
-
* @returns
|
|
21
|
+
* 修改现有表的结构
|
|
22
|
+
* @param query ALTER TABLE SQL 语句
|
|
23
|
+
* @returns 操作结果
|
|
24
24
|
*/
|
|
25
25
|
export async function alterTable(query) {
|
|
26
26
|
try {
|
|
@@ -35,10 +35,10 @@ export async function alterTable(query) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @param tableName
|
|
40
|
-
* @param confirm
|
|
41
|
-
* @returns
|
|
38
|
+
* 从数据库中删除表
|
|
39
|
+
* @param tableName 要删除的表名
|
|
40
|
+
* @param confirm 安全确认标志
|
|
41
|
+
* @returns 操作结果
|
|
42
42
|
*/
|
|
43
43
|
export async function dropTable(tableName, confirm) {
|
|
44
44
|
try {
|
|
@@ -70,8 +70,8 @@ export async function dropTable(tableName, confirm) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
74
|
-
* @returns
|
|
73
|
+
* 列出数据库中的所有表
|
|
74
|
+
* @returns 表名数组
|
|
75
75
|
*/
|
|
76
76
|
export async function listTables() {
|
|
77
77
|
try {
|
|
@@ -85,9 +85,9 @@ export async function listTables() {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
89
|
-
* @param tableName
|
|
90
|
-
* @returns
|
|
88
|
+
* 获取指定表的结构信息
|
|
89
|
+
* @param tableName 要描述的表名
|
|
90
|
+
* @returns 表的列定义
|
|
91
91
|
*/
|
|
92
92
|
export async function describeTable(tableName) {
|
|
93
93
|
try {
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param data
|
|
4
|
-
* @returns CSV
|
|
2
|
+
* 将数据转换为 CSV 格式
|
|
3
|
+
* @param data 要转换为 CSV 的对象数组
|
|
4
|
+
* @returns CSV 格式的字符串
|
|
5
5
|
*/
|
|
6
6
|
export function convertToCSV(data) {
|
|
7
7
|
if (data.length === 0)
|
|
8
8
|
return '';
|
|
9
|
-
//
|
|
9
|
+
// 获取表头
|
|
10
10
|
const headers = Object.keys(data[0]);
|
|
11
|
-
//
|
|
11
|
+
// 创建 CSV 表头行
|
|
12
12
|
let csv = headers.join(',') + '\n';
|
|
13
|
-
//
|
|
13
|
+
// 添加数据行
|
|
14
14
|
data.forEach(row => {
|
|
15
15
|
const values = headers.map(header => {
|
|
16
16
|
const val = row[header];
|
|
17
|
-
//
|
|
17
|
+
// 处理包含逗号、引号等的字符串
|
|
18
18
|
if (typeof val === 'string') {
|
|
19
19
|
return `"${val.replace(/"/g, '""')}"`;
|
|
20
20
|
}
|
|
21
|
-
//
|
|
21
|
+
// 对于 null/undefined 使用空字符串
|
|
22
22
|
return val === null || val === undefined ? '' : val;
|
|
23
23
|
});
|
|
24
24
|
csv += values.join(',') + '\n';
|
|
@@ -26,9 +26,9 @@ export function convertToCSV(data) {
|
|
|
26
26
|
return csv;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @param error
|
|
31
|
-
* @returns
|
|
29
|
+
* 格式化错误响应
|
|
30
|
+
* @param error 错误对象或错误消息
|
|
31
|
+
* @returns 格式化的错误响应对象
|
|
32
32
|
*/
|
|
33
33
|
export function formatErrorResponse(error) {
|
|
34
34
|
const message = error instanceof Error ? error.message : error;
|
|
@@ -41,9 +41,9 @@ export function formatErrorResponse(error) {
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
* @param data
|
|
46
|
-
* @returns
|
|
44
|
+
* 格式化成功响应
|
|
45
|
+
* @param data 要格式化的数据
|
|
46
|
+
* @returns 格式化的成功响应对象
|
|
47
47
|
*/
|
|
48
48
|
export function formatSuccessResponse(data) {
|
|
49
49
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmd233/mcp-database-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "MCP server for interacting with SQLite, SQL Server, PostgreSQL and MySQL databases (Fixed nullable field detection)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "cmd233",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"bugs": "https://github.com/cmd233/mcp-database-server/issues",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"bin": {
|
|
11
|
-
"ea-database-server": "dist/src/index.js"
|
|
11
|
+
"ea-database-server": "dist/src/index.js",
|
|
12
|
+
"mcp-database-server": "dist/src/index.js"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"dist"
|