@conte777/db-view-mcp 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/LICENSE +21 -0
- package/README.md +341 -0
- package/config.example.json +41 -0
- package/dist/config/loader.d.ts +6 -0
- package/dist/config/loader.js +28 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +149 -0
- package/dist/config/types.js +73 -0
- package/dist/config/types.js.map +1 -0
- package/dist/connectors/clickhouse.d.ts +20 -0
- package/dist/connectors/clickhouse.js +95 -0
- package/dist/connectors/clickhouse.js.map +1 -0
- package/dist/connectors/interface.d.ts +37 -0
- package/dist/connectors/interface.js +2 -0
- package/dist/connectors/interface.js.map +1 -0
- package/dist/connectors/manager.d.ts +14 -0
- package/dist/connectors/manager.js +51 -0
- package/dist/connectors/manager.js.map +1 -0
- package/dist/connectors/postgresql.d.ts +20 -0
- package/dist/connectors/postgresql.js +138 -0
- package/dist/connectors/postgresql.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +23 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/readonly/describe-table.d.ts +19 -0
- package/dist/tools/readonly/describe-table.js +22 -0
- package/dist/tools/readonly/describe-table.js.map +1 -0
- package/dist/tools/readonly/explain.d.ts +17 -0
- package/dist/tools/readonly/explain.js +26 -0
- package/dist/tools/readonly/explain.js.map +1 -0
- package/dist/tools/readonly/list-databases.d.ts +7 -0
- package/dist/tools/readonly/list-databases.js +12 -0
- package/dist/tools/readonly/list-databases.js.map +1 -0
- package/dist/tools/readonly/list-tables.d.ts +17 -0
- package/dist/tools/readonly/list-tables.js +21 -0
- package/dist/tools/readonly/list-tables.js.map +1 -0
- package/dist/tools/readonly/performance.d.ts +43 -0
- package/dist/tools/readonly/performance.js +72 -0
- package/dist/tools/readonly/performance.js.map +1 -0
- package/dist/tools/readonly/query.d.ts +19 -0
- package/dist/tools/readonly/query.js +27 -0
- package/dist/tools/readonly/query.js.map +1 -0
- package/dist/tools/readonly/schema.d.ts +15 -0
- package/dist/tools/readonly/schema.js +20 -0
- package/dist/tools/readonly/schema.js.map +1 -0
- package/dist/tools/registry.d.ts +4 -0
- package/dist/tools/registry.js +56 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/write/execute.d.ts +19 -0
- package/dist/tools/write/execute.js +26 -0
- package/dist/tools/write/execute.js.map +1 -0
- package/dist/tools/write/transaction.d.ts +28 -0
- package/dist/tools/write/transaction.js +76 -0
- package/dist/tools/write/transaction.js.map +1 -0
- package/dist/transport/http.d.ts +15 -0
- package/dist/transport/http.js +96 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/utils/response.d.ts +31 -0
- package/dist/utils/response.js +25 -0
- package/dist/utils/response.js.map +1 -0
- package/dist/utils/sql-validator.d.ts +5 -0
- package/dist/utils/sql-validator.js +36 -0
- package/dist/utils/sql-validator.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Connector, QueryResult, TableInfo, ColumnInfo, ExplainResult, TransactionHandle } from "./interface.js";
|
|
2
|
+
import type { ClickHouseConfig } from "../config/types.js";
|
|
3
|
+
export declare class ClickHouseConnector implements Connector {
|
|
4
|
+
readonly type: "clickhouse";
|
|
5
|
+
private client;
|
|
6
|
+
private config;
|
|
7
|
+
private maxRows;
|
|
8
|
+
private queryTimeout;
|
|
9
|
+
constructor(config: ClickHouseConfig, queryTimeout: number, maxRows: number);
|
|
10
|
+
connect(): Promise<void>;
|
|
11
|
+
disconnect(): Promise<void>;
|
|
12
|
+
private getClient;
|
|
13
|
+
query(sql: string, _params?: string[], maxRows?: number): Promise<QueryResult>;
|
|
14
|
+
execute(sql: string, _params?: string[]): Promise<QueryResult>;
|
|
15
|
+
listTables(_schema?: string): Promise<TableInfo[]>;
|
|
16
|
+
describeTable(table: string, _schema?: string): Promise<ColumnInfo[]>;
|
|
17
|
+
getSchema(): Promise<string>;
|
|
18
|
+
explain(sql: string): Promise<ExplainResult>;
|
|
19
|
+
beginTransaction(): Promise<TransactionHandle>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createClient } from "@clickhouse/client";
|
|
2
|
+
export class ClickHouseConnector {
|
|
3
|
+
type = "clickhouse";
|
|
4
|
+
client = null;
|
|
5
|
+
config;
|
|
6
|
+
maxRows;
|
|
7
|
+
queryTimeout;
|
|
8
|
+
constructor(config, queryTimeout, maxRows) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.queryTimeout = queryTimeout;
|
|
11
|
+
this.maxRows = maxRows;
|
|
12
|
+
}
|
|
13
|
+
async connect() {
|
|
14
|
+
this.client = createClient({
|
|
15
|
+
url: this.config.url,
|
|
16
|
+
database: this.config.database,
|
|
17
|
+
username: this.config.user,
|
|
18
|
+
password: this.config.password,
|
|
19
|
+
request_timeout: this.queryTimeout,
|
|
20
|
+
});
|
|
21
|
+
await this.client.ping();
|
|
22
|
+
}
|
|
23
|
+
async disconnect() {
|
|
24
|
+
if (this.client) {
|
|
25
|
+
await this.client.close();
|
|
26
|
+
this.client = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
getClient() {
|
|
30
|
+
if (!this.client)
|
|
31
|
+
throw new Error("Not connected");
|
|
32
|
+
return this.client;
|
|
33
|
+
}
|
|
34
|
+
async query(sql, _params, maxRows) {
|
|
35
|
+
const limit = maxRows ?? this.maxRows;
|
|
36
|
+
const wrappedSql = `SELECT * FROM (${sql}) AS _q LIMIT ${limit}`;
|
|
37
|
+
const result = await this.getClient().query({ query: wrappedSql, format: "JSONEachRow" });
|
|
38
|
+
const rows = (await result.json());
|
|
39
|
+
return { rows, rowCount: rows.length };
|
|
40
|
+
}
|
|
41
|
+
async execute(sql, _params) {
|
|
42
|
+
await this.getClient().command({ query: sql });
|
|
43
|
+
return { rows: [], rowCount: 0 };
|
|
44
|
+
}
|
|
45
|
+
async listTables(_schema) {
|
|
46
|
+
const result = await this.getClient().query({
|
|
47
|
+
query: `SELECT name, engine FROM system.tables WHERE database = currentDatabase() ORDER BY name`,
|
|
48
|
+
format: "JSONEachRow",
|
|
49
|
+
});
|
|
50
|
+
const rows = (await result.json());
|
|
51
|
+
return rows.map((r) => ({
|
|
52
|
+
schema: this.config.database,
|
|
53
|
+
name: r.name,
|
|
54
|
+
type: r.engine.includes("View") ? "view" : "table",
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
async describeTable(table, _schema) {
|
|
58
|
+
const result = await this.getClient().query({
|
|
59
|
+
query: `SELECT name, type, default_kind, default_expression, is_in_primary_key
|
|
60
|
+
FROM system.columns
|
|
61
|
+
WHERE database = currentDatabase() AND table = {table:String}
|
|
62
|
+
ORDER BY position`,
|
|
63
|
+
format: "JSONEachRow",
|
|
64
|
+
query_params: { table },
|
|
65
|
+
});
|
|
66
|
+
const rows = (await result.json());
|
|
67
|
+
return rows.map((r) => ({
|
|
68
|
+
name: r.name,
|
|
69
|
+
type: r.type,
|
|
70
|
+
nullable: r.type.startsWith("Nullable"),
|
|
71
|
+
defaultValue: r.default_expression || null,
|
|
72
|
+
isPrimaryKey: r.is_in_primary_key === 1,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
async getSchema() {
|
|
76
|
+
const result = await this.getClient().query({
|
|
77
|
+
query: `SELECT name, create_table_query FROM system.tables WHERE database = currentDatabase()`,
|
|
78
|
+
format: "JSONEachRow",
|
|
79
|
+
});
|
|
80
|
+
const rows = (await result.json());
|
|
81
|
+
return rows.map((r) => r.create_table_query).join(";\n\n");
|
|
82
|
+
}
|
|
83
|
+
async explain(sql) {
|
|
84
|
+
const result = await this.getClient().query({
|
|
85
|
+
query: `EXPLAIN ${sql}`,
|
|
86
|
+
format: "JSONEachRow",
|
|
87
|
+
});
|
|
88
|
+
const rows = (await result.json());
|
|
89
|
+
return { plan: rows.map((r) => r.explain).join("\n") };
|
|
90
|
+
}
|
|
91
|
+
async beginTransaction() {
|
|
92
|
+
throw new Error("Transactions are not supported in ClickHouse");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=clickhouse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickhouse.js","sourceRoot":"","sources":["../../src/connectors/clickhouse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface QueryResult {
|
|
2
|
+
rows: Record<string, unknown>[];
|
|
3
|
+
rowCount: number;
|
|
4
|
+
}
|
|
5
|
+
export interface TableInfo {
|
|
6
|
+
schema: string;
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ColumnInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
nullable: boolean;
|
|
14
|
+
defaultValue: string | null;
|
|
15
|
+
isPrimaryKey: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ExplainResult {
|
|
18
|
+
plan: string;
|
|
19
|
+
}
|
|
20
|
+
export interface TransactionHandle {
|
|
21
|
+
id: string;
|
|
22
|
+
execute(sql: string, params?: string[]): Promise<QueryResult>;
|
|
23
|
+
commit(): Promise<void>;
|
|
24
|
+
rollback(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export interface Connector {
|
|
27
|
+
readonly type: "postgresql" | "clickhouse";
|
|
28
|
+
connect(): Promise<void>;
|
|
29
|
+
disconnect(): Promise<void>;
|
|
30
|
+
query(sql: string, params?: string[], maxRows?: number): Promise<QueryResult>;
|
|
31
|
+
execute(sql: string, params?: string[]): Promise<QueryResult>;
|
|
32
|
+
listTables(schema?: string): Promise<TableInfo[]>;
|
|
33
|
+
describeTable(table: string, schema?: string): Promise<ColumnInfo[]>;
|
|
34
|
+
getSchema(): Promise<string>;
|
|
35
|
+
explain(sql: string): Promise<ExplainResult>;
|
|
36
|
+
beginTransaction(): Promise<TransactionHandle>;
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/connectors/interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Connector } from "./interface.js";
|
|
2
|
+
import type { ResolvedDatabaseConfig } from "../config/types.js";
|
|
3
|
+
export declare class ConnectorManager {
|
|
4
|
+
private configs;
|
|
5
|
+
private connectors;
|
|
6
|
+
constructor(databases: ResolvedDatabaseConfig[]);
|
|
7
|
+
getDatabaseIds(): string[];
|
|
8
|
+
getConfig(dbId: string): ResolvedDatabaseConfig | undefined;
|
|
9
|
+
getAllConfigs(): ResolvedDatabaseConfig[];
|
|
10
|
+
getConnector(dbId: string): Promise<Connector>;
|
|
11
|
+
private createConnector;
|
|
12
|
+
connectEager(): Promise<void>;
|
|
13
|
+
disconnectAll(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { PostgresConnector } from "./postgresql.js";
|
|
2
|
+
import { ClickHouseConnector } from "./clickhouse.js";
|
|
3
|
+
export class ConnectorManager {
|
|
4
|
+
configs = new Map();
|
|
5
|
+
connectors = new Map();
|
|
6
|
+
constructor(databases) {
|
|
7
|
+
for (const db of databases) {
|
|
8
|
+
this.configs.set(db.id, db);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
getDatabaseIds() {
|
|
12
|
+
return Array.from(this.configs.keys());
|
|
13
|
+
}
|
|
14
|
+
getConfig(dbId) {
|
|
15
|
+
return this.configs.get(dbId);
|
|
16
|
+
}
|
|
17
|
+
getAllConfigs() {
|
|
18
|
+
return Array.from(this.configs.values());
|
|
19
|
+
}
|
|
20
|
+
async getConnector(dbId) {
|
|
21
|
+
const existing = this.connectors.get(dbId);
|
|
22
|
+
if (existing)
|
|
23
|
+
return existing;
|
|
24
|
+
const config = this.configs.get(dbId);
|
|
25
|
+
if (!config)
|
|
26
|
+
throw new Error(`Unknown database: ${dbId}`);
|
|
27
|
+
const connector = this.createConnector(config);
|
|
28
|
+
await connector.connect();
|
|
29
|
+
this.connectors.set(dbId, connector);
|
|
30
|
+
return connector;
|
|
31
|
+
}
|
|
32
|
+
createConnector(config) {
|
|
33
|
+
if (config.type === "postgresql") {
|
|
34
|
+
return new PostgresConnector(config, config.queryTimeout, config.maxRows);
|
|
35
|
+
}
|
|
36
|
+
if (config.type === "clickhouse") {
|
|
37
|
+
return new ClickHouseConnector(config, config.queryTimeout, config.maxRows);
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`Unsupported database type: ${config.type}`);
|
|
40
|
+
}
|
|
41
|
+
async connectEager() {
|
|
42
|
+
const eagerDbs = Array.from(this.configs.values()).filter((c) => !c.lazyConnection);
|
|
43
|
+
await Promise.all(eagerDbs.map((db) => this.getConnector(db.id)));
|
|
44
|
+
}
|
|
45
|
+
async disconnectAll() {
|
|
46
|
+
const tasks = Array.from(this.connectors.values()).map((c) => c.disconnect());
|
|
47
|
+
await Promise.all(tasks);
|
|
48
|
+
this.connectors.clear();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/connectors/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Connector, QueryResult, TableInfo, ColumnInfo, ExplainResult, TransactionHandle } from "./interface.js";
|
|
2
|
+
import type { PostgresConfig } from "../config/types.js";
|
|
3
|
+
export declare class PostgresConnector implements Connector {
|
|
4
|
+
readonly type: "postgresql";
|
|
5
|
+
private pool;
|
|
6
|
+
private config;
|
|
7
|
+
private queryTimeout;
|
|
8
|
+
private maxRows;
|
|
9
|
+
constructor(config: PostgresConfig, queryTimeout: number, maxRows: number);
|
|
10
|
+
connect(): Promise<void>;
|
|
11
|
+
disconnect(): Promise<void>;
|
|
12
|
+
private getPool;
|
|
13
|
+
query(sql: string, params?: string[], maxRows?: number): Promise<QueryResult>;
|
|
14
|
+
execute(sql: string, params?: string[]): Promise<QueryResult>;
|
|
15
|
+
listTables(schema?: string): Promise<TableInfo[]>;
|
|
16
|
+
describeTable(table: string, schema?: string): Promise<ColumnInfo[]>;
|
|
17
|
+
getSchema(): Promise<string>;
|
|
18
|
+
explain(sql: string): Promise<ExplainResult>;
|
|
19
|
+
beginTransaction(): Promise<TransactionHandle>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
export class PostgresConnector {
|
|
4
|
+
type = "postgresql";
|
|
5
|
+
pool = null;
|
|
6
|
+
config;
|
|
7
|
+
queryTimeout;
|
|
8
|
+
maxRows;
|
|
9
|
+
constructor(config, queryTimeout, maxRows) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.queryTimeout = queryTimeout;
|
|
12
|
+
this.maxRows = maxRows;
|
|
13
|
+
}
|
|
14
|
+
async connect() {
|
|
15
|
+
this.pool = new pg.Pool({
|
|
16
|
+
host: this.config.host,
|
|
17
|
+
port: this.config.port,
|
|
18
|
+
database: this.config.database,
|
|
19
|
+
user: this.config.user,
|
|
20
|
+
password: this.config.password,
|
|
21
|
+
ssl: this.config.ssl ? { rejectUnauthorized: false } : undefined,
|
|
22
|
+
max: 10,
|
|
23
|
+
});
|
|
24
|
+
// Verify connection
|
|
25
|
+
const client = await this.pool.connect();
|
|
26
|
+
client.release();
|
|
27
|
+
}
|
|
28
|
+
async disconnect() {
|
|
29
|
+
if (this.pool) {
|
|
30
|
+
await this.pool.end();
|
|
31
|
+
this.pool = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
getPool() {
|
|
35
|
+
if (!this.pool)
|
|
36
|
+
throw new Error("Not connected");
|
|
37
|
+
return this.pool;
|
|
38
|
+
}
|
|
39
|
+
async query(sql, params, maxRows) {
|
|
40
|
+
const limit = maxRows ?? this.maxRows;
|
|
41
|
+
const wrappedSql = `SELECT * FROM (${sql}) AS _q LIMIT ${limit}`;
|
|
42
|
+
const result = await this.getPool().query(wrappedSql, params);
|
|
43
|
+
return { rows: result.rows, rowCount: result.rows.length };
|
|
44
|
+
}
|
|
45
|
+
async execute(sql, params) {
|
|
46
|
+
const result = await this.getPool().query(sql, params);
|
|
47
|
+
return { rows: result.rows ?? [], rowCount: result.rowCount ?? 0 };
|
|
48
|
+
}
|
|
49
|
+
async listTables(schema) {
|
|
50
|
+
const s = schema ?? "public";
|
|
51
|
+
const result = await this.getPool().query(`SELECT table_schema, table_name, table_type
|
|
52
|
+
FROM information_schema.tables
|
|
53
|
+
WHERE table_schema = $1
|
|
54
|
+
ORDER BY table_name`, [s]);
|
|
55
|
+
return result.rows.map((r) => ({
|
|
56
|
+
schema: r.table_schema,
|
|
57
|
+
name: r.table_name,
|
|
58
|
+
type: r.table_type === "BASE TABLE" ? "table" : "view",
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
async describeTable(table, schema) {
|
|
62
|
+
const s = schema ?? "public";
|
|
63
|
+
const result = await this.getPool().query(`SELECT
|
|
64
|
+
c.column_name,
|
|
65
|
+
c.data_type,
|
|
66
|
+
c.is_nullable,
|
|
67
|
+
c.column_default,
|
|
68
|
+
CASE WHEN pk.column_name IS NOT NULL THEN true ELSE false END as is_primary_key
|
|
69
|
+
FROM information_schema.columns c
|
|
70
|
+
LEFT JOIN (
|
|
71
|
+
SELECT ku.column_name
|
|
72
|
+
FROM information_schema.table_constraints tc
|
|
73
|
+
JOIN information_schema.key_column_usage ku
|
|
74
|
+
ON tc.constraint_name = ku.constraint_name
|
|
75
|
+
AND tc.table_schema = ku.table_schema
|
|
76
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
77
|
+
AND tc.table_name = $1
|
|
78
|
+
AND tc.table_schema = $2
|
|
79
|
+
) pk ON c.column_name = pk.column_name
|
|
80
|
+
WHERE c.table_name = $1 AND c.table_schema = $2
|
|
81
|
+
ORDER BY c.ordinal_position`, [table, s]);
|
|
82
|
+
return result.rows.map((r) => ({
|
|
83
|
+
name: r.column_name,
|
|
84
|
+
type: r.data_type,
|
|
85
|
+
nullable: r.is_nullable === "YES",
|
|
86
|
+
defaultValue: r.column_default,
|
|
87
|
+
isPrimaryKey: r.is_primary_key,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
async getSchema() {
|
|
91
|
+
const result = await this.getPool().query(`SELECT table_name, column_name, data_type, is_nullable, column_default
|
|
92
|
+
FROM information_schema.columns
|
|
93
|
+
WHERE table_schema = 'public'
|
|
94
|
+
ORDER BY table_name, ordinal_position`);
|
|
95
|
+
const tables = new Map();
|
|
96
|
+
for (const row of result.rows) {
|
|
97
|
+
const t = row.table_name;
|
|
98
|
+
if (!tables.has(t))
|
|
99
|
+
tables.set(t, []);
|
|
100
|
+
const nullable = row.is_nullable === "YES" ? " NULL" : " NOT NULL";
|
|
101
|
+
const def = row.column_default ? ` DEFAULT ${row.column_default}` : "";
|
|
102
|
+
tables.get(t).push(` ${row.column_name} ${row.data_type}${nullable}${def}`);
|
|
103
|
+
}
|
|
104
|
+
const lines = [];
|
|
105
|
+
for (const [table, cols] of tables) {
|
|
106
|
+
lines.push(`CREATE TABLE ${table} (`);
|
|
107
|
+
lines.push(cols.join(",\n"));
|
|
108
|
+
lines.push(`);\n`);
|
|
109
|
+
}
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
}
|
|
112
|
+
async explain(sql) {
|
|
113
|
+
const result = await this.getPool().query(`EXPLAIN ANALYZE ${sql}`);
|
|
114
|
+
const plan = result.rows.map((r) => r["QUERY PLAN"]).join("\n");
|
|
115
|
+
return { plan };
|
|
116
|
+
}
|
|
117
|
+
async beginTransaction() {
|
|
118
|
+
const client = await this.getPool().connect();
|
|
119
|
+
await client.query("BEGIN");
|
|
120
|
+
const id = randomUUID();
|
|
121
|
+
return {
|
|
122
|
+
id,
|
|
123
|
+
async execute(sql, params) {
|
|
124
|
+
const result = await client.query(sql, params);
|
|
125
|
+
return { rows: result.rows ?? [], rowCount: result.rowCount ?? 0 };
|
|
126
|
+
},
|
|
127
|
+
async commit() {
|
|
128
|
+
await client.query("COMMIT");
|
|
129
|
+
client.release();
|
|
130
|
+
},
|
|
131
|
+
async rollback() {
|
|
132
|
+
await client.query("ROLLBACK");
|
|
133
|
+
client.release();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=postgresql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresql.js","sourceRoot":"","sources":["../../src/connectors/postgresql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWzC,MAAM,OAAO,iBAAiB;IACnB,IAAI,GAAG,YAAqB,CAAC;IAC9B,IAAI,GAAmB,IAAI,CAAC;IAC5B,MAAM,CAAiB;IACvB,YAAY,CAAS;IACrB,OAAO,CAAS;IAExB,YAAY,MAAsB,EAAE,YAAoB,EAAE,OAAe;QACvE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC;YACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YACtB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YACtB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;YAChE,GAAG,EAAE,EAAE;SACR,CAAC,CAAC;QACH,oBAAoB;QACpB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzC,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB,EAAE,OAAgB;QAC1D,MAAM,KAAK,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;QACtC,MAAM,UAAU,GAAG,kBAAkB,GAAG,iBAAiB,KAAK,EAAE,CAAC;QACjE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAAiB;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAe;QAC9B,MAAM,CAAC,GAAG,MAAM,IAAI,QAAQ,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CACvC;;;2BAGqB,EACrB,CAAC,CAAC,CAAC,CACJ,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAA0B,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM,EAAE,CAAC,CAAC,YAAsB;YAChC,IAAI,EAAE,CAAC,CAAC,UAAoB;YAC5B,IAAI,EAAG,CAAC,CAAC,UAAqB,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;SACnE,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,MAAe;QAChD,MAAM,CAAC,GAAG,MAAM,IAAI,QAAQ,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CACvC;;;;;;;;;;;;;;;;;;mCAkB6B,EAC7B,CAAC,KAAK,EAAE,CAAC,CAAC,CACX,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAA0B,EAAE,EAAE,CAAC,CAAC;YACtD,IAAI,EAAE,CAAC,CAAC,WAAqB;YAC7B,IAAI,EAAE,CAAC,CAAC,SAAmB;YAC3B,QAAQ,EAAG,CAAC,CAAC,WAAsB,KAAK,KAAK;YAC7C,YAAY,EAAE,CAAC,CAAC,cAA+B;YAC/C,YAAY,EAAE,CAAC,CAAC,cAAyB;SAC1C,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CACvC;;;6CAGuC,CACxC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,UAAoB,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtC,MAAM,QAAQ,GAAI,GAAG,CAAC,WAAsB,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC;YAC/E,MAAM,GAAG,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,SAAS,GAAG,QAAQ,GAAG,GAAG,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,gBAAgB,KAAK,IAAI,CAAC,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAA0B,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzF,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;QAC9C,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,OAAO;YACL,EAAE;YACF,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAAiB;gBAC1C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YACrE,CAAC;YACD,KAAK,CAAC,MAAM;gBACV,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC7B,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC;YACD,KAAK,CAAC,QAAQ;gBACZ,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAC/B,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { loadConfig, parseCliArgs } from "./config/loader.js";
|
|
4
|
+
import { createServer } from "./server.js";
|
|
5
|
+
import { startHttpTransport } from "./transport/http.js";
|
|
6
|
+
async function main() {
|
|
7
|
+
const { configPath, transport: cliTransport } = parseCliArgs(process.argv.slice(2));
|
|
8
|
+
const config = loadConfig(configPath);
|
|
9
|
+
// CLI --transport overrides config
|
|
10
|
+
const transportType = cliTransport ?? config.transport.type;
|
|
11
|
+
if (transportType === "http") {
|
|
12
|
+
const transportConfig = config.transport.type === "http"
|
|
13
|
+
? config.transport
|
|
14
|
+
: { type: "http", port: 3000, host: "127.0.0.1", stateless: false };
|
|
15
|
+
const { httpServer, manager } = await startHttpTransport(config, transportConfig);
|
|
16
|
+
const shutdown = async () => {
|
|
17
|
+
console.error("Shutting down HTTP server...");
|
|
18
|
+
httpServer.close();
|
|
19
|
+
await manager.disconnectAll();
|
|
20
|
+
process.exit(0);
|
|
21
|
+
};
|
|
22
|
+
process.on("SIGINT", shutdown);
|
|
23
|
+
process.on("SIGTERM", shutdown);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const { server, manager } = await createServer(config);
|
|
27
|
+
const transport = new StdioServerTransport();
|
|
28
|
+
await server.connect(transport);
|
|
29
|
+
process.on("SIGINT", async () => {
|
|
30
|
+
await manager.disconnectAll();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
});
|
|
33
|
+
process.on("SIGTERM", async () => {
|
|
34
|
+
await manager.disconnectAll();
|
|
35
|
+
process.exit(0);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
main().catch((err) => {
|
|
40
|
+
console.error("Failed to start db-view-mcp:", err);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAEtC,mCAAmC;IACnC,MAAM,aAAa,GAAG,YAAY,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;IAE5D,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;QAC7B,MAAM,eAAe,GACnB,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM;YAC9B,CAAC,CAAC,MAAM,CAAC,SAAS;YAClB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAEjF,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAElF,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAC9C,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QAEvD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YAC/B,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { ConnectorManager } from "./connectors/manager.js";
|
|
3
|
+
import { type AppConfig } from "./config/types.js";
|
|
4
|
+
export declare function createConnectorManager(config: AppConfig): ConnectorManager;
|
|
5
|
+
export declare function createMcpServerInstance(manager: ConnectorManager, config: AppConfig): McpServer;
|
|
6
|
+
export declare function createServer(config: AppConfig): Promise<{
|
|
7
|
+
server: McpServer;
|
|
8
|
+
manager: ConnectorManager;
|
|
9
|
+
}>;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { ConnectorManager } from "./connectors/manager.js";
|
|
3
|
+
import { registerTools } from "./tools/registry.js";
|
|
4
|
+
import { resolveDbConfig } from "./config/types.js";
|
|
5
|
+
export function createConnectorManager(config) {
|
|
6
|
+
const resolvedDbs = config.databases.map((db) => resolveDbConfig(db, config.defaults));
|
|
7
|
+
return new ConnectorManager(resolvedDbs);
|
|
8
|
+
}
|
|
9
|
+
export function createMcpServerInstance(manager, config) {
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: "db-view-mcp",
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
});
|
|
14
|
+
registerTools(server, manager, config.defaults);
|
|
15
|
+
return server;
|
|
16
|
+
}
|
|
17
|
+
export async function createServer(config) {
|
|
18
|
+
const manager = createConnectorManager(config);
|
|
19
|
+
const server = createMcpServerInstance(manager, config);
|
|
20
|
+
await manager.connectEager();
|
|
21
|
+
return { server, manager };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAkB,MAAM,mBAAmB,CAAC;AAEpE,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAC9C,eAAe,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CACrC,CAAC;IACF,OAAO,IAAI,gBAAgB,CAAC,WAAW,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAAyB,EACzB,MAAiB;IAEjB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IACH,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAiB;IAClD,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,uBAAuB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAExD,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;IAE7B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ConnectorManager } from "../../connectors/manager.js";
|
|
3
|
+
export declare function createDescribeTableParams(dbIds: string[]): {
|
|
4
|
+
database: z.ZodEnum<{
|
|
5
|
+
[x: string]: string;
|
|
6
|
+
}>;
|
|
7
|
+
table: z.ZodString;
|
|
8
|
+
schema: z.ZodOptional<z.ZodString>;
|
|
9
|
+
};
|
|
10
|
+
export declare function describeTableHandler(manager: ConnectorManager): (params: {
|
|
11
|
+
database: string;
|
|
12
|
+
table: string;
|
|
13
|
+
schema?: string;
|
|
14
|
+
}) => Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatSuccess, formatError } from "../../utils/response.js";
|
|
3
|
+
export function createDescribeTableParams(dbIds) {
|
|
4
|
+
return {
|
|
5
|
+
database: z.enum(dbIds).describe(`Database ID. Available: ${dbIds.join(", ")}`),
|
|
6
|
+
table: z.string().describe("Table name"),
|
|
7
|
+
schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function describeTableHandler(manager) {
|
|
11
|
+
return async (params) => {
|
|
12
|
+
try {
|
|
13
|
+
const connector = await manager.getConnector(params.database);
|
|
14
|
+
const columns = await connector.describeTable(params.table, params.schema);
|
|
15
|
+
return formatSuccess({ data: columns, database: params.database });
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return formatError(String(err));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=describe-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe-table.js","sourceRoot":"","sources":["../../../src/tools/readonly/describe-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAErE,MAAM,UAAU,yBAAyB,CAAC,KAAe;IACvD,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,KAA8B,CAAC,CAAC,QAAQ,CAAC,2BAA2B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxG,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;QACxC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;KACvF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAyB;IAC5D,OAAO,KAAK,EAAE,MAA4D,EAAE,EAAE;QAC5E,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3E,OAAO,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ConnectorManager } from "../../connectors/manager.js";
|
|
3
|
+
export declare function createExplainParams(dbIds: string[]): {
|
|
4
|
+
database: z.ZodEnum<{
|
|
5
|
+
[x: string]: string;
|
|
6
|
+
}>;
|
|
7
|
+
sql: z.ZodString;
|
|
8
|
+
};
|
|
9
|
+
export declare function explainHandler(manager: ConnectorManager): (params: {
|
|
10
|
+
database: string;
|
|
11
|
+
sql: string;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatSuccess, formatError } from "../../utils/response.js";
|
|
3
|
+
import { validateReadonlySql } from "../../utils/sql-validator.js";
|
|
4
|
+
export function createExplainParams(dbIds) {
|
|
5
|
+
return {
|
|
6
|
+
database: z.enum(dbIds).describe(`Database ID. Available: ${dbIds.join(", ")}`),
|
|
7
|
+
sql: z.string().describe("SQL query to explain"),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function explainHandler(manager) {
|
|
11
|
+
return async (params) => {
|
|
12
|
+
const validation = validateReadonlySql(params.sql);
|
|
13
|
+
if (!validation.valid) {
|
|
14
|
+
return formatError(validation.error, "READONLY_VIOLATION");
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const connector = await manager.getConnector(params.database);
|
|
18
|
+
const result = await connector.explain(params.sql);
|
|
19
|
+
return formatSuccess({ data: result.plan, database: params.database });
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return formatError(String(err));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/tools/readonly/explain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAEnE,MAAM,UAAU,mBAAmB,CAAC,KAAe;IACjD,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,KAA8B,CAAC,CAAC,QAAQ,CAAC,2BAA2B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxG,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAyB;IACtD,OAAO,KAAK,EAAE,MAAyC,EAAE,EAAE;QACzD,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,WAAW,CAAC,UAAU,CAAC,KAAM,EAAE,oBAAoB,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACnD,OAAO,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { formatSuccess } from "../../utils/response.js";
|
|
2
|
+
export function listDatabasesHandler(manager) {
|
|
3
|
+
return async () => {
|
|
4
|
+
const databases = manager.getAllConfigs().map((c) => ({
|
|
5
|
+
id: c.id,
|
|
6
|
+
type: c.type,
|
|
7
|
+
description: c.description ?? "",
|
|
8
|
+
}));
|
|
9
|
+
return formatSuccess({ data: databases });
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=list-databases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-databases.js","sourceRoot":"","sources":["../../../src/tools/readonly/list-databases.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,MAAM,UAAU,oBAAoB,CAAC,OAAyB;IAC5D,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;SACjC,CAAC,CAAC,CAAC;QACJ,OAAO,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ConnectorManager } from "../../connectors/manager.js";
|
|
3
|
+
export declare function createListTablesParams(dbIds: string[]): {
|
|
4
|
+
database: z.ZodEnum<{
|
|
5
|
+
[x: string]: string;
|
|
6
|
+
}>;
|
|
7
|
+
schema: z.ZodOptional<z.ZodString>;
|
|
8
|
+
};
|
|
9
|
+
export declare function listTablesHandler(manager: ConnectorManager): (params: {
|
|
10
|
+
database: string;
|
|
11
|
+
schema?: string;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|