@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.
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MysqlApiClient = void 0;
4
+ class MysqlApiClient {
5
+ endpoint;
6
+ apiKey;
7
+ timeoutMs;
8
+ defaultPageSize;
9
+ maxPageSize;
10
+ maxBypassLimit;
11
+ constructor(config) {
12
+ this.endpoint = config.apiEndpoint.replace(/\/+$/, "");
13
+ this.apiKey = config.apiKey;
14
+ this.timeoutMs = config.apiTimeoutMs;
15
+ this.defaultPageSize = config.defaultPageSize;
16
+ this.maxPageSize = config.maxPageSize;
17
+ this.maxBypassLimit = config.maxBypassLimit;
18
+ }
19
+ getHeaders() {
20
+ const headers = {
21
+ "Content-Type": "application/json",
22
+ Accept: "application/json",
23
+ };
24
+ if (this.apiKey) {
25
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
26
+ headers["x-api-key"] = this.apiKey;
27
+ }
28
+ return headers;
29
+ }
30
+ async request(method, path, body, queryParams) {
31
+ let url = `${this.endpoint}${path.startsWith("/") ? path : `/${path}`}`;
32
+ if (queryParams) {
33
+ const searchParams = new URLSearchParams();
34
+ for (const [key, value] of Object.entries(queryParams)) {
35
+ if (value !== undefined && value !== null) {
36
+ if (typeof value === "object") {
37
+ searchParams.append(key, JSON.stringify(value));
38
+ }
39
+ else {
40
+ searchParams.append(key, String(value));
41
+ }
42
+ }
43
+ }
44
+ const qs = searchParams.toString();
45
+ if (qs) {
46
+ url += (url.includes("?") ? "&" : "?") + qs;
47
+ }
48
+ }
49
+ const controller = new AbortController();
50
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
51
+ try {
52
+ const startTime = Date.now();
53
+ const response = await fetch(url, {
54
+ method,
55
+ headers: this.getHeaders(),
56
+ body: body ? JSON.stringify(body) : undefined,
57
+ signal: controller.signal,
58
+ });
59
+ const duration = Date.now() - startTime;
60
+ const text = await response.text();
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(text);
64
+ }
65
+ catch {
66
+ throw new Error(`Remote server returned non-JSON response (HTTP ${response.status}): ${text.slice(0, 200)}`);
67
+ }
68
+ if (!response.ok) {
69
+ const errorMsg = parsed?.error?.message ||
70
+ parsed?.message ||
71
+ `Remote API error (HTTP ${response.status})`;
72
+ const errorObj = new Error(errorMsg);
73
+ errorObj.status = response.status;
74
+ errorObj.details = parsed;
75
+ throw errorObj;
76
+ }
77
+ // If remote already wrapped in envelope
78
+ if (parsed && typeof parsed === "object" && "success" in parsed && "data" in parsed) {
79
+ return {
80
+ data: parsed.data,
81
+ meta: {
82
+ ...parsed.meta,
83
+ executionTimeMs: duration,
84
+ endpoint: path,
85
+ },
86
+ };
87
+ }
88
+ return {
89
+ data: parsed,
90
+ meta: {
91
+ executionTimeMs: duration,
92
+ endpoint: path,
93
+ },
94
+ };
95
+ }
96
+ catch (err) {
97
+ if (err.name === "AbortError") {
98
+ throw new Error(`Request to MySQL API timed out after ${this.timeoutMs}ms (${method} ${path})`);
99
+ }
100
+ throw err;
101
+ }
102
+ finally {
103
+ clearTimeout(timeout);
104
+ }
105
+ }
106
+ // Health / Ping
107
+ async testConnection() {
108
+ return await this.request("GET", "/health");
109
+ }
110
+ async getInfo() {
111
+ return await this.request("GET", "/api/info");
112
+ }
113
+ // Discovery
114
+ async listDatabases() {
115
+ return await this.request("GET", "/api/databases");
116
+ }
117
+ async listTables(database) {
118
+ return await this.request("GET", "/api/tables", undefined, { database });
119
+ }
120
+ async readTableSchema(tableName, database) {
121
+ return await this.request("GET", `/api/tables/${encodeURIComponent(tableName)}/schema`, undefined, {
122
+ database,
123
+ });
124
+ }
125
+ async getDatabaseSummary(options) {
126
+ return await this.request("GET", "/api/schema/summary", undefined, {
127
+ database: options?.database,
128
+ max_tables: options?.maxTables,
129
+ include_relationships: options?.includeRelationships,
130
+ });
131
+ }
132
+ async getSchemaRagContext(options) {
133
+ return await this.request("GET", "/api/schema/rag-context", undefined, {
134
+ database: options?.database,
135
+ max_tables: options?.max_tables,
136
+ max_columns: options?.max_columns,
137
+ keyword_filter: options?.keyword_filter,
138
+ });
139
+ }
140
+ async getSchemaErd(database) {
141
+ return await this.request("GET", "/api/schema/erd", undefined, { database });
142
+ }
143
+ async getAllTablesRelationships(database) {
144
+ return await this.request("GET", "/api/schema/relationships", undefined, { database });
145
+ }
146
+ async searchSchema(query, database) {
147
+ return await this.request("GET", "/api/schema/search", undefined, { query, database });
148
+ }
149
+ async findTablesByKeyword(keyword, database, limit) {
150
+ return await this.request("GET", "/api/tables/find", undefined, {
151
+ keyword,
152
+ database,
153
+ limit,
154
+ });
155
+ }
156
+ async searchDataAcrossTables(options) {
157
+ return await this.request("GET", "/api/tables/search-data", undefined, {
158
+ keyword: options.keyword,
159
+ database: options.database,
160
+ tables: options.tables,
161
+ columns: options.columns,
162
+ max_tables: options.max_tables,
163
+ limit_per_table: options.limit_per_table,
164
+ });
165
+ }
166
+ // CRUD with Default Pagination + Bypass
167
+ async readRecords(params) {
168
+ const isBypass = Boolean(params.bypass_pagination || params.all || params.limit === 0 || params.pagination?.limit === 0);
169
+ let page = params.pagination?.page || params.page || 1;
170
+ let limit = params.pagination?.limit || params.limit || this.defaultPageSize;
171
+ if (isBypass) {
172
+ limit = this.maxBypassLimit;
173
+ }
174
+ else {
175
+ if (limit > this.maxPageSize) {
176
+ limit = this.maxPageSize;
177
+ }
178
+ }
179
+ const filters = params.filters || params.conditions;
180
+ return await this.request("GET", `/api/tables/${encodeURIComponent(params.table_name)}/records`, undefined, {
181
+ database: params.database,
182
+ columns: params.columns?.join(","),
183
+ filters,
184
+ sort_field: params.sorting?.field,
185
+ sort_direction: params.sorting?.direction,
186
+ page,
187
+ limit,
188
+ bypass: isBypass,
189
+ });
190
+ }
191
+ async readRecord(params) {
192
+ const id = params.id !== undefined ? params.id : params.key_value;
193
+ return await this.request("GET", `/api/tables/${encodeURIComponent(params.table_name)}/records/${encodeURIComponent(String(id))}`, undefined, {
194
+ database: params.database,
195
+ key_column: params.key_column,
196
+ });
197
+ }
198
+ async countRecords(params) {
199
+ const filters = params.filters || params.conditions;
200
+ return await this.request("GET", `/api/tables/${encodeURIComponent(params.table_name)}/count`, undefined, {
201
+ database: params.database,
202
+ filters,
203
+ });
204
+ }
205
+ async getColumnStatistics(params) {
206
+ return await this.request("GET", `/api/tables/${encodeURIComponent(params.table_name)}/columns/${encodeURIComponent(params.column_name)}/stats`, undefined, {
207
+ database: params.database,
208
+ });
209
+ }
210
+ async createRecord(params) {
211
+ return await this.request("POST", `/api/tables/${encodeURIComponent(params.table_name)}/records`, {
212
+ data: params.data,
213
+ database: params.database,
214
+ });
215
+ }
216
+ async bulkInsert(params) {
217
+ return await this.request("POST", `/api/tables/${encodeURIComponent(params.table_name)}/records/bulk`, {
218
+ records: params.records,
219
+ database: params.database,
220
+ });
221
+ }
222
+ async updateRecord(params) {
223
+ const filters = params.filters || params.conditions;
224
+ return await this.request("PUT", `/api/tables/${encodeURIComponent(params.table_name)}/records`, {
225
+ data: params.data,
226
+ filters,
227
+ database: params.database,
228
+ });
229
+ }
230
+ async bulkUpdate(params) {
231
+ return await this.request("PUT", `/api/tables/${encodeURIComponent(params.table_name)}/records/bulk`, {
232
+ records: params.records,
233
+ key_column: params.key_column,
234
+ database: params.database,
235
+ });
236
+ }
237
+ async deleteRecord(params) {
238
+ const filters = params.filters || params.conditions;
239
+ return await this.request("DELETE", `/api/tables/${encodeURIComponent(params.table_name)}/records`, {
240
+ filters,
241
+ database: params.database,
242
+ });
243
+ }
244
+ async bulkDelete(params) {
245
+ return await this.request("POST", `/api/tables/${encodeURIComponent(params.table_name)}/records/bulk-delete`, {
246
+ key_column: params.key_column,
247
+ keys: params.keys,
248
+ database: params.database,
249
+ });
250
+ }
251
+ // Queries
252
+ async runSelectQuery(params) {
253
+ const isBypass = Boolean(params.bypass_pagination || params.all || params.limit === 0);
254
+ let page = params.page || 1;
255
+ let limit = params.limit || this.defaultPageSize;
256
+ if (isBypass) {
257
+ limit = this.maxBypassLimit;
258
+ }
259
+ else {
260
+ if (limit > this.maxPageSize) {
261
+ limit = this.maxPageSize;
262
+ }
263
+ }
264
+ return await this.request("POST", "/api/query/select", {
265
+ query: params.query,
266
+ params: params.params,
267
+ database: params.database,
268
+ page,
269
+ limit,
270
+ bypass: isBypass,
271
+ });
272
+ }
273
+ async executeWriteQuery(params) {
274
+ return await this.request("POST", "/api/query/write", {
275
+ query: params.query,
276
+ params: params.params,
277
+ database: params.database,
278
+ });
279
+ }
280
+ // DDL
281
+ async createTable(params) {
282
+ return await this.request("POST", "/api/tables", params);
283
+ }
284
+ async alterTable(tableName, action, columnDefinition, database) {
285
+ return await this.request("PUT", `/api/tables/${encodeURIComponent(tableName)}`, {
286
+ action,
287
+ column_definition: columnDefinition,
288
+ database,
289
+ });
290
+ }
291
+ async dropTable(params) {
292
+ return await this.request("DELETE", `/api/tables/${encodeURIComponent(params.table_name)}`, {
293
+ if_exists: params.if_exists,
294
+ database: params.database,
295
+ });
296
+ }
297
+ async executeDdl(params) {
298
+ return await this.request("POST", "/api/ddl", {
299
+ query: params.query,
300
+ database: params.database,
301
+ });
302
+ }
303
+ // Export
304
+ async exportTableToCsv(tableName, limit, database) {
305
+ return await this.request("GET", `/api/tables/${encodeURIComponent(tableName)}/export`, undefined, {
306
+ limit,
307
+ database,
308
+ });
309
+ }
310
+ async exportQueryToCsv(query, limit, database) {
311
+ return await this.request("POST", "/api/query/export", {
312
+ query,
313
+ limit,
314
+ database,
315
+ });
316
+ }
317
+ }
318
+ exports.MysqlApiClient = MysqlApiClient;
@@ -0,0 +1,10 @@
1
+ import { ServerConfig } from "../types/index.js";
2
+ export declare class ConfigManager {
3
+ private static instance;
4
+ private config;
5
+ private constructor();
6
+ static getInstance(overrides?: Partial<ServerConfig>): ConfigManager;
7
+ getConfig(): ServerConfig;
8
+ updateConfig(updates: Partial<ServerConfig>): void;
9
+ }
10
+ export declare const appConfig: ServerConfig;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.appConfig = exports.ConfigManager = void 0;
7
+ const dotenv_1 = __importDefault(require("dotenv"));
8
+ dotenv_1.default.config();
9
+ class ConfigManager {
10
+ static instance;
11
+ config;
12
+ constructor(overrides) {
13
+ this.config = {
14
+ apiEndpoint: process.env.MYSQL_API_ENDPOINT ||
15
+ process.env.API_ENDPOINT ||
16
+ "http://localhost:3300",
17
+ apiKey: process.env.MYSQL_API_KEY ||
18
+ process.env.API_KEY ||
19
+ process.env.MYSQL_API_TOKEN ||
20
+ "",
21
+ permissions: process.env.MCP_PERMISSIONS ||
22
+ process.env.MCP_CONFIG ||
23
+ "all",
24
+ defaultPageSize: parseInt(process.env.DEFAULT_PAGE_SIZE || "50", 10),
25
+ maxPageSize: parseInt(process.env.MAX_PAGE_SIZE || "500", 10),
26
+ maxBypassLimit: parseInt(process.env.MAX_BYPASS_LIMIT || "10000", 10),
27
+ apiTimeoutMs: parseInt(process.env.API_TIMEOUT_MS || "30000", 10),
28
+ serverPort: parseInt(process.env.PORT || process.env.SERVER_PORT || "3300", 10),
29
+ ...overrides,
30
+ };
31
+ // Normalize endpoint (strip trailing slash)
32
+ if (this.config.apiEndpoint.endsWith("/")) {
33
+ this.config.apiEndpoint = this.config.apiEndpoint.slice(0, -1);
34
+ }
35
+ }
36
+ static getInstance(overrides) {
37
+ if (!ConfigManager.instance || overrides) {
38
+ ConfigManager.instance = new ConfigManager(overrides);
39
+ }
40
+ return ConfigManager.instance;
41
+ }
42
+ getConfig() {
43
+ return { ...this.config };
44
+ }
45
+ updateConfig(updates) {
46
+ this.config = {
47
+ ...this.config,
48
+ ...updates,
49
+ };
50
+ if (this.config.apiEndpoint.endsWith("/")) {
51
+ this.config.apiEndpoint = this.config.apiEndpoint.slice(0, -1);
52
+ }
53
+ }
54
+ }
55
+ exports.ConfigManager = ConfigManager;
56
+ exports.appConfig = ConfigManager.getInstance().getConfig();
@@ -0,0 +1,48 @@
1
+ import { ApiResponseMeta } from "../types/index.js";
2
+ /**
3
+ * Strict JSON Response Formatter
4
+ * Guarantees that all outputs returned to AI Agents (Hermes, OpenClaw, Claude, etc.)
5
+ * are 100% valid JSON adhering to a standardized schema.
6
+ */
7
+ export declare class JsonResponseFormatter {
8
+ /**
9
+ * Formats successful data into a standardized JSON string envelope.
10
+ */
11
+ static formatSuccess<T = any>(data: T, metaOptions?: Partial<ApiResponseMeta>): string;
12
+ /**
13
+ * Formats error data into a standardized JSON string envelope.
14
+ * Under no circumstances returns non-JSON or raw text prefixes.
15
+ */
16
+ static formatError(code: string, message: string, details?: Record<string, any>): string;
17
+ /**
18
+ * Wraps formatted JSON string in standard MCP tool response format.
19
+ * The text inside content is guaranteed to be parseable JSON.
20
+ */
21
+ static toMcpResult(jsonString: string, isError?: boolean): {
22
+ content: {
23
+ type: "text";
24
+ text: string;
25
+ }[];
26
+ isError: boolean;
27
+ };
28
+ /**
29
+ * Helper to directly produce a success MCP result
30
+ */
31
+ static mcpSuccess<T = any>(data: T, metaOptions?: Partial<ApiResponseMeta>): {
32
+ content: {
33
+ type: "text";
34
+ text: string;
35
+ }[];
36
+ isError: boolean;
37
+ };
38
+ /**
39
+ * Helper to directly produce an error MCP result
40
+ */
41
+ static mcpError(code: string, message: string, details?: Record<string, any>): {
42
+ content: {
43
+ type: "text";
44
+ text: string;
45
+ }[];
46
+ isError: boolean;
47
+ };
48
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JsonResponseFormatter = void 0;
4
+ /**
5
+ * Strict JSON Response Formatter
6
+ * Guarantees that all outputs returned to AI Agents (Hermes, OpenClaw, Claude, etc.)
7
+ * are 100% valid JSON adhering to a standardized schema.
8
+ */
9
+ class JsonResponseFormatter {
10
+ /**
11
+ * Formats successful data into a standardized JSON string envelope.
12
+ */
13
+ static formatSuccess(data, metaOptions = {}) {
14
+ const envelope = {
15
+ success: true,
16
+ data,
17
+ meta: {
18
+ timestamp: new Date().toISOString(),
19
+ ...metaOptions,
20
+ },
21
+ };
22
+ return JSON.stringify(envelope, null, 2);
23
+ }
24
+ /**
25
+ * Formats error data into a standardized JSON string envelope.
26
+ * Under no circumstances returns non-JSON or raw text prefixes.
27
+ */
28
+ static formatError(code, message, details) {
29
+ const envelope = {
30
+ success: false,
31
+ error: {
32
+ code,
33
+ message,
34
+ details: details || {},
35
+ timestamp: new Date().toISOString(),
36
+ },
37
+ };
38
+ return JSON.stringify(envelope, null, 2);
39
+ }
40
+ /**
41
+ * Wraps formatted JSON string in standard MCP tool response format.
42
+ * The text inside content is guaranteed to be parseable JSON.
43
+ */
44
+ static toMcpResult(jsonString, isError = false) {
45
+ // Validate JSON validity just in case
46
+ let validatedString = jsonString;
47
+ try {
48
+ JSON.parse(jsonString);
49
+ }
50
+ catch {
51
+ validatedString = JSON.stringify({
52
+ success: false,
53
+ error: {
54
+ code: "INTERNAL_SERIALIZATION_ERROR",
55
+ message: "Output could not be serialized to valid JSON",
56
+ timestamp: new Date().toISOString(),
57
+ },
58
+ });
59
+ isError = true;
60
+ }
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: validatedString,
66
+ },
67
+ ],
68
+ isError,
69
+ };
70
+ }
71
+ /**
72
+ * Helper to directly produce a success MCP result
73
+ */
74
+ static mcpSuccess(data, metaOptions = {}) {
75
+ const jsonStr = this.formatSuccess(data, metaOptions);
76
+ return this.toMcpResult(jsonStr, false);
77
+ }
78
+ /**
79
+ * Helper to directly produce an error MCP result
80
+ */
81
+ static mcpError(code, message, details) {
82
+ const jsonStr = this.formatError(code, message, details);
83
+ return this.toMcpResult(jsonStr, true);
84
+ }
85
+ }
86
+ exports.JsonResponseFormatter = JsonResponseFormatter;
@@ -0,0 +1,9 @@
1
+ export * from "./types/index.js";
2
+ export * from "./config/config.js";
3
+ export * from "./permissions/permissionManager.js";
4
+ export * from "./formatters/jsonResponse.js";
5
+ export * from "./api-client/mysqlApiClient.js";
6
+ export * from "./tools/toolDefinitions.js";
7
+ export * from "./tools/toolHandlers.js";
8
+ export * from "./server/apiServer.js";
9
+ export * from "./server/dbPool.js";
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types/index.js"), exports);
18
+ __exportStar(require("./config/config.js"), exports);
19
+ __exportStar(require("./permissions/permissionManager.js"), exports);
20
+ __exportStar(require("./formatters/jsonResponse.js"), exports);
21
+ __exportStar(require("./api-client/mysqlApiClient.js"), exports);
22
+ __exportStar(require("./tools/toolDefinitions.js"), exports);
23
+ __exportStar(require("./tools/toolHandlers.js"), exports);
24
+ __exportStar(require("./server/apiServer.js"), exports);
25
+ __exportStar(require("./server/dbPool.js"), exports);
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
+ const config_js_1 = require("./config/config.js");
8
+ const permissionManager_js_1 = require("./permissions/permissionManager.js");
9
+ const mysqlApiClient_js_1 = require("./api-client/mysqlApiClient.js");
10
+ const toolHandlers_js_1 = require("./tools/toolHandlers.js");
11
+ const toolDefinitions_js_1 = require("./tools/toolDefinitions.js");
12
+ const jsonResponse_js_1 = require("./formatters/jsonResponse.js");
13
+ const SERVER_NAME = "mysql-mcp-via-api";
14
+ const SERVER_VERSION = "1.0.0";
15
+ async function main() {
16
+ // 1. Initialize configuration & dependencies
17
+ const configManager = config_js_1.ConfigManager.getInstance();
18
+ const config = configManager.getConfig();
19
+ // Allow CLI arguments override:
20
+ // e.g. mcp-mysql-via-api <apiEndpoint> <permissions> <apiKey>
21
+ const args = process.argv.slice(2);
22
+ if (args[0] && !args[0].startsWith("-")) {
23
+ config.apiEndpoint = args[0];
24
+ }
25
+ if (args[1] && !args[1].startsWith("-")) {
26
+ config.permissions = args[1];
27
+ }
28
+ if (args[2] && !args[2].startsWith("-")) {
29
+ config.apiKey = args[2];
30
+ }
31
+ configManager.updateConfig(config);
32
+ const permissionManager = new permissionManager_js_1.PermissionManager(config.permissions);
33
+ const apiClient = new mysqlApiClient_js_1.MysqlApiClient(config);
34
+ const handlerRegistry = new toolHandlers_js_1.ToolHandlerRegistry(config, permissionManager, apiClient);
35
+ // 2. Instantiate MCP Server
36
+ const server = new index_js_1.Server({
37
+ name: SERVER_NAME,
38
+ version: SERVER_VERSION,
39
+ }, {
40
+ capabilities: {
41
+ tools: {},
42
+ },
43
+ });
44
+ // 3. List Tools Handler
45
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
46
+ return {
47
+ tools: toolDefinitions_js_1.TOOL_DEFINITIONS,
48
+ };
49
+ });
50
+ // 4. Call Tool Handler
51
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
52
+ const { name, arguments: toolArgs } = request.params;
53
+ try {
54
+ return await handlerRegistry.handleToolCall(name, toolArgs || {});
55
+ }
56
+ catch (err) {
57
+ // Catch-all safety: ensure strictly valid JSON envelope
58
+ return jsonResponse_js_1.JsonResponseFormatter.mcpError("UNHANDLED_EXCEPTION", err.message || "An unhandled exception occurred during tool execution", { tool: name, stack: err.stack });
59
+ }
60
+ });
61
+ // 5. Connect Stdio transport
62
+ const transport = new stdio_js_1.StdioServerTransport();
63
+ await server.connect(transport);
64
+ const profile = permissionManager.getProfileSummary();
65
+ console.error(`[${SERVER_NAME} v${SERVER_VERSION}] running on stdio`);
66
+ console.error(`[Target API]: ${config.apiEndpoint}`);
67
+ console.error(`[Policy Profile]: ${profile.preset || "custom"}`);
68
+ console.error(`[Active Permissions]: [${profile.activePermissions.join(", ")}]`);
69
+ console.error(`[Strict JSON Guarantee]: ACTIVE`);
70
+ console.error(`[Pagination Default]: limit=${config.defaultPageSize}, bypass_enabled=true`);
71
+ }
72
+ main().catch((fatalError) => {
73
+ console.error("Fatal error starting MCP server:", fatalError);
74
+ process.exit(1);
75
+ });
@@ -0,0 +1,25 @@
1
+ import { ToolPermission } from "../types/index.js";
2
+ export declare const ALL_PERMISSIONS: ToolPermission[];
3
+ export declare const DANGEROUS_SQL_KEYWORDS: string[];
4
+ export declare const TOOL_PERMISSION_MAP: Record<string, ToolPermission>;
5
+ export declare class PermissionManager {
6
+ private activePermissions;
7
+ private originalConfig;
8
+ private presetName?;
9
+ constructor(permissionsConfig?: string);
10
+ parseConfig(configStr: string): void;
11
+ isPermissionAllowed(perm: ToolPermission): boolean;
12
+ isToolAllowed(toolName: string, queryContext?: string): {
13
+ allowed: boolean;
14
+ requiredPermission?: ToolPermission;
15
+ reason?: string;
16
+ };
17
+ getActivePermissions(): ToolPermission[];
18
+ getBlockedPermissions(): ToolPermission[];
19
+ getProfileSummary(): {
20
+ preset: string | undefined;
21
+ originalConfig: string;
22
+ activePermissions: ToolPermission[];
23
+ blockedPermissions: ToolPermission[];
24
+ };
25
+ }