@jessejoris/mcp-mysql-via-api 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/PRD.md +364 -0
- package/README.md +202 -0
- package/bin/mcp-mysql-via-api.js +4 -0
- package/dist/api-client/mysqlApiClient.d.ts +46 -0
- package/dist/api-client/mysqlApiClient.js +318 -0
- package/dist/config/config.d.ts +10 -0
- package/dist/config/config.js +56 -0
- package/dist/formatters/jsonResponse.d.ts +48 -0
- package/dist/formatters/jsonResponse.js +86 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +25 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +75 -0
- package/dist/permissions/permissionManager.d.ts +25 -0
- package/dist/permissions/permissionManager.js +199 -0
- package/dist/server/apiServer.d.ts +7 -0
- package/dist/server/apiServer.js +1502 -0
- package/dist/server/dbPool.d.ts +12 -0
- package/dist/server/dbPool.js +53 -0
- package/dist/server/standalone.d.ts +1 -0
- package/dist/server/standalone.js +19 -0
- package/dist/tools/toolDefinitions.d.ts +5 -0
- package/dist/tools/toolDefinitions.js +725 -0
- package/dist/tools/toolHandlers.d.ts +16 -0
- package/dist/tools/toolHandlers.js +285 -0
- package/dist/types/index.d.ts +184 -0
- package/dist/types/index.js +5 -0
- package/package.json +64 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PermissionManager } from "../permissions/permissionManager.js";
|
|
2
|
+
import { MysqlApiClient } from "../api-client/mysqlApiClient.js";
|
|
3
|
+
import { ServerConfig } from "../types/index.js";
|
|
4
|
+
export declare class ToolHandlerRegistry {
|
|
5
|
+
private permissionManager;
|
|
6
|
+
private apiClient;
|
|
7
|
+
private config;
|
|
8
|
+
constructor(config: ServerConfig, permissionManager: PermissionManager, apiClient: MysqlApiClient);
|
|
9
|
+
handleToolCall(toolName: string, args?: Record<string, any>): Promise<{
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
isError: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolHandlerRegistry = void 0;
|
|
4
|
+
const toolDefinitions_js_1 = require("./toolDefinitions.js");
|
|
5
|
+
const permissionManager_js_1 = require("../permissions/permissionManager.js");
|
|
6
|
+
const jsonResponse_js_1 = require("../formatters/jsonResponse.js");
|
|
7
|
+
class ToolHandlerRegistry {
|
|
8
|
+
permissionManager;
|
|
9
|
+
apiClient;
|
|
10
|
+
config;
|
|
11
|
+
constructor(config, permissionManager, apiClient) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.permissionManager = permissionManager;
|
|
14
|
+
this.apiClient = apiClient;
|
|
15
|
+
}
|
|
16
|
+
async handleToolCall(toolName, args = {}) {
|
|
17
|
+
const startTime = Date.now();
|
|
18
|
+
// 1. Permission check
|
|
19
|
+
const queryContext = args?.query || undefined;
|
|
20
|
+
const permCheck = this.permissionManager.isToolAllowed(toolName, queryContext);
|
|
21
|
+
if (!permCheck.allowed) {
|
|
22
|
+
return jsonResponse_js_1.JsonResponseFormatter.mcpError("PERMISSION_DENIED", permCheck.reason || `Tool '${toolName}' is not permitted under current policy.`, {
|
|
23
|
+
tool: toolName,
|
|
24
|
+
requiredPermission: permCheck.requiredPermission,
|
|
25
|
+
activePermissions: this.permissionManager.getActivePermissions(),
|
|
26
|
+
policy: this.permissionManager.getProfileSummary().preset,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// 2. Dispatch to specific tool handler
|
|
30
|
+
try {
|
|
31
|
+
let result;
|
|
32
|
+
switch (toolName) {
|
|
33
|
+
// --- Discovery Tools ---
|
|
34
|
+
case "list_databases":
|
|
35
|
+
result = await this.apiClient.listDatabases();
|
|
36
|
+
break;
|
|
37
|
+
case "list_tables":
|
|
38
|
+
result = await this.apiClient.listTables(args.database);
|
|
39
|
+
break;
|
|
40
|
+
case "read_table_schema":
|
|
41
|
+
result = await this.apiClient.readTableSchema(args.table_name, args.database);
|
|
42
|
+
break;
|
|
43
|
+
case "get_database_summary":
|
|
44
|
+
result = await this.apiClient.getDatabaseSummary({
|
|
45
|
+
database: args.database,
|
|
46
|
+
maxTables: args.max_tables,
|
|
47
|
+
includeRelationships: args.include_relationships,
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
case "get_schema_rag_context":
|
|
51
|
+
result = await this.apiClient.getSchemaRagContext({
|
|
52
|
+
database: args.database,
|
|
53
|
+
max_tables: args.max_tables,
|
|
54
|
+
max_columns: args.max_columns,
|
|
55
|
+
keyword_filter: args.keyword_filter,
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
58
|
+
case "get_schema_erd":
|
|
59
|
+
result = await this.apiClient.getSchemaErd(args.database);
|
|
60
|
+
break;
|
|
61
|
+
case "get_all_tables_relationships":
|
|
62
|
+
result = await this.apiClient.getAllTablesRelationships(args.database);
|
|
63
|
+
break;
|
|
64
|
+
case "search_schema":
|
|
65
|
+
result = await this.apiClient.searchSchema(args.query, args.database);
|
|
66
|
+
break;
|
|
67
|
+
case "find_tables_by_keyword":
|
|
68
|
+
result = await this.apiClient.findTablesByKeyword(args.keyword, args.database, args.limit);
|
|
69
|
+
break;
|
|
70
|
+
case "search_data_across_tables":
|
|
71
|
+
result = await this.apiClient.searchDataAcrossTables({
|
|
72
|
+
keyword: args.keyword,
|
|
73
|
+
tables: args.tables,
|
|
74
|
+
database: args.database,
|
|
75
|
+
max_tables: args.max_tables,
|
|
76
|
+
limit_per_table: args.limit_per_table,
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
// --- CRUD Tools with Default Pagination and Bypass ---
|
|
80
|
+
case "read_records":
|
|
81
|
+
result = await this.apiClient.readRecords({
|
|
82
|
+
table_name: args.table_name,
|
|
83
|
+
columns: args.columns,
|
|
84
|
+
filters: args.filters || args.conditions,
|
|
85
|
+
conditions: args.conditions,
|
|
86
|
+
sorting: args.sorting,
|
|
87
|
+
pagination: args.pagination,
|
|
88
|
+
bypass_pagination: args.bypass_pagination ?? args.all,
|
|
89
|
+
all: args.all,
|
|
90
|
+
database: args.database,
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
case "read_record":
|
|
94
|
+
result = await this.apiClient.readRecord({
|
|
95
|
+
table_name: args.table_name,
|
|
96
|
+
id: args.id,
|
|
97
|
+
key_column: args.key_column,
|
|
98
|
+
key_value: args.key_value,
|
|
99
|
+
database: args.database,
|
|
100
|
+
});
|
|
101
|
+
break;
|
|
102
|
+
case "count_records":
|
|
103
|
+
result = await this.apiClient.countRecords({
|
|
104
|
+
table_name: args.table_name,
|
|
105
|
+
filters: args.filters || args.conditions,
|
|
106
|
+
conditions: args.conditions,
|
|
107
|
+
database: args.database,
|
|
108
|
+
});
|
|
109
|
+
break;
|
|
110
|
+
case "create_record":
|
|
111
|
+
result = await this.apiClient.createRecord({
|
|
112
|
+
table_name: args.table_name,
|
|
113
|
+
data: args.data,
|
|
114
|
+
database: args.database,
|
|
115
|
+
});
|
|
116
|
+
break;
|
|
117
|
+
case "bulk_insert":
|
|
118
|
+
result = await this.apiClient.bulkInsert({
|
|
119
|
+
table_name: args.table_name,
|
|
120
|
+
records: args.records,
|
|
121
|
+
database: args.database,
|
|
122
|
+
});
|
|
123
|
+
break;
|
|
124
|
+
case "update_record":
|
|
125
|
+
result = await this.apiClient.updateRecord({
|
|
126
|
+
table_name: args.table_name,
|
|
127
|
+
data: args.data,
|
|
128
|
+
filters: args.filters || args.conditions,
|
|
129
|
+
conditions: args.conditions,
|
|
130
|
+
database: args.database,
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
case "bulk_update":
|
|
134
|
+
result = await this.apiClient.bulkUpdate({
|
|
135
|
+
table_name: args.table_name,
|
|
136
|
+
records: args.records,
|
|
137
|
+
key_column: args.key_column,
|
|
138
|
+
database: args.database,
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
case "delete_record":
|
|
142
|
+
result = await this.apiClient.deleteRecord({
|
|
143
|
+
table_name: args.table_name,
|
|
144
|
+
filters: args.filters || args.conditions,
|
|
145
|
+
conditions: args.conditions,
|
|
146
|
+
database: args.database,
|
|
147
|
+
});
|
|
148
|
+
break;
|
|
149
|
+
case "bulk_delete":
|
|
150
|
+
result = await this.apiClient.bulkDelete({
|
|
151
|
+
table_name: args.table_name,
|
|
152
|
+
key_column: args.key_column,
|
|
153
|
+
keys: args.keys,
|
|
154
|
+
database: args.database,
|
|
155
|
+
});
|
|
156
|
+
break;
|
|
157
|
+
// --- Data Analysis Tools ---
|
|
158
|
+
case "get_column_statistics":
|
|
159
|
+
result = await this.apiClient.getColumnStatistics({
|
|
160
|
+
table_name: args.table_name,
|
|
161
|
+
column_name: args.column_name,
|
|
162
|
+
database: args.database,
|
|
163
|
+
});
|
|
164
|
+
break;
|
|
165
|
+
// --- Query Management ---
|
|
166
|
+
case "run_select_query":
|
|
167
|
+
result = await this.apiClient.runSelectQuery({
|
|
168
|
+
query: args.query,
|
|
169
|
+
params: args.params,
|
|
170
|
+
page: args.page,
|
|
171
|
+
limit: args.limit,
|
|
172
|
+
bypass_pagination: args.bypass_pagination ?? args.all,
|
|
173
|
+
all: args.all,
|
|
174
|
+
database: args.database,
|
|
175
|
+
});
|
|
176
|
+
break;
|
|
177
|
+
case "execute_write_query":
|
|
178
|
+
result = await this.apiClient.executeWriteQuery({
|
|
179
|
+
query: args.query,
|
|
180
|
+
params: args.params,
|
|
181
|
+
database: args.database,
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
// --- Schema DDL ---
|
|
185
|
+
case "create_table":
|
|
186
|
+
result = await this.apiClient.createTable({
|
|
187
|
+
table_name: args.table_name,
|
|
188
|
+
columns: args.columns,
|
|
189
|
+
primary_key: args.primary_key,
|
|
190
|
+
database: args.database,
|
|
191
|
+
});
|
|
192
|
+
break;
|
|
193
|
+
case "alter_table":
|
|
194
|
+
result = await this.apiClient.alterTable(args.table_name, args.action, args.column_definition, args.database);
|
|
195
|
+
break;
|
|
196
|
+
case "drop_table":
|
|
197
|
+
result = await this.apiClient.dropTable({
|
|
198
|
+
table_name: args.table_name,
|
|
199
|
+
if_exists: args.if_exists,
|
|
200
|
+
database: args.database,
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
case "execute_ddl":
|
|
204
|
+
result = await this.apiClient.executeDdl({
|
|
205
|
+
query: args.query,
|
|
206
|
+
database: args.database,
|
|
207
|
+
});
|
|
208
|
+
break;
|
|
209
|
+
// --- Utilities ---
|
|
210
|
+
case "test_connection":
|
|
211
|
+
result = await this.apiClient.testConnection();
|
|
212
|
+
break;
|
|
213
|
+
case "describe_connection":
|
|
214
|
+
result = {
|
|
215
|
+
data: {
|
|
216
|
+
apiEndpoint: this.config.apiEndpoint,
|
|
217
|
+
hasApiKey: Boolean(this.config.apiKey),
|
|
218
|
+
permissionsProfile: this.permissionManager.getProfileSummary(),
|
|
219
|
+
paginationDefaults: {
|
|
220
|
+
defaultPageSize: this.config.defaultPageSize,
|
|
221
|
+
maxPageSize: this.config.maxPageSize,
|
|
222
|
+
maxBypassLimit: this.config.maxBypassLimit,
|
|
223
|
+
},
|
|
224
|
+
timeoutMs: this.config.apiTimeoutMs,
|
|
225
|
+
status: "connected",
|
|
226
|
+
},
|
|
227
|
+
meta: {
|
|
228
|
+
executionTimeMs: Date.now() - startTime,
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
break;
|
|
232
|
+
case "list_all_tools": {
|
|
233
|
+
const catalog = toolDefinitions_js_1.TOOL_DEFINITIONS.map((def) => {
|
|
234
|
+
const reqPerm = permissionManager_js_1.TOOL_PERMISSION_MAP[def.name];
|
|
235
|
+
const isEnabled = this.permissionManager.isPermissionAllowed(reqPerm);
|
|
236
|
+
return {
|
|
237
|
+
name: def.name,
|
|
238
|
+
requiredPermission: reqPerm,
|
|
239
|
+
enabled: isEnabled,
|
|
240
|
+
description: def.description,
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
result = {
|
|
244
|
+
data: {
|
|
245
|
+
totalTools: catalog.length,
|
|
246
|
+
enabledTools: catalog.filter((t) => t.enabled).length,
|
|
247
|
+
blockedTools: catalog.filter((t) => !t.enabled).length,
|
|
248
|
+
profile: this.permissionManager.getProfileSummary(),
|
|
249
|
+
tools: catalog,
|
|
250
|
+
},
|
|
251
|
+
meta: {
|
|
252
|
+
executionTimeMs: Date.now() - startTime,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case "export_table_to_csv":
|
|
258
|
+
result = await this.apiClient.exportTableToCsv(args.table_name, args.limit, args.database);
|
|
259
|
+
break;
|
|
260
|
+
case "export_query_to_csv":
|
|
261
|
+
result = await this.apiClient.exportQueryToCsv(args.query, args.limit, args.database);
|
|
262
|
+
break;
|
|
263
|
+
default:
|
|
264
|
+
return jsonResponse_js_1.JsonResponseFormatter.mcpError("UNKNOWN_TOOL", `No handler found for tool '${toolName}'.`);
|
|
265
|
+
}
|
|
266
|
+
// Check if result has standard data/meta or raw payload
|
|
267
|
+
const responseData = result?.data !== undefined ? result.data : result;
|
|
268
|
+
const responseMeta = {
|
|
269
|
+
executionTimeMs: Date.now() - startTime,
|
|
270
|
+
...result?.meta,
|
|
271
|
+
};
|
|
272
|
+
return jsonResponse_js_1.JsonResponseFormatter.mcpSuccess(responseData, responseMeta);
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
return jsonResponse_js_1.JsonResponseFormatter.mcpError(error.code || (error.status ? `HTTP_${error.status}` : "API_REQUEST_FAILED"), error.message || "An unexpected error occurred while executing tool via API.", {
|
|
276
|
+
tool: toolName,
|
|
277
|
+
executionTimeMs: Date.now() - startTime,
|
|
278
|
+
endpoint: this.config.apiEndpoint,
|
|
279
|
+
status: error.status,
|
|
280
|
+
details: error.details,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
exports.ToolHandlerRegistry = ToolHandlerRegistry;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for MySQL MCP via API
|
|
3
|
+
*/
|
|
4
|
+
export type ToolPermission = "list" | "read" | "create" | "update" | "delete" | "execute" | "ddl" | "utility" | "transaction";
|
|
5
|
+
export type PermissionPreset = "only-read" | "readonly" | "read-only" | "read-write" | "admin" | "all" | "custom";
|
|
6
|
+
export interface PaginationParams {
|
|
7
|
+
page?: number;
|
|
8
|
+
limit?: number;
|
|
9
|
+
bypass_pagination?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface PaginationMeta {
|
|
12
|
+
page: number;
|
|
13
|
+
limit: number;
|
|
14
|
+
total: number;
|
|
15
|
+
totalPages: number;
|
|
16
|
+
hasMore: boolean;
|
|
17
|
+
bypassed?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ApiResponseMeta {
|
|
20
|
+
pagination?: PaginationMeta;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
executionTimeMs?: number;
|
|
23
|
+
endpoint?: string;
|
|
24
|
+
recordCount?: number;
|
|
25
|
+
warning?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ApiSuccessEnvelope<T = any> {
|
|
28
|
+
success: true;
|
|
29
|
+
data: T;
|
|
30
|
+
meta: ApiResponseMeta;
|
|
31
|
+
}
|
|
32
|
+
export interface ApiErrorDetail {
|
|
33
|
+
code: string;
|
|
34
|
+
message: string;
|
|
35
|
+
details?: Record<string, any>;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ApiErrorEnvelope {
|
|
39
|
+
success: false;
|
|
40
|
+
error: ApiErrorDetail;
|
|
41
|
+
}
|
|
42
|
+
export type StrictJsonResponse<T = any> = ApiSuccessEnvelope<T> | ApiErrorEnvelope;
|
|
43
|
+
export type FilterOperator = "=" | "==" | "eq" | "!=" | "<>" | "neq" | ">" | "gt" | ">=" | "gte" | "<" | "lt" | "<=" | "lte" | "LIKE" | "like" | "NOT LIKE" | "not like" | "IN" | "in" | "NOT IN" | "not in" | "IS NULL" | "is null" | "IS NOT NULL" | "is not null" | "BETWEEN" | "between";
|
|
44
|
+
export interface FilterClause {
|
|
45
|
+
field: string;
|
|
46
|
+
operator: FilterOperator | string;
|
|
47
|
+
value?: any;
|
|
48
|
+
secondValue?: any;
|
|
49
|
+
}
|
|
50
|
+
export interface SortingClause {
|
|
51
|
+
field: string;
|
|
52
|
+
direction: "asc" | "desc" | "ASC" | "DESC";
|
|
53
|
+
}
|
|
54
|
+
export interface ReadRecordsParams extends PaginationParams {
|
|
55
|
+
table_name: string;
|
|
56
|
+
columns?: string[];
|
|
57
|
+
filters?: FilterClause[];
|
|
58
|
+
conditions?: FilterClause[];
|
|
59
|
+
sorting?: SortingClause;
|
|
60
|
+
database?: string;
|
|
61
|
+
all?: boolean;
|
|
62
|
+
pagination?: {
|
|
63
|
+
page?: number;
|
|
64
|
+
limit?: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export interface ReadRecordParams {
|
|
68
|
+
table_name: string;
|
|
69
|
+
id?: string | number;
|
|
70
|
+
key_column?: string;
|
|
71
|
+
key_value?: string | number;
|
|
72
|
+
database?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface CreateRecordParams {
|
|
75
|
+
table_name: string;
|
|
76
|
+
data: Record<string, any>;
|
|
77
|
+
database?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface BulkInsertParams {
|
|
80
|
+
table_name: string;
|
|
81
|
+
records: Array<Record<string, any>>;
|
|
82
|
+
database?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface UpdateRecordParams {
|
|
85
|
+
table_name: string;
|
|
86
|
+
data: Record<string, any>;
|
|
87
|
+
filters?: FilterClause[];
|
|
88
|
+
conditions?: FilterClause[];
|
|
89
|
+
database?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface BulkUpdateParams {
|
|
92
|
+
table_name: string;
|
|
93
|
+
records: Array<Record<string, any>>;
|
|
94
|
+
key_column: string;
|
|
95
|
+
database?: string;
|
|
96
|
+
}
|
|
97
|
+
export interface DeleteRecordParams {
|
|
98
|
+
table_name: string;
|
|
99
|
+
filters?: FilterClause[];
|
|
100
|
+
conditions?: FilterClause[];
|
|
101
|
+
database?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface BulkDeleteParams {
|
|
104
|
+
table_name: string;
|
|
105
|
+
key_column: string;
|
|
106
|
+
keys: Array<string | number>;
|
|
107
|
+
database?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface CountRecordsParams {
|
|
110
|
+
table_name: string;
|
|
111
|
+
filters?: FilterClause[];
|
|
112
|
+
conditions?: FilterClause[];
|
|
113
|
+
database?: string;
|
|
114
|
+
}
|
|
115
|
+
export interface RagContextParams {
|
|
116
|
+
database?: string;
|
|
117
|
+
max_tables?: number;
|
|
118
|
+
max_columns?: number;
|
|
119
|
+
include_relationships?: boolean;
|
|
120
|
+
include_comments?: boolean;
|
|
121
|
+
keyword_filter?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface SearchDataParams {
|
|
124
|
+
keyword: string;
|
|
125
|
+
database?: string;
|
|
126
|
+
tables?: string[];
|
|
127
|
+
columns?: string[];
|
|
128
|
+
max_tables?: number;
|
|
129
|
+
limit_per_table?: number;
|
|
130
|
+
}
|
|
131
|
+
export interface ColumnStatsParams {
|
|
132
|
+
table_name: string;
|
|
133
|
+
column_name: string;
|
|
134
|
+
database?: string;
|
|
135
|
+
}
|
|
136
|
+
export interface SelectQueryParams extends PaginationParams {
|
|
137
|
+
query: string;
|
|
138
|
+
params?: any[];
|
|
139
|
+
database?: string;
|
|
140
|
+
all?: boolean;
|
|
141
|
+
}
|
|
142
|
+
export interface WriteQueryParams {
|
|
143
|
+
query: string;
|
|
144
|
+
params?: any[];
|
|
145
|
+
database?: string;
|
|
146
|
+
}
|
|
147
|
+
export interface DdlQueryParams {
|
|
148
|
+
query: string;
|
|
149
|
+
database?: string;
|
|
150
|
+
}
|
|
151
|
+
export interface CreateTableParams {
|
|
152
|
+
table_name: string;
|
|
153
|
+
columns: Array<{
|
|
154
|
+
name: string;
|
|
155
|
+
type: string;
|
|
156
|
+
nullable?: boolean;
|
|
157
|
+
defaultValue?: any;
|
|
158
|
+
autoIncrement?: boolean;
|
|
159
|
+
primaryKey?: boolean;
|
|
160
|
+
}>;
|
|
161
|
+
primary_key?: string | string[];
|
|
162
|
+
database?: string;
|
|
163
|
+
}
|
|
164
|
+
export interface DropTableParams {
|
|
165
|
+
table_name: string;
|
|
166
|
+
if_exists?: boolean;
|
|
167
|
+
database?: string;
|
|
168
|
+
}
|
|
169
|
+
export interface ExportCsvParams {
|
|
170
|
+
table_name?: string;
|
|
171
|
+
query?: string;
|
|
172
|
+
limit?: number;
|
|
173
|
+
database?: string;
|
|
174
|
+
}
|
|
175
|
+
export interface ServerConfig {
|
|
176
|
+
apiEndpoint: string;
|
|
177
|
+
apiKey: string;
|
|
178
|
+
permissions: string;
|
|
179
|
+
defaultPageSize: number;
|
|
180
|
+
maxPageSize: number;
|
|
181
|
+
maxBypassLimit: number;
|
|
182
|
+
apiTimeoutMs: number;
|
|
183
|
+
serverPort?: number;
|
|
184
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jessejoris/mcp-mysql-via-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol server for MySQL access via HTTP/REST API with strict JSON output, default pagination with bypass, and configurable granular permissions",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"bin",
|
|
11
|
+
"README.md",
|
|
12
|
+
"PRD.md"
|
|
13
|
+
],
|
|
14
|
+
"bin": {
|
|
15
|
+
"mcp-mysql-via-api": "bin/mcp-mysql-via-api.js"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"start": "node dist/mcp-server.js",
|
|
20
|
+
"start:server": "node dist/server/standalone.js",
|
|
21
|
+
"dev": "ts-node src/mcp-server.ts",
|
|
22
|
+
"dev:server": "ts-node src/server/standalone.ts",
|
|
23
|
+
"test": "jest --runInBand --detectOpenHandles",
|
|
24
|
+
"test:coverage": "jest --coverage --runInBand --detectOpenHandles"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"mysql",
|
|
29
|
+
"api",
|
|
30
|
+
"rest",
|
|
31
|
+
"model-context-protocol",
|
|
32
|
+
"ai",
|
|
33
|
+
"hermes",
|
|
34
|
+
"openclaw",
|
|
35
|
+
"claude",
|
|
36
|
+
"cursor",
|
|
37
|
+
"windsurf",
|
|
38
|
+
"cline"
|
|
39
|
+
],
|
|
40
|
+
"author": "Bertho Joris",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
44
|
+
"cors": "^2.8.5",
|
|
45
|
+
"dotenv": "^16.4.5",
|
|
46
|
+
"express": "^4.21.0",
|
|
47
|
+
"mysql2": "^3.11.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/cors": "^2.8.17",
|
|
51
|
+
"@types/express": "^4.17.21",
|
|
52
|
+
"@types/jest": "^29.5.12",
|
|
53
|
+
"@types/node": "^20.14.0",
|
|
54
|
+
"@types/supertest": "^6.0.2",
|
|
55
|
+
"jest": "^29.7.0",
|
|
56
|
+
"supertest": "^7.0.0",
|
|
57
|
+
"ts-jest": "^29.2.0",
|
|
58
|
+
"ts-node": "^10.9.2",
|
|
59
|
+
"typescript": "^5.5.0"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|