@cmd233/mcp-database-server 1.1.6 → 1.2.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.
@@ -3,12 +3,20 @@ import { formatSuccessResponse } from '../utils/formatUtils.js';
3
3
  /**
4
4
  * 添加业务洞察到备忘录
5
5
  * @param insight 业务洞察文本
6
- * @returns 操作结果
6
+ * @param confirm 安全确认标志(默认 false,防止误操作)
7
+ * @returns 操作结果,包含新增记录的 ID
7
8
  */
8
- export async function appendInsight(insight) {
9
+ export async function appendInsight(insight, confirm = false) {
9
10
  try {
10
11
  if (!insight) {
11
- throw new Error("Insight text is required");
12
+ throw new Error("洞察内容不能为空");
13
+ }
14
+ // 确认检查:防止误操作
15
+ if (!confirm) {
16
+ return formatSuccessResponse({
17
+ success: false,
18
+ message: "需要安全确认。设置 confirm=true 以继续添加洞察。"
19
+ });
12
20
  }
13
21
  // 如果 insights 表不存在则创建
14
22
  await dbExec(`
@@ -18,12 +26,16 @@ export async function appendInsight(insight) {
18
26
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
19
27
  )
20
28
  `);
21
- // 插入洞察记录
22
- await dbRun("INSERT INTO mcp_insights (insight) VALUES (?)", [insight]);
23
- return formatSuccessResponse({ success: true, message: "Insight added" });
29
+ // 插入洞察记录并获取返回的 ID
30
+ const result = await dbRun("INSERT INTO mcp_insights (insight) VALUES (?)", [insight]);
31
+ return formatSuccessResponse({
32
+ success: true,
33
+ message: "洞察已添加",
34
+ id: result.lastID
35
+ });
24
36
  }
25
37
  catch (error) {
26
- throw new Error(`Error adding insight: ${error.message}`);
38
+ throw new Error(`添加洞察失败: ${error.message}`);
27
39
  }
28
40
  }
29
41
  /**
@@ -49,6 +61,6 @@ export async function listInsights() {
49
61
  return formatSuccessResponse(insights);
50
62
  }
51
63
  catch (error) {
52
- throw new Error(`Error listing insights: ${error.message}`);
64
+ throw new Error(`列出洞察失败: ${error.message}`);
53
65
  }
54
66
  }
@@ -8,34 +8,45 @@ import { formatSuccessResponse, convertToCSV } from '../utils/formatUtils.js';
8
8
  export async function readQuery(query) {
9
9
  try {
10
10
  if (!query.trim().toLowerCase().startsWith("select")) {
11
- throw new Error("Only SELECT queries are allowed with read_query");
11
+ throw new Error("read_query 只允许执行 SELECT 查询");
12
12
  }
13
13
  const result = await dbAll(query);
14
14
  return formatSuccessResponse(result);
15
15
  }
16
16
  catch (error) {
17
- throw new Error(`SQL Error: ${error.message}`);
17
+ throw new Error(`SQL 错误: ${error.message}`);
18
18
  }
19
19
  }
20
20
  /**
21
21
  * 执行数据修改 SQL 查询
22
22
  * @param query 要执行的 SQL 查询
23
+ * @param confirm 安全确认标志(默认 false,防止误操作)
23
24
  * @returns 受影响行的信息
24
25
  */
25
- export async function writeQuery(query) {
26
+ export async function writeQuery(query, confirm = false) {
26
27
  try {
27
28
  const lowerQuery = query.trim().toLowerCase();
28
29
  if (lowerQuery.startsWith("select")) {
29
- throw new Error("Use read_query for SELECT operations");
30
+ throw new Error("SELECT 操作请使用 read_query");
30
31
  }
31
- if (!(lowerQuery.startsWith("insert") || lowerQuery.startsWith("update") || lowerQuery.startsWith("delete"))) {
32
- throw new Error("Only INSERT, UPDATE, or DELETE operations are allowed with write_query");
32
+ // 支持 INSERT、UPDATE、DELETE TRUNCATE 操作
33
+ const supportedOperations = ["insert", "update", "delete", "truncate"];
34
+ const operation = supportedOperations.find(op => lowerQuery.startsWith(op));
35
+ if (!operation) {
36
+ throw new Error("write_query 只允许执行 INSERT、UPDATE、DELETE 或 TRUNCATE 操作");
37
+ }
38
+ // 确认检查:防止误操作
39
+ if (!confirm) {
40
+ return formatSuccessResponse({
41
+ success: false,
42
+ message: `需要安全确认。设置 confirm=true 以继续执行 ${operation.toUpperCase()} 操作。`
43
+ });
33
44
  }
34
45
  const result = await dbRun(query);
35
46
  return formatSuccessResponse({ affected_rows: result.changes });
36
47
  }
37
48
  catch (error) {
38
- throw new Error(`SQL Error: ${error.message}`);
49
+ throw new Error(`SQL 错误: ${error.message}`);
39
50
  }
40
51
  }
41
52
  /**
@@ -47,7 +58,7 @@ export async function writeQuery(query) {
47
58
  export async function exportQuery(query, format) {
48
59
  try {
49
60
  if (!query.trim().toLowerCase().startsWith("select")) {
50
- throw new Error("Only SELECT queries are allowed with export_query");
61
+ throw new Error("export_query 只允许执行 SELECT 查询");
51
62
  }
52
63
  const result = await dbAll(query);
53
64
  if (format === "csv") {
@@ -64,10 +75,10 @@ export async function exportQuery(query, format) {
64
75
  return formatSuccessResponse(result);
65
76
  }
66
77
  else {
67
- throw new Error("Unsupported export format. Use 'csv' or 'json'");
78
+ throw new Error("不支持的导出格式。请使用 'csv' 'json'");
68
79
  }
69
80
  }
70
81
  catch (error) {
71
- throw new Error(`Export Error: ${error.message}`);
82
+ throw new Error(`导出错误: ${error.message}`);
72
83
  }
73
84
  }
@@ -1,37 +1,125 @@
1
- import { dbAll, dbExec, getListTablesQuery, getDescribeTableQuery } from '../db/index.js';
1
+ import { dbAll, dbExec, getListTablesQuery, getDescribeTableQuery, getListViewsQuery, getViewDefinitionQuery, supportsViews } from '../db/index.js';
2
2
  import { formatSuccessResponse } from '../utils/formatUtils.js';
3
+ /**
4
+ * 检查数据库对象是否存在
5
+ * @param objectName 对象名
6
+ * @param objectType 对象类型 ('table' | 'view')
7
+ * @returns 如果存在返回 true,否则返回 false
8
+ */
9
+ async function checkObjectExists(objectName, objectType) {
10
+ if (objectType === 'table') {
11
+ const query = getListTablesQuery();
12
+ const objects = await dbAll(query);
13
+ return objects.some(obj => obj.name === objectName);
14
+ }
15
+ else if (supportsViews()) {
16
+ const query = getListViewsQuery();
17
+ const objects = await dbAll(query);
18
+ return objects.some(obj => obj.name === objectName);
19
+ }
20
+ return false;
21
+ }
22
+ /**
23
+ * 格式化列结构信息
24
+ * @param columns 原始列数据数组
25
+ * @returns 格式化后的列数组
26
+ */
27
+ function formatColumns(columns) {
28
+ return columns.map((col) => ({
29
+ name: col.name,
30
+ type: col.type,
31
+ notnull: !!col.notnull,
32
+ default_value: col.dflt_value,
33
+ primary_key: !!col.pk,
34
+ comment: col.comment || null
35
+ }));
36
+ }
37
+ /**
38
+ * 从 SQL 语句中提取表名
39
+ * @param query SQL 语句
40
+ * @param operation SQL 操作类型(CREATE TABLE、ALTER TABLE 等)
41
+ * @returns 提取的表名或 null
42
+ */
43
+ function extractTableName(query, operation) {
44
+ try {
45
+ const normalizedQuery = query.trim().replace(/\s+/g, ' ');
46
+ const operationPrefix = operation.toLowerCase();
47
+ if (!normalizedQuery.toLowerCase().startsWith(operationPrefix)) {
48
+ return null;
49
+ }
50
+ // 移除操作前缀后的剩余部分
51
+ const afterOperation = normalizedQuery.substring(operationPrefix.length).trim();
52
+ // 处理 IF NOT EXISTS 或 IF EXISTS 等子句
53
+ const patterns = [
54
+ /^if\s+not\s+exists\s+([^\s(]+)/i, // CREATE TABLE IF NOT EXISTS tablename
55
+ /^if\s+exists\s+([^\s(]+)/i, // DROP TABLE IF EXISTS tablename
56
+ /^([^\s(]+)/ // CREATE TABLE tablename
57
+ ];
58
+ for (const pattern of patterns) {
59
+ const match = afterOperation.match(pattern);
60
+ if (match && match[1]) {
61
+ // 移除引号(如果有)
62
+ return match[1].replace(/^[`"[]|[`"\]]$/g, '');
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
3
71
  /**
4
72
  * 在数据库中创建新表
5
73
  * @param query CREATE TABLE SQL 语句
74
+ * @param confirm 安全确认标志(默认 false,防止误操作)
6
75
  * @returns 操作结果
7
76
  */
8
- export async function createTable(query) {
77
+ export async function createTable(query, confirm = false) {
9
78
  try {
10
79
  if (!query.trim().toLowerCase().startsWith("create table")) {
11
- throw new Error("Only CREATE TABLE statements are allowed");
80
+ throw new Error("只允许执行 CREATE TABLE 语句");
81
+ }
82
+ // 确认检查:防止误操作
83
+ if (!confirm) {
84
+ const tableName = extractTableName(query, "CREATE TABLE");
85
+ const tableInfo = tableName ? ` '${tableName}'` : '';
86
+ return formatSuccessResponse({
87
+ success: false,
88
+ message: `需要安全确认。设置 confirm=true 以继续创建表${tableInfo}。`
89
+ });
12
90
  }
13
91
  await dbExec(query);
14
- return formatSuccessResponse({ success: true, message: "Table created successfully" });
92
+ return formatSuccessResponse({ success: true, message: "表创建成功" });
15
93
  }
16
94
  catch (error) {
17
- throw new Error(`SQL Error: ${error.message}`);
95
+ throw new Error(`SQL 错误: ${error.message}`);
18
96
  }
19
97
  }
20
98
  /**
21
99
  * 修改现有表的结构
22
100
  * @param query ALTER TABLE SQL 语句
101
+ * @param confirm 安全确认标志(默认 false,防止误操作)
23
102
  * @returns 操作结果
24
103
  */
25
- export async function alterTable(query) {
104
+ export async function alterTable(query, confirm = false) {
26
105
  try {
27
106
  if (!query.trim().toLowerCase().startsWith("alter table")) {
28
- throw new Error("Only ALTER TABLE statements are allowed");
107
+ throw new Error("只允许执行 ALTER TABLE 语句");
108
+ }
109
+ // 确认检查:防止误操作
110
+ if (!confirm) {
111
+ const tableName = extractTableName(query, "ALTER TABLE");
112
+ const tableInfo = tableName ? ` '${tableName}'` : '';
113
+ return formatSuccessResponse({
114
+ success: false,
115
+ message: `需要安全确认。设置 confirm=true 以继续修改表结构${tableInfo}。`
116
+ });
29
117
  }
30
118
  await dbExec(query);
31
- return formatSuccessResponse({ success: true, message: "Table altered successfully" });
119
+ return formatSuccessResponse({ success: true, message: "表结构修改成功" });
32
120
  }
33
121
  catch (error) {
34
- throw new Error(`SQL Error: ${error.message}`);
122
+ throw new Error(`SQL 错误: ${error.message}`);
35
123
  }
36
124
  }
37
125
  /**
@@ -43,77 +131,171 @@ export async function alterTable(query) {
43
131
  export async function dropTable(tableName, confirm) {
44
132
  try {
45
133
  if (!tableName) {
46
- throw new Error("Table name is required");
134
+ throw new Error("表名不能为空");
47
135
  }
48
136
  if (!confirm) {
49
137
  return formatSuccessResponse({
50
138
  success: false,
51
- message: "Safety confirmation required. Set confirm=true to proceed with dropping the table."
139
+ message: "需要安全确认。设置 confirm=true 以继续删除表。"
52
140
  });
53
141
  }
54
- // First check if table exists by directly querying for tables
55
- const query = getListTablesQuery();
56
- const tables = await dbAll(query);
57
- const tableNames = tables.map(t => t.name);
58
- if (!tableNames.includes(tableName)) {
59
- throw new Error(`Table '${tableName}' does not exist`);
142
+ // 检查表是否存在
143
+ if (!(await checkObjectExists(tableName, 'table'))) {
144
+ throw new Error(`表 '${tableName}' 不存在`);
60
145
  }
61
- // Drop the table
146
+ // 删除表
62
147
  await dbExec(`DROP TABLE "${tableName}"`);
63
148
  return formatSuccessResponse({
64
149
  success: true,
65
- message: `Table '${tableName}' dropped successfully`
150
+ message: `表 '${tableName}' 删除成功`
66
151
  });
67
152
  }
68
153
  catch (error) {
69
- throw new Error(`Error dropping table: ${error.message}`);
154
+ throw new Error(`删除表失败: ${error.message}`);
70
155
  }
71
156
  }
72
157
  /**
73
158
  * 列出数据库中的所有表
159
+ * @param includeViews 是否包含视图(默认 false)
74
160
  * @returns 表名数组
75
161
  */
76
- export async function listTables() {
162
+ export async function listTables(includeViews = false) {
77
163
  try {
78
- // Use adapter-specific query for listing tables
164
+ // 使用适配器特定的查询来列出表
79
165
  const query = getListTablesQuery();
80
166
  const tables = await dbAll(query);
81
- return formatSuccessResponse(tables.map((t) => t.name));
167
+ const result = tables.map((t) => ({ name: t.name, type: 'table' }));
168
+ // 如果需要包含视图且数据库支持视图
169
+ if (includeViews && supportsViews()) {
170
+ const viewsQuery = getListViewsQuery();
171
+ const views = await dbAll(viewsQuery);
172
+ result.push(...views.map((v) => ({ name: v.name, type: 'view' })));
173
+ }
174
+ return formatSuccessResponse(result);
82
175
  }
83
176
  catch (error) {
84
- throw new Error(`Error listing tables: ${error.message}`);
177
+ throw new Error(`列出表失败: ${error.message}`);
85
178
  }
86
179
  }
87
180
  /**
88
181
  * 获取指定表的结构信息
89
- * @param tableName 要描述的表名
90
- * @returns 表的列定义
182
+ * 支持实体表和视图
183
+ * @param tableName 要描述的表名或视图名
184
+ * @returns 表/视图的列定义
91
185
  */
92
186
  export async function describeTable(tableName) {
93
187
  try {
94
188
  if (!tableName) {
95
- throw new Error("Table name is required");
189
+ throw new Error("表名不能为空");
96
190
  }
97
- // First check if table exists by directly querying for tables
98
- const query = getListTablesQuery();
99
- const tables = await dbAll(query);
100
- const tableNames = tables.map(t => t.name);
101
- if (!tableNames.includes(tableName)) {
102
- throw new Error(`Table '${tableName}' does not exist`);
191
+ // 检查是表还是视图
192
+ let objectType = 'table';
193
+ if (await checkObjectExists(tableName, 'table')) {
194
+ objectType = 'table';
103
195
  }
104
- // Use adapter-specific query for describing tables
196
+ else if (supportsViews() && await checkObjectExists(tableName, 'view')) {
197
+ objectType = 'view';
198
+ }
199
+ else {
200
+ throw new Error(supportsViews() ? `表或视图 '${tableName}' 不存在` : `表 '${tableName}' 不存在`);
201
+ }
202
+ // 使用适配器特定的查询来描述表/视图结构
105
203
  const descQuery = getDescribeTableQuery(tableName);
106
204
  const columns = await dbAll(descQuery);
107
- return formatSuccessResponse(columns.map((col) => ({
108
- name: col.name,
109
- type: col.type,
110
- notnull: !!col.notnull,
111
- default_value: col.dflt_value,
112
- primary_key: !!col.pk,
113
- comment: col.comment || null
114
- })));
205
+ return formatSuccessResponse({
206
+ name: tableName,
207
+ type: objectType,
208
+ columns: formatColumns(columns)
209
+ });
210
+ }
211
+ catch (error) {
212
+ throw new Error(`描述表结构失败: ${error.message}`);
213
+ }
214
+ }
215
+ /**
216
+ * 列出数据库中的所有视图
217
+ * 仅支持 SQL Server
218
+ * @returns 视图名数组
219
+ */
220
+ export async function listViews() {
221
+ try {
222
+ if (!supportsViews()) {
223
+ throw new Error("视图功能仅支持 SQL Server 数据库");
224
+ }
225
+ const query = getListViewsQuery();
226
+ const views = await dbAll(query);
227
+ return formatSuccessResponse(views.map((v) => v.name));
228
+ }
229
+ catch (error) {
230
+ throw new Error(`列出视图失败: ${error.message}`);
231
+ }
232
+ }
233
+ /**
234
+ * 获取指定视图的结构信息
235
+ * 仅支持 SQL Server
236
+ * @param viewName 视图名
237
+ * @returns 视图的列定义
238
+ */
239
+ export async function describeView(viewName) {
240
+ try {
241
+ if (!viewName) {
242
+ throw new Error("视图名不能为空");
243
+ }
244
+ if (!supportsViews()) {
245
+ throw new Error("视图功能仅支持 SQL Server 数据库");
246
+ }
247
+ // 检查视图是否存在
248
+ if (!(await checkObjectExists(viewName, 'view'))) {
249
+ throw new Error(`视图 '${viewName}' 不存在`);
250
+ }
251
+ // 使用相同的 describe 查询获取视图列结构
252
+ const descQuery = getDescribeTableQuery(viewName);
253
+ const columns = await dbAll(descQuery);
254
+ return formatSuccessResponse({
255
+ name: viewName,
256
+ type: 'view',
257
+ columns: formatColumns(columns)
258
+ });
259
+ }
260
+ catch (error) {
261
+ throw new Error(`描述视图结构失败: ${error.message}`);
262
+ }
263
+ }
264
+ /**
265
+ * 获取视图的定义 SQL
266
+ * 仅支持 SQL Server
267
+ * 注意: 使用 WITH ENCRYPTION 创建的视图无法获取定义
268
+ * @param viewName 视图名
269
+ * @returns 视图定义 SQL
270
+ */
271
+ export async function getViewDefinition(viewName) {
272
+ try {
273
+ if (!viewName) {
274
+ throw new Error("视图名不能为空");
275
+ }
276
+ if (!supportsViews()) {
277
+ throw new Error("视图功能仅支持 SQL Server 数据库");
278
+ }
279
+ // 检查视图是否存在
280
+ if (!(await checkObjectExists(viewName, 'view'))) {
281
+ throw new Error(`视图 '${viewName}' 不存在`);
282
+ }
283
+ // 获取视图定义
284
+ const defQuery = getViewDefinitionQuery(viewName);
285
+ const result = await dbAll(defQuery);
286
+ if (result.length === 0 || !result[0].definition) {
287
+ return formatSuccessResponse({
288
+ name: viewName,
289
+ definition: null,
290
+ message: "视图定义不可用(可能使用 WITH ENCRYPTION 创建)"
291
+ });
292
+ }
293
+ return formatSuccessResponse({
294
+ name: viewName,
295
+ definition: result[0].definition
296
+ });
115
297
  }
116
298
  catch (error) {
117
- throw new Error(`Error describing table: ${error.message}`);
299
+ throw new Error(`获取视图定义失败: ${error.message}`);
118
300
  }
119
301
  }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * 加密和确认码工具模块
3
+ * 用于两步确认机制,防止 AI 模型自动执行危险操作
4
+ */
5
+ // 硬编码密钥,用于生成确认码
6
+ const CONFIRM_SECRET_KEY = 'mcp-db-server-confirm-key-v1';
7
+ /**
8
+ * 获取当前分钟时间戳
9
+ * @returns 当前分钟的时间戳字符串
10
+ */
11
+ function getMinuteTimestamp() {
12
+ const now = Date.now();
13
+ // 转换为分钟级时间戳(去掉秒和毫秒)
14
+ const minuteTimestamp = Math.floor(now / 60000);
15
+ return minuteTimestamp.toString();
16
+ }
17
+ /**
18
+ * 生成简单哈希
19
+ * 使用简单的字符串哈希算法生成固定长度的哈希值
20
+ * @param content 要哈希的内容
21
+ * @returns 8位十六进制哈希字符串
22
+ */
23
+ function generateSimpleHash(content) {
24
+ let hash = 0;
25
+ // 使用简单的字符串哈希算法
26
+ for (let i = 0; i < content.length; i++) {
27
+ const char = content.charCodeAt(i);
28
+ hash = ((hash << 5) - hash) + char;
29
+ hash = hash & hash; // 转换为 32 位整数
30
+ }
31
+ // 转换为无符号整数并转为十六进制,取前 8 位
32
+ const hashStr = Math.abs(hash).toString(16).padStart(8, '0');
33
+ return hashStr.substring(0, 8);
34
+ }
35
+ /**
36
+ * 标准化操作内容
37
+ * 移除多余的空格,统一大小写,确保相同操作生成相同的确认码
38
+ * @param query 原始 SQL 查询或操作内容
39
+ * @returns 标准化后的操作内容
40
+ */
41
+ export function normalizeOperationContent(query) {
42
+ // 移除前后空格,将多个连续空格替换为单个空格
43
+ return query.trim().replace(/\s+/g, ' ');
44
+ }
45
+ /**
46
+ * 生成确认码
47
+ * 基于操作内容和当前分钟时间戳生成 8 位十六进制确认码
48
+ * @param operationContent 操作内容(SQL 查询或操作描述)
49
+ * @returns 8 位十六进制确认码
50
+ */
51
+ export function generateConfirmCode(operationContent) {
52
+ const normalized = normalizeOperationContent(operationContent);
53
+ const minuteTimestamp = getMinuteTimestamp();
54
+ const hashInput = normalized + minuteTimestamp + CONFIRM_SECRET_KEY;
55
+ return generateSimpleHash(hashInput);
56
+ }
57
+ /**
58
+ * 验证确认码
59
+ * 验证确认码是否有效,支持当前分钟和上一分钟的确认码(2 分钟窗口)
60
+ * @param operationContent 操作内容(SQL 查询或操作描述)
61
+ * @param confirmCode 要验证的确认码
62
+ * @returns 确认码是否有效
63
+ */
64
+ export function verifyConfirmCode(operationContent, confirmCode) {
65
+ if (!confirmCode || confirmCode.length !== 8) {
66
+ return false;
67
+ }
68
+ const normalized = normalizeOperationContent(operationContent);
69
+ const now = Date.now();
70
+ const currentMinute = Math.floor(now / 60000);
71
+ const previousMinute = currentMinute - 1;
72
+ // 验证当前分钟的确认码
73
+ const currentHashInput = normalized + currentMinute.toString() + CONFIRM_SECRET_KEY;
74
+ const currentCode = generateSimpleHash(currentHashInput);
75
+ if (currentCode === confirmCode) {
76
+ return true;
77
+ }
78
+ // 验证上一分钟的确认码
79
+ const previousHashInput = normalized + previousMinute.toString() + CONFIRM_SECRET_KEY;
80
+ const previousCode = generateSimpleHash(previousHashInput);
81
+ return previousCode === confirmCode;
82
+ }
83
+ /**
84
+ * 格式化预览响应
85
+ * 生成预览模式的响应格式,包含确认码和操作说明
86
+ * @param toolName 工具名称
87
+ * @param operationContent 操作内容
88
+ * @param additionalInfo 额外信息(如警告消息)
89
+ * @returns 预览响应对象
90
+ */
91
+ export function formatPreviewResponse(toolName, operationContent, additionalInfo) {
92
+ const confirmCode = generateConfirmCode(operationContent);
93
+ const response = {
94
+ preview: true,
95
+ tool_name: toolName,
96
+ confirm_code: confirmCode,
97
+ message: "⚠️ 预览模式:此操作需要确认",
98
+ operation_content: normalizeOperationContent(operationContent),
99
+ instructions: [
100
+ "请仔细检查上述操作内容",
101
+ "如确认执行,请重新调用此工具并提供确认码",
102
+ `参数示例: { "preview": false, "confirm_code": "${confirmCode}" }`
103
+ ]
104
+ };
105
+ // 添加额外信息
106
+ if (additionalInfo?.warning) {
107
+ response.warning = additionalInfo.warning;
108
+ }
109
+ if (additionalInfo?.operation) {
110
+ response.operation = additionalInfo.operation;
111
+ }
112
+ return {
113
+ content: [{
114
+ type: "text",
115
+ text: JSON.stringify(response, null, 2)
116
+ }],
117
+ isError: false
118
+ };
119
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmd233/mcp-database-server",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
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",