@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,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PermissionManager = exports.TOOL_PERMISSION_MAP = exports.DANGEROUS_SQL_KEYWORDS = exports.ALL_PERMISSIONS = void 0;
|
|
4
|
+
exports.ALL_PERMISSIONS = [
|
|
5
|
+
"list",
|
|
6
|
+
"read",
|
|
7
|
+
"create",
|
|
8
|
+
"update",
|
|
9
|
+
"delete",
|
|
10
|
+
"execute",
|
|
11
|
+
"ddl",
|
|
12
|
+
"utility",
|
|
13
|
+
"transaction",
|
|
14
|
+
];
|
|
15
|
+
exports.DANGEROUS_SQL_KEYWORDS = [
|
|
16
|
+
"INTO OUTFILE",
|
|
17
|
+
"INTO DUMPFILE",
|
|
18
|
+
"LOAD DATA",
|
|
19
|
+
"LOAD_FILE",
|
|
20
|
+
"GRANT",
|
|
21
|
+
"REVOKE",
|
|
22
|
+
"CREATE USER",
|
|
23
|
+
"DROP USER",
|
|
24
|
+
"ALTER USER",
|
|
25
|
+
"SET PASSWORD",
|
|
26
|
+
];
|
|
27
|
+
exports.TOOL_PERMISSION_MAP = {
|
|
28
|
+
// Discovery
|
|
29
|
+
list_databases: "list",
|
|
30
|
+
list_tables: "list",
|
|
31
|
+
read_table_schema: "list",
|
|
32
|
+
get_database_summary: "list",
|
|
33
|
+
get_schema_erd: "list",
|
|
34
|
+
get_schema_rag_context: "list",
|
|
35
|
+
get_all_tables_relationships: "list",
|
|
36
|
+
search_schema: "list",
|
|
37
|
+
find_tables_by_keyword: "list",
|
|
38
|
+
// Read
|
|
39
|
+
read_records: "read",
|
|
40
|
+
read_record: "read",
|
|
41
|
+
count_records: "read",
|
|
42
|
+
run_select_query: "read",
|
|
43
|
+
search_data_across_tables: "read",
|
|
44
|
+
get_column_statistics: "read",
|
|
45
|
+
// Create
|
|
46
|
+
create_record: "create",
|
|
47
|
+
bulk_insert: "create",
|
|
48
|
+
// Update
|
|
49
|
+
update_record: "update",
|
|
50
|
+
bulk_update: "update",
|
|
51
|
+
// Delete
|
|
52
|
+
delete_record: "delete",
|
|
53
|
+
bulk_delete: "delete",
|
|
54
|
+
// Execute
|
|
55
|
+
execute_write_query: "execute",
|
|
56
|
+
// DDL
|
|
57
|
+
create_table: "ddl",
|
|
58
|
+
alter_table: "ddl",
|
|
59
|
+
drop_table: "ddl",
|
|
60
|
+
execute_ddl: "ddl",
|
|
61
|
+
// Utility
|
|
62
|
+
test_connection: "utility",
|
|
63
|
+
describe_connection: "utility",
|
|
64
|
+
list_all_tools: "utility",
|
|
65
|
+
export_table_to_csv: "utility",
|
|
66
|
+
export_query_to_csv: "utility",
|
|
67
|
+
};
|
|
68
|
+
class PermissionManager {
|
|
69
|
+
activePermissions;
|
|
70
|
+
originalConfig;
|
|
71
|
+
presetName;
|
|
72
|
+
constructor(permissionsConfig = "all") {
|
|
73
|
+
this.activePermissions = new Set();
|
|
74
|
+
this.originalConfig = permissionsConfig;
|
|
75
|
+
this.parseConfig(permissionsConfig);
|
|
76
|
+
}
|
|
77
|
+
parseConfig(configStr) {
|
|
78
|
+
this.originalConfig = configStr.trim().toLowerCase();
|
|
79
|
+
this.activePermissions.clear();
|
|
80
|
+
const normalized = this.originalConfig;
|
|
81
|
+
if (!normalized || normalized === "all" || normalized === "admin" || normalized === "*") {
|
|
82
|
+
this.presetName = "admin";
|
|
83
|
+
exports.ALL_PERMISSIONS.forEach((p) => this.activePermissions.add(p));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (normalized === "only-read" ||
|
|
87
|
+
normalized === "readonly" ||
|
|
88
|
+
normalized === "read-only" ||
|
|
89
|
+
normalized === "only_read" ||
|
|
90
|
+
normalized === "read_only" ||
|
|
91
|
+
normalized === "view-only" ||
|
|
92
|
+
normalized === "view_only") {
|
|
93
|
+
this.presetName = "only-read";
|
|
94
|
+
this.activePermissions.add("list");
|
|
95
|
+
this.activePermissions.add("read");
|
|
96
|
+
this.activePermissions.add("utility");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (normalized === "read-write" || normalized === "read_write") {
|
|
100
|
+
this.presetName = "read-write";
|
|
101
|
+
this.activePermissions.add("list");
|
|
102
|
+
this.activePermissions.add("read");
|
|
103
|
+
this.activePermissions.add("create");
|
|
104
|
+
this.activePermissions.add("update");
|
|
105
|
+
this.activePermissions.add("utility");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Custom list: comma-separated
|
|
109
|
+
this.presetName = "custom";
|
|
110
|
+
const tokens = normalized.split(",").map((s) => s.trim().toLowerCase());
|
|
111
|
+
tokens.forEach((token) => {
|
|
112
|
+
if (exports.ALL_PERMISSIONS.includes(token)) {
|
|
113
|
+
this.activePermissions.add(token);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
isPermissionAllowed(perm) {
|
|
118
|
+
return this.activePermissions.has(perm);
|
|
119
|
+
}
|
|
120
|
+
isToolAllowed(toolName, queryContext) {
|
|
121
|
+
const requiredPermission = exports.TOOL_PERMISSION_MAP[toolName];
|
|
122
|
+
if (!requiredPermission) {
|
|
123
|
+
return {
|
|
124
|
+
allowed: false,
|
|
125
|
+
reason: `Unknown tool '${toolName}'.`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// Global check: dangerous SQL keywords (INTO OUTFILE, LOAD DATA, GRANT, etc.)
|
|
129
|
+
if (queryContext) {
|
|
130
|
+
const upper = queryContext.toUpperCase();
|
|
131
|
+
for (const dangerous of exports.DANGEROUS_SQL_KEYWORDS) {
|
|
132
|
+
if (upper.includes(dangerous)) {
|
|
133
|
+
return {
|
|
134
|
+
allowed: false,
|
|
135
|
+
reason: `Operation contains forbidden dangerous SQL keyword '${dangerous}' for security protection.`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Extra safety: export_query_to_csv must only run read-only queries
|
|
141
|
+
if (toolName === "export_query_to_csv" && queryContext) {
|
|
142
|
+
const trimmed = queryContext.trim().toUpperCase();
|
|
143
|
+
if (!/^(SELECT|SHOW|DESCRIBE|DESC|EXPLAIN)/.test(trimmed)) {
|
|
144
|
+
return {
|
|
145
|
+
allowed: false,
|
|
146
|
+
requiredPermission: "read",
|
|
147
|
+
reason: `export_query_to_csv requires a read-only query (SELECT, SHOW, DESCRIBE, EXPLAIN).`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Extra safety: execute_write_query containing DELETE/TRUNCATE requires delete permission
|
|
152
|
+
if (toolName === "execute_write_query" && queryContext) {
|
|
153
|
+
const upper = queryContext.toUpperCase();
|
|
154
|
+
if (upper.includes("DELETE") || upper.includes("TRUNCATE")) {
|
|
155
|
+
if (!this.activePermissions.has("delete")) {
|
|
156
|
+
return {
|
|
157
|
+
allowed: false,
|
|
158
|
+
requiredPermission: "delete",
|
|
159
|
+
reason: `Operation contains DELETE/TRUNCATE statement which requires explicit 'delete' permission.`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (upper.includes("DROP TABLE") ||
|
|
164
|
+
upper.includes("ALTER TABLE") ||
|
|
165
|
+
upper.includes("CREATE TABLE")) {
|
|
166
|
+
if (!this.activePermissions.has("ddl")) {
|
|
167
|
+
return {
|
|
168
|
+
allowed: false,
|
|
169
|
+
requiredPermission: "ddl",
|
|
170
|
+
reason: `Operation contains DDL statement (CREATE/ALTER/DROP) which requires explicit 'ddl' permission.`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!this.activePermissions.has(requiredPermission)) {
|
|
176
|
+
return {
|
|
177
|
+
allowed: false,
|
|
178
|
+
requiredPermission,
|
|
179
|
+
reason: `Permission denied: Tool '${toolName}' requires '${requiredPermission}' permission. Current active permissions: [${Array.from(this.activePermissions).join(", ")}]. Active policy: ${this.presetName || "custom"}.`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return { allowed: true, requiredPermission };
|
|
183
|
+
}
|
|
184
|
+
getActivePermissions() {
|
|
185
|
+
return Array.from(this.activePermissions);
|
|
186
|
+
}
|
|
187
|
+
getBlockedPermissions() {
|
|
188
|
+
return exports.ALL_PERMISSIONS.filter((p) => !this.activePermissions.has(p));
|
|
189
|
+
}
|
|
190
|
+
getProfileSummary() {
|
|
191
|
+
return {
|
|
192
|
+
preset: this.presetName,
|
|
193
|
+
originalConfig: this.originalConfig,
|
|
194
|
+
activePermissions: this.getActivePermissions(),
|
|
195
|
+
blockedPermissions: this.getBlockedPermissions(),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.PermissionManager = PermissionManager;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DatabasePoolManager } from "./dbPool.js";
|
|
2
|
+
export interface CreateApiServerOptions {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
poolManager?: DatabasePoolManager;
|
|
5
|
+
permissions?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createApiServer(options?: CreateApiServerOptions): import("express-serve-static-core").Express;
|