@conte777/db-view-mcp 1.0.0 → 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.
- package/config.example.json +15 -3
- package/dist/config/loader.d.ts +1 -0
- package/dist/config/loader.js +25 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +53 -15
- package/dist/config/types.js +31 -14
- package/dist/config/types.js.map +1 -1
- package/dist/connectors/clickhouse.d.ts +2 -2
- package/dist/connectors/clickhouse.js +14 -3
- package/dist/connectors/clickhouse.js.map +1 -1
- package/dist/connectors/instrumented.d.ts +18 -0
- package/dist/connectors/instrumented.js +52 -0
- package/dist/connectors/instrumented.js.map +1 -0
- package/dist/connectors/interface.d.ts +2 -2
- package/dist/connectors/manager.d.ts +11 -0
- package/dist/connectors/manager.js +81 -5
- package/dist/connectors/manager.js.map +1 -1
- package/dist/connectors/postgresql.d.ts +2 -2
- package/dist/connectors/postgresql.js +57 -19
- package/dist/connectors/postgresql.js.map +1 -1
- package/dist/index.js +75 -16
- package/dist/index.js.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/readonly/explain.d.ts +2 -0
- package/dist/tools/readonly/explain.js +2 -1
- package/dist/tools/readonly/explain.js.map +1 -1
- package/dist/tools/readonly/performance.d.ts +1 -2
- package/dist/tools/readonly/performance.js +7 -9
- package/dist/tools/readonly/performance.js.map +1 -1
- package/dist/tools/readonly/schema.d.ts +2 -0
- package/dist/tools/readonly/schema.js +2 -1
- package/dist/tools/readonly/schema.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.js +2 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/write/transaction.d.ts +18 -0
- package/dist/tools/write/transaction.js +70 -13
- package/dist/tools/write/transaction.js.map +1 -1
- package/dist/transport/http.d.ts +2 -0
- package/dist/transport/http.js +28 -14
- package/dist/transport/http.js.map +1 -1
- package/dist/utils/logger.d.ts +15 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/sql-validator.js +39 -10
- package/dist/utils/sql-validator.js.map +1 -1
- package/package.json +15 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAErF,MAAM,UAAU,aAAa,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { ConnectorManager } from "../../connectors/manager.js";
|
|
3
|
+
import type { TransactionHandle } from "../../connectors/interface.js";
|
|
4
|
+
interface TransactionEntry {
|
|
5
|
+
handle: TransactionHandle;
|
|
6
|
+
database: string;
|
|
7
|
+
timer: ReturnType<typeof setTimeout>;
|
|
8
|
+
}
|
|
9
|
+
export declare class TransactionStore {
|
|
10
|
+
private entries;
|
|
11
|
+
private ttlMs;
|
|
12
|
+
constructor(ttlMs?: number);
|
|
13
|
+
add(handle: TransactionHandle, database: string): void;
|
|
14
|
+
get(id: string): TransactionEntry | undefined;
|
|
15
|
+
remove(id: string): void;
|
|
16
|
+
cleanupAll(): Promise<void>;
|
|
17
|
+
private autoRollback;
|
|
18
|
+
}
|
|
19
|
+
export declare const transactionStore: TransactionStore;
|
|
3
20
|
export declare function createTransactionParams(dbIds: string[]): {
|
|
4
21
|
database: z.ZodEnum<{
|
|
5
22
|
[x: string]: string;
|
|
@@ -26,3 +43,4 @@ export declare function transactionHandler(manager: ConnectorManager): (params:
|
|
|
26
43
|
text: string;
|
|
27
44
|
}[];
|
|
28
45
|
}>;
|
|
46
|
+
export {};
|
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatSuccess, formatError } from "../../utils/response.js";
|
|
3
|
-
|
|
3
|
+
import { getLogger } from "../../utils/logger.js";
|
|
4
|
+
const DEFAULT_TX_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
5
|
+
export class TransactionStore {
|
|
6
|
+
entries = new Map();
|
|
7
|
+
ttlMs;
|
|
8
|
+
constructor(ttlMs = DEFAULT_TX_TTL_MS) {
|
|
9
|
+
this.ttlMs = ttlMs;
|
|
10
|
+
}
|
|
11
|
+
add(handle, database) {
|
|
12
|
+
const timer = setTimeout(() => {
|
|
13
|
+
this.autoRollback(handle.id);
|
|
14
|
+
}, this.ttlMs);
|
|
15
|
+
timer.unref();
|
|
16
|
+
this.entries.set(handle.id, { handle, database, timer });
|
|
17
|
+
}
|
|
18
|
+
get(id) {
|
|
19
|
+
return this.entries.get(id);
|
|
20
|
+
}
|
|
21
|
+
remove(id) {
|
|
22
|
+
const entry = this.entries.get(id);
|
|
23
|
+
if (entry) {
|
|
24
|
+
clearTimeout(entry.timer);
|
|
25
|
+
this.entries.delete(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async cleanupAll() {
|
|
29
|
+
const logger = getLogger();
|
|
30
|
+
const ids = Array.from(this.entries.keys());
|
|
31
|
+
for (const id of ids) {
|
|
32
|
+
await this.autoRollback(id);
|
|
33
|
+
}
|
|
34
|
+
logger.info("All transactions cleaned up", { count: ids.length });
|
|
35
|
+
}
|
|
36
|
+
async autoRollback(id) {
|
|
37
|
+
const entry = this.entries.get(id);
|
|
38
|
+
if (!entry)
|
|
39
|
+
return;
|
|
40
|
+
const logger = getLogger();
|
|
41
|
+
try {
|
|
42
|
+
await entry.handle.rollback();
|
|
43
|
+
logger.warn("Transaction auto-rolled back due to TTL expiry", {
|
|
44
|
+
transactionId: id,
|
|
45
|
+
database: entry.database,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
logger.error("Failed to auto-rollback transaction", {
|
|
50
|
+
transactionId: id,
|
|
51
|
+
error: String(err),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
clearTimeout(entry.timer);
|
|
56
|
+
this.entries.delete(id);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export const transactionStore = new TransactionStore();
|
|
4
61
|
export function createTransactionParams(dbIds) {
|
|
5
62
|
return {
|
|
6
63
|
database: z.enum(dbIds).describe(`Database ID. Available: ${dbIds.join(", ")}`),
|
|
@@ -17,7 +74,7 @@ export function transactionHandler(manager) {
|
|
|
17
74
|
case "begin": {
|
|
18
75
|
const connector = await manager.getConnector(params.database);
|
|
19
76
|
const tx = await connector.beginTransaction();
|
|
20
|
-
|
|
77
|
+
transactionStore.add(tx, params.database);
|
|
21
78
|
return formatSuccess({
|
|
22
79
|
data: { transactionId: tx.id, message: "Transaction started" },
|
|
23
80
|
database: params.database,
|
|
@@ -28,10 +85,10 @@ export function transactionHandler(manager) {
|
|
|
28
85
|
return formatError("transactionId is required for execute");
|
|
29
86
|
if (!params.statement)
|
|
30
87
|
return formatError("statement is required for execute");
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
88
|
+
const entry = transactionStore.get(params.transactionId);
|
|
89
|
+
if (!entry)
|
|
33
90
|
return formatError(`Transaction not found: ${params.transactionId}`, "TX_NOT_FOUND");
|
|
34
|
-
const result = await
|
|
91
|
+
const result = await entry.handle.execute(params.statement, params.params);
|
|
35
92
|
return formatSuccess({
|
|
36
93
|
rows: result.rows,
|
|
37
94
|
count: result.rowCount,
|
|
@@ -41,11 +98,11 @@ export function transactionHandler(manager) {
|
|
|
41
98
|
case "commit": {
|
|
42
99
|
if (!params.transactionId)
|
|
43
100
|
return formatError("transactionId is required for commit");
|
|
44
|
-
const
|
|
45
|
-
if (!
|
|
101
|
+
const entry = transactionStore.get(params.transactionId);
|
|
102
|
+
if (!entry)
|
|
46
103
|
return formatError(`Transaction not found: ${params.transactionId}`, "TX_NOT_FOUND");
|
|
47
|
-
await
|
|
48
|
-
|
|
104
|
+
await entry.handle.commit();
|
|
105
|
+
transactionStore.remove(params.transactionId);
|
|
49
106
|
return formatSuccess({
|
|
50
107
|
data: { message: "Transaction committed" },
|
|
51
108
|
database: params.database,
|
|
@@ -54,11 +111,11 @@ export function transactionHandler(manager) {
|
|
|
54
111
|
case "rollback": {
|
|
55
112
|
if (!params.transactionId)
|
|
56
113
|
return formatError("transactionId is required for rollback");
|
|
57
|
-
const
|
|
58
|
-
if (!
|
|
114
|
+
const entry = transactionStore.get(params.transactionId);
|
|
115
|
+
if (!entry)
|
|
59
116
|
return formatError(`Transaction not found: ${params.transactionId}`, "TX_NOT_FOUND");
|
|
60
|
-
await
|
|
61
|
-
|
|
117
|
+
await entry.handle.rollback();
|
|
118
|
+
transactionStore.remove(params.transactionId);
|
|
62
119
|
return formatSuccess({
|
|
63
120
|
data: { message: "Transaction rolled back" },
|
|
64
121
|
database: params.database,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../src/tools/write/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../src/tools/write/transaction.ts"],"names":[],"mappings":"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"}
|
package/dist/transport/http.d.ts
CHANGED
|
@@ -6,10 +6,12 @@ import type { AppConfig, HttpTransportConfig } from "../config/types.js";
|
|
|
6
6
|
interface SessionEntry {
|
|
7
7
|
transport: StreamableHTTPServerTransport;
|
|
8
8
|
server: McpServer;
|
|
9
|
+
lastAccessedAt: number;
|
|
9
10
|
}
|
|
10
11
|
export declare function startHttpTransport(config: AppConfig, transportConfig: HttpTransportConfig): Promise<{
|
|
11
12
|
httpServer: Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>;
|
|
12
13
|
manager: ConnectorManager;
|
|
13
14
|
sessions: Map<string, SessionEntry>;
|
|
15
|
+
cleanupInterval: NodeJS.Timeout | undefined;
|
|
14
16
|
}>;
|
|
15
17
|
export {};
|
package/dist/transport/http.js
CHANGED
|
@@ -1,29 +1,47 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
1
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
3
3
|
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
4
4
|
import { createConnectorManager, createMcpServerInstance } from "../server.js";
|
|
5
|
+
import { getLogger } from "../utils/logger.js";
|
|
5
6
|
export async function startHttpTransport(config, transportConfig) {
|
|
7
|
+
const logger = getLogger();
|
|
6
8
|
const manager = createConnectorManager(config);
|
|
7
9
|
await manager.connectEager();
|
|
8
10
|
const sessions = new Map();
|
|
9
11
|
const { host, port } = transportConfig;
|
|
10
12
|
const app = createMcpExpressApp({ host });
|
|
11
13
|
if (transportConfig.auth) {
|
|
12
|
-
const
|
|
14
|
+
const expectedValue = `Bearer ${transportConfig.auth.token}`;
|
|
15
|
+
const expectedBuf = Buffer.from(expectedValue);
|
|
13
16
|
app.use("/mcp", (req, res, next) => {
|
|
14
|
-
const authHeader = req.headers.authorization;
|
|
15
|
-
|
|
17
|
+
const authHeader = req.headers.authorization ?? "";
|
|
18
|
+
const actualBuf = Buffer.from(authHeader);
|
|
19
|
+
if (actualBuf.length !== expectedBuf.length || !timingSafeEqual(actualBuf, expectedBuf)) {
|
|
16
20
|
res.status(401).json({ error: "Unauthorized" });
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
23
|
next();
|
|
20
24
|
});
|
|
21
25
|
}
|
|
26
|
+
let cleanupInterval;
|
|
22
27
|
if (transportConfig.stateless) {
|
|
23
28
|
setupStatelessRoutes(app, manager, config);
|
|
24
29
|
}
|
|
25
30
|
else {
|
|
26
31
|
setupStatefulRoutes(app, manager, config, sessions);
|
|
32
|
+
const sessionTimeout = transportConfig.sessionTimeout;
|
|
33
|
+
cleanupInterval = setInterval(() => {
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
for (const [sid, entry] of sessions) {
|
|
36
|
+
if (now - entry.lastAccessedAt > sessionTimeout) {
|
|
37
|
+
logger.info("Cleaning up expired session", { sessionId: sid });
|
|
38
|
+
entry.transport.close().catch(() => { });
|
|
39
|
+
entry.server.close().catch(() => { });
|
|
40
|
+
sessions.delete(sid);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, 60_000);
|
|
44
|
+
cleanupInterval.unref();
|
|
27
45
|
}
|
|
28
46
|
setupHealthEndpoint(app, manager, sessions);
|
|
29
47
|
const httpServer = await new Promise((resolve) => {
|
|
@@ -31,15 +49,10 @@ export async function startHttpTransport(config, transportConfig) {
|
|
|
31
49
|
resolve(server);
|
|
32
50
|
});
|
|
33
51
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
console.error("Mode: stateful (session-based)");
|
|
41
|
-
}
|
|
42
|
-
return { httpServer, manager, sessions };
|
|
52
|
+
logger.info(`HTTP server listening on http://${host}:${port}/mcp`);
|
|
53
|
+
logger.info(`Health check: http://${host}:${port}/health`);
|
|
54
|
+
logger.info(`Mode: ${transportConfig.stateless ? "stateless" : "stateful (session-based)"}`);
|
|
55
|
+
return { httpServer, manager, sessions, cleanupInterval };
|
|
43
56
|
}
|
|
44
57
|
function setupStatelessRoutes(app, manager, config) {
|
|
45
58
|
app.all("/mcp", async (req, res) => {
|
|
@@ -59,6 +72,7 @@ function setupStatefulRoutes(app, manager, config, sessions) {
|
|
|
59
72
|
const sessionId = req.headers["mcp-session-id"];
|
|
60
73
|
if (sessionId && sessions.has(sessionId)) {
|
|
61
74
|
const session = sessions.get(sessionId);
|
|
75
|
+
session.lastAccessedAt = Date.now();
|
|
62
76
|
await session.transport.handleRequest(req, res, req.body);
|
|
63
77
|
return;
|
|
64
78
|
}
|
|
@@ -71,7 +85,7 @@ function setupStatefulRoutes(app, manager, config, sessions) {
|
|
|
71
85
|
const transport = new StreamableHTTPServerTransport({
|
|
72
86
|
sessionIdGenerator: () => randomUUID(),
|
|
73
87
|
onsessioninitialized: (newSessionId) => {
|
|
74
|
-
sessions.set(newSessionId, { transport, server });
|
|
88
|
+
sessions.set(newSessionId, { transport, server, lastAccessedAt: Date.now() });
|
|
75
89
|
},
|
|
76
90
|
});
|
|
77
91
|
transport.onclose = () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
2
|
+
export declare class Logger {
|
|
3
|
+
private level;
|
|
4
|
+
private context;
|
|
5
|
+
constructor(level?: LogLevel, context?: Record<string, unknown>);
|
|
6
|
+
child(context: Record<string, unknown>): Logger;
|
|
7
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
8
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
9
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
10
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
11
|
+
private log;
|
|
12
|
+
}
|
|
13
|
+
export declare function initLogger(level: LogLevel): Logger;
|
|
14
|
+
export declare function getLogger(): Logger;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const LOG_LEVELS = {
|
|
2
|
+
debug: 0,
|
|
3
|
+
info: 1,
|
|
4
|
+
warn: 2,
|
|
5
|
+
error: 3,
|
|
6
|
+
};
|
|
7
|
+
export class Logger {
|
|
8
|
+
level;
|
|
9
|
+
context;
|
|
10
|
+
constructor(level = "info", context = {}) {
|
|
11
|
+
this.level = LOG_LEVELS[level];
|
|
12
|
+
this.context = context;
|
|
13
|
+
}
|
|
14
|
+
child(context) {
|
|
15
|
+
const child = new Logger("debug", { ...this.context, ...context });
|
|
16
|
+
child.level = this.level;
|
|
17
|
+
return child;
|
|
18
|
+
}
|
|
19
|
+
debug(message, data) {
|
|
20
|
+
this.log("debug", message, data);
|
|
21
|
+
}
|
|
22
|
+
info(message, data) {
|
|
23
|
+
this.log("info", message, data);
|
|
24
|
+
}
|
|
25
|
+
warn(message, data) {
|
|
26
|
+
this.log("warn", message, data);
|
|
27
|
+
}
|
|
28
|
+
error(message, data) {
|
|
29
|
+
this.log("error", message, data);
|
|
30
|
+
}
|
|
31
|
+
log(level, message, data) {
|
|
32
|
+
if (LOG_LEVELS[level] < this.level)
|
|
33
|
+
return;
|
|
34
|
+
const entry = {
|
|
35
|
+
level,
|
|
36
|
+
message,
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
...this.context,
|
|
39
|
+
...data,
|
|
40
|
+
};
|
|
41
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let globalLogger = new Logger("info");
|
|
45
|
+
export function initLogger(level) {
|
|
46
|
+
globalLogger = new Logger(level);
|
|
47
|
+
return globalLogger;
|
|
48
|
+
}
|
|
49
|
+
export function getLogger() {
|
|
50
|
+
return globalLogger;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAA6B;IAC3C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AASF,MAAM,OAAO,MAAM;IACT,KAAK,CAAS;IACd,OAAO,CAA0B;IAEzC,YAAY,QAAkB,MAAM,EAAE,UAAmC,EAAE;QACzE,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAgC;QACpC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;QACnE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAEO,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;QAC1E,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK;YAAE,OAAO;QAE3C,MAAM,KAAK,GAAa;YACtB,KAAK;YACL,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,GAAG,IAAI,CAAC,OAAO;YACf,GAAG,IAAI;SACR,CAAC;QAEF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACrD,CAAC;CACF;AAED,IAAI,YAAY,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;AAEtC,MAAM,UAAU,UAAU,CAAC,KAAe;IACxC,YAAY,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,YAAY,CAAC;AACtB,CAAC"}
|
|
@@ -10,25 +10,54 @@ const WRITE_KEYWORDS = [
|
|
|
10
10
|
"REVOKE",
|
|
11
11
|
"REPLACE",
|
|
12
12
|
"MERGE",
|
|
13
|
+
"COPY",
|
|
14
|
+
"CALL",
|
|
13
15
|
];
|
|
14
|
-
const WRITE_PATTERN = new RegExp(
|
|
15
|
-
|
|
16
|
+
const WRITE_PATTERN = new RegExp(`\\b(${WRITE_KEYWORDS.join("|")})\\b`, "i");
|
|
17
|
+
function stripStringLiterals(sql) {
|
|
18
|
+
// Replace single-quoted strings (handling escaped quotes)
|
|
19
|
+
let result = sql.replace(/'(?:[^'\\]|\\.)*'/g, "__STR__");
|
|
20
|
+
// Replace double-quoted identifiers
|
|
21
|
+
result = result.replace(/"(?:[^"\\]|\\.)*"/g, "__ID__");
|
|
22
|
+
// Replace dollar-quoted strings (PostgreSQL): $$...$$, $tag$...$tag$
|
|
23
|
+
result = result.replace(/\$([^$]*)\$[\s\S]*?\$\1\$/g, "__STR__");
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function stripComments(sql) {
|
|
27
|
+
// Remove block comments
|
|
28
|
+
let result = sql.replace(/\/\*[\s\S]*?\*\//g, " ");
|
|
29
|
+
// Remove line comments
|
|
30
|
+
result = result.replace(/--[^\n]*/g, " ");
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function normalizeWhitespace(sql) {
|
|
34
|
+
return sql.replace(/\s+/g, " ").trim();
|
|
35
|
+
}
|
|
16
36
|
export function validateReadonlySql(sql) {
|
|
17
37
|
const trimmed = sql.trim();
|
|
18
38
|
if (!trimmed) {
|
|
19
39
|
return { valid: false, error: "Empty SQL statement" };
|
|
20
40
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
// Normalize: strip strings first, then comments, then whitespace
|
|
42
|
+
const noStrings = stripStringLiterals(trimmed);
|
|
43
|
+
const noComments = stripComments(noStrings);
|
|
44
|
+
const normalized = normalizeWhitespace(noComments);
|
|
45
|
+
// Check for multiple statements (after removing string literals)
|
|
46
|
+
if (normalized.includes(";")) {
|
|
47
|
+
const afterSemicolon = normalized.split(";").slice(1).join(";").trim();
|
|
48
|
+
if (afterSemicolon.length > 0) {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
error: "Multiple statements are not allowed in read-only mode",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
26
54
|
}
|
|
27
|
-
|
|
28
|
-
|
|
55
|
+
// Check for write keywords anywhere in normalized SQL
|
|
56
|
+
const match = normalized.match(WRITE_PATTERN);
|
|
57
|
+
if (match) {
|
|
29
58
|
return {
|
|
30
59
|
valid: false,
|
|
31
|
-
error: `Statement '${match
|
|
60
|
+
error: `Statement '${match[1].toUpperCase()}' is not allowed in read-only mode`,
|
|
32
61
|
};
|
|
33
62
|
}
|
|
34
63
|
return { valid: true };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-validator.js","sourceRoot":"","sources":["../../src/utils/sql-validator.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG;IACrB,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO;
|
|
1
|
+
{"version":3,"file":"sql-validator.js","sourceRoot":"","sources":["../../src/utils/sql-validator.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG;IACrB,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO;IACP,MAAM;IACN,MAAM;CACP,CAAC;AAEF,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAO7E,SAAS,mBAAmB,CAAC,GAAW;IACtC,0DAA0D;IAC1D,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;IAC1D,oCAAoC;IACpC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;IACxD,qEAAqE;IACrE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,4BAA4B,EAAE,SAAS,CAAC,CAAC;IACjE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,wBAAwB;IACxB,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;IACnD,uBAAuB;IACvB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAE3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;IACxD,CAAC;IAED,iEAAiE;IACjE,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAEnD,iEAAiE;IACjE,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACvE,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,uDAAuD;aAC/D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sDAAsD;IACtD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC9C,IAAI,KAAK,EAAE,CAAC;QACV,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,oCAAoC;SAChF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conte777/db-view-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP server for database access (PostgreSQL + ClickHouse)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,6 +15,14 @@
|
|
|
15
15
|
"build": "tsc",
|
|
16
16
|
"dev": "tsx src/index.ts",
|
|
17
17
|
"start": "node dist/index.js",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"test:coverage": "vitest run --coverage",
|
|
21
|
+
"lint": "biome lint src/ tests/",
|
|
22
|
+
"lint:fix": "biome lint --write src/ tests/",
|
|
23
|
+
"format": "biome format --write src/ tests/",
|
|
24
|
+
"format:check": "biome format src/ tests/",
|
|
25
|
+
"prepare": "lefthook install",
|
|
18
26
|
"prepublishOnly": "npm run build"
|
|
19
27
|
},
|
|
20
28
|
"keywords": [
|
|
@@ -32,10 +40,14 @@
|
|
|
32
40
|
"zod": "^4.3.6"
|
|
33
41
|
},
|
|
34
42
|
"devDependencies": {
|
|
43
|
+
"@biomejs/biome": "^2.4.2",
|
|
35
44
|
"@types/express": "^5.0.3",
|
|
36
45
|
"@types/node": "^25.2.3",
|
|
37
46
|
"@types/pg": "^8.16.0",
|
|
47
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
48
|
+
"lefthook": "^2.1.1",
|
|
38
49
|
"tsx": "^4.21.0",
|
|
39
|
-
"typescript": "^5.9.3"
|
|
50
|
+
"typescript": "^5.9.3",
|
|
51
|
+
"vitest": "^3.1.1"
|
|
40
52
|
}
|
|
41
|
-
}
|
|
53
|
+
}
|