@calebmabry/postgres-mcp-server 0.1.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 +15 -0
- package/README.md +223 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +53 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +46 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +140 -0
- package/dist/db.js.map +1 -0
- package/dist/http-server.d.ts +3 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +184 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +85 -0
- package/dist/logger.js.map +1 -0
- package/dist/register-tools.d.ts +6 -0
- package/dist/register-tools.d.ts.map +1 -0
- package/dist/register-tools.js +83 -0
- package/dist/register-tools.js.map +1 -0
- package/dist/tools/describe.d.ts +31 -0
- package/dist/tools/describe.d.ts.map +1 -0
- package/dist/tools/describe.js +61 -0
- package/dist/tools/describe.js.map +1 -0
- package/dist/tools/functions.d.ts +20 -0
- package/dist/tools/functions.d.ts.map +1 -0
- package/dist/tools/functions.js +65 -0
- package/dist/tools/functions.js.map +1 -0
- package/dist/tools/indexes.d.ts +17 -0
- package/dist/tools/indexes.d.ts.map +1 -0
- package/dist/tools/indexes.js +82 -0
- package/dist/tools/indexes.js.map +1 -0
- package/dist/tools/list.d.ts +22 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/list.js +64 -0
- package/dist/tools/list.js.map +1 -0
- package/dist/tools/performance.d.ts +26 -0
- package/dist/tools/performance.d.ts.map +1 -0
- package/dist/tools/performance.js +125 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/query.d.ts +6 -0
- package/dist/tools/query.d.ts.map +1 -0
- package/dist/tools/query.js +318 -0
- package/dist/tools/query.js.map +1 -0
- package/dist/tools/schemas.d.ts +10 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +51 -0
- package/dist/tools/schemas.js.map +1 -0
- package/dist/validation.d.ts +159 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +90 -0
- package/dist/validation.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functions.js","sourceRoot":"","sources":["../../src/tools/functions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAA2B,MAAM,kBAAkB,CAAC;AAsBpG,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAc;IACpD,IAAI,CAAC;QACH,iBAAiB;QACjB,MAAM,UAAU,GAAG,aAAa,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAClE,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,EAAE,KAAK,EAAE,4BAA4B,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;QACnE,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,IAAI,QAAQ,CAAC;QAEjD,MAAM,KAAK,GAAG,GAAG,CAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAoCT,MAAM;;;KAG3B,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEvC,OAAO;YACL,SAAS,EAAE,MAAM,CAAC,IAAI;SACvB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;SACzE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface IndexInfo {
|
|
2
|
+
schema_name: string;
|
|
3
|
+
table_name: string;
|
|
4
|
+
index_name: string;
|
|
5
|
+
index_type: string;
|
|
6
|
+
is_unique: boolean;
|
|
7
|
+
is_primary: boolean;
|
|
8
|
+
columns: string;
|
|
9
|
+
definition: string;
|
|
10
|
+
size_bytes?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ListIndexesOutput {
|
|
13
|
+
indexes?: IndexInfo[];
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function listIndexesTool(input: unknown): Promise<ListIndexesOutput>;
|
|
17
|
+
//# sourceMappingURL=indexes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexes.d.ts","sourceRoot":"","sources":["../../src/tools/indexes.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,iBAAiB,CAAC,CA+E5B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getDb } from "../db.js";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
import { ListIndexesInputSchema, validateInput } from "../validation.js";
|
|
4
|
+
export async function listIndexesTool(input) {
|
|
5
|
+
try {
|
|
6
|
+
// Validate input
|
|
7
|
+
const validation = validateInput(ListIndexesInputSchema, input);
|
|
8
|
+
if (!validation.success) {
|
|
9
|
+
return { error: `Input validation failed: ${validation.error}` };
|
|
10
|
+
}
|
|
11
|
+
const validatedInput = validation.data;
|
|
12
|
+
const db = getDb();
|
|
13
|
+
let query;
|
|
14
|
+
if (validatedInput.table) {
|
|
15
|
+
query = sql `
|
|
16
|
+
SELECT
|
|
17
|
+
schemaname as schema_name,
|
|
18
|
+
tablename as table_name,
|
|
19
|
+
indexname as index_name,
|
|
20
|
+
CASE
|
|
21
|
+
WHEN indexdef LIKE '%USING btree%' THEN 'btree'
|
|
22
|
+
WHEN indexdef LIKE '%USING hash%' THEN 'hash'
|
|
23
|
+
WHEN indexdef LIKE '%USING gin%' THEN 'gin'
|
|
24
|
+
WHEN indexdef LIKE '%USING gist%' THEN 'gist'
|
|
25
|
+
WHEN indexdef LIKE '%USING spgist%' THEN 'sp-gist'
|
|
26
|
+
WHEN indexdef LIKE '%USING brin%' THEN 'brin'
|
|
27
|
+
ELSE 'unknown'
|
|
28
|
+
END as index_type,
|
|
29
|
+
CASE WHEN indexdef LIKE '%UNIQUE%' THEN true ELSE false END as is_unique,
|
|
30
|
+
CASE WHEN indexname LIKE '%_pkey' THEN true ELSE false END as is_primary,
|
|
31
|
+
regexp_replace(
|
|
32
|
+
regexp_replace(indexdef, '.*\\((.*?)\\).*', '\\1'),
|
|
33
|
+
' COLLATE [^,)]+', '', 'g'
|
|
34
|
+
) as columns,
|
|
35
|
+
indexdef as definition,
|
|
36
|
+
pg_relation_size(schemaname||'.'||indexname) as size_bytes
|
|
37
|
+
FROM pg_indexes
|
|
38
|
+
WHERE schemaname = ${validatedInput.schema}
|
|
39
|
+
AND tablename = ${validatedInput.table}
|
|
40
|
+
ORDER BY tablename, indexname
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
query = sql `
|
|
45
|
+
SELECT
|
|
46
|
+
schemaname as schema_name,
|
|
47
|
+
tablename as table_name,
|
|
48
|
+
indexname as index_name,
|
|
49
|
+
CASE
|
|
50
|
+
WHEN indexdef LIKE '%USING btree%' THEN 'btree'
|
|
51
|
+
WHEN indexdef LIKE '%USING hash%' THEN 'hash'
|
|
52
|
+
WHEN indexdef LIKE '%USING gin%' THEN 'gin'
|
|
53
|
+
WHEN indexdef LIKE '%USING gist%' THEN 'gist'
|
|
54
|
+
WHEN indexdef LIKE '%USING spgist%' THEN 'sp-gist'
|
|
55
|
+
WHEN indexdef LIKE '%USING brin%' THEN 'brin'
|
|
56
|
+
ELSE 'unknown'
|
|
57
|
+
END as index_type,
|
|
58
|
+
CASE WHEN indexdef LIKE '%UNIQUE%' THEN true ELSE false END as is_unique,
|
|
59
|
+
CASE WHEN indexname LIKE '%_pkey' THEN true ELSE false END as is_primary,
|
|
60
|
+
regexp_replace(
|
|
61
|
+
regexp_replace(indexdef, '.*\\((.*?)\\).*', '\\1'),
|
|
62
|
+
' COLLATE [^,)]+', '', 'g'
|
|
63
|
+
) as columns,
|
|
64
|
+
indexdef as definition,
|
|
65
|
+
pg_relation_size(schemaname||'.'||indexname) as size_bytes
|
|
66
|
+
FROM pg_indexes
|
|
67
|
+
WHERE schemaname = ${validatedInput.schema}
|
|
68
|
+
ORDER BY tablename, indexname
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
const result = await query.execute(db);
|
|
72
|
+
return {
|
|
73
|
+
indexes: result.rows,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
error: error instanceof Error ? error.message : "Unknown error occurred",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=indexes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexes.js","sourceRoot":"","sources":["../../src/tools/indexes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAyB,MAAM,kBAAkB,CAAC;AAmBhG,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAc;IAEd,IAAI,CAAC;QACH,iBAAiB;QACjB,MAAM,UAAU,GAAG,aAAa,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;QAChE,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,EAAE,KAAK,EAAE,4BAA4B,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;QACnE,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QAEnB,IAAI,KAAK,CAAC;QACV,IAAI,cAAc,CAAC,KAAK,EAAE,CAAC;YACzB,KAAK,GAAG,GAAG,CAAW;;;;;;;;;;;;;;;;;;;;;;;6BAuBC,cAAc,CAAC,MAAM;4BACtB,cAAc,CAAC,KAAK;;OAEzC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,GAAG,CAAW;;;;;;;;;;;;;;;;;;;;;;;6BAuBC,cAAc,CAAC,MAAM;;OAE3C,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEvC,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,IAAI;SACrB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;SACzE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface TableInfo {
|
|
2
|
+
table_name: string;
|
|
3
|
+
table_type: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ListTablesOutput {
|
|
6
|
+
tables?: TableInfo[];
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function listTablesTool(input: unknown): Promise<ListTablesOutput>;
|
|
10
|
+
export interface ViewInfo {
|
|
11
|
+
schema_name: string;
|
|
12
|
+
view_name: string;
|
|
13
|
+
view_definition: string;
|
|
14
|
+
is_updatable: string;
|
|
15
|
+
check_option: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ListViewsOutput {
|
|
18
|
+
views?: ViewInfo[];
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function listViewsTool(input: unknown): Promise<ListViewsOutput>;
|
|
22
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,gBAAgB,CAAC,CA+B3B;AAED,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAiC5E"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getDb } from "../db.js";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
import { ListTablesInputSchema, ListViewsInputSchema, validateInput, } from "../validation.js";
|
|
4
|
+
export async function listTablesTool(input) {
|
|
5
|
+
try {
|
|
6
|
+
const validation = validateInput(ListTablesInputSchema, input);
|
|
7
|
+
if (!validation.success) {
|
|
8
|
+
return { error: `Input validation failed: ${validation.error}` };
|
|
9
|
+
}
|
|
10
|
+
const validatedInput = validation.data;
|
|
11
|
+
const db = getDb();
|
|
12
|
+
const schema = validatedInput.schema || "public";
|
|
13
|
+
const query = sql `
|
|
14
|
+
SELECT
|
|
15
|
+
table_name,
|
|
16
|
+
table_type
|
|
17
|
+
FROM information_schema.tables
|
|
18
|
+
WHERE table_schema = ${schema}
|
|
19
|
+
AND table_type IN ('BASE TABLE', 'VIEW')
|
|
20
|
+
ORDER BY table_name
|
|
21
|
+
`;
|
|
22
|
+
const result = await query.execute(db);
|
|
23
|
+
return {
|
|
24
|
+
tables: result.rows,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
error: error instanceof Error ? error.message : "Unknown error occurred",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function listViewsTool(input) {
|
|
34
|
+
try {
|
|
35
|
+
const validation = validateInput(ListViewsInputSchema, input);
|
|
36
|
+
if (!validation.success) {
|
|
37
|
+
return { error: `Input validation failed: ${validation.error}` };
|
|
38
|
+
}
|
|
39
|
+
const validatedInput = validation.data;
|
|
40
|
+
const db = getDb();
|
|
41
|
+
const schema = validatedInput.schema || "public";
|
|
42
|
+
const query = sql `
|
|
43
|
+
SELECT
|
|
44
|
+
table_schema as schema_name,
|
|
45
|
+
table_name as view_name,
|
|
46
|
+
view_definition,
|
|
47
|
+
is_updatable,
|
|
48
|
+
check_option
|
|
49
|
+
FROM information_schema.views
|
|
50
|
+
WHERE table_schema = ${schema}
|
|
51
|
+
ORDER BY table_name
|
|
52
|
+
`;
|
|
53
|
+
const result = await query.execute(db);
|
|
54
|
+
return {
|
|
55
|
+
views: result.rows,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return {
|
|
60
|
+
error: error instanceof Error ? error.message : "Unknown error occurred",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAY1B,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAc;IAEd,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;QAC/D,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,EAAE,KAAK,EAAE,4BAA4B,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;QACnE,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,IAAI,QAAQ,CAAC;QAEjD,MAAM,KAAK,GAAG,GAAG,CAAW;;;;;6BAKH,MAAM;;;KAG9B,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEvC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;SACzE,CAAC;IACJ,CAAC;AACH,CAAC;AAeD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAc;IAChD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,EAAE,KAAK,EAAE,4BAA4B,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;QACnE,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,IAAI,QAAQ,CAAC;QAEjD,MAAM,KAAK,GAAG,GAAG,CAAU;;;;;;;;6BAQF,MAAM;;KAE9B,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEvC,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,IAAI;SACnB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;SACzE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface ExplainQueryOutput {
|
|
2
|
+
plan?: any[];
|
|
3
|
+
error?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function explainQueryTool(input: unknown): Promise<ExplainQueryOutput>;
|
|
6
|
+
export interface TableStatsInfo {
|
|
7
|
+
schema_name: string;
|
|
8
|
+
table_name: string;
|
|
9
|
+
row_count: number;
|
|
10
|
+
table_size_bytes: number;
|
|
11
|
+
table_size_pretty: string;
|
|
12
|
+
index_size_bytes: number;
|
|
13
|
+
index_size_pretty: string;
|
|
14
|
+
total_size_bytes: number;
|
|
15
|
+
total_size_pretty: string;
|
|
16
|
+
last_vacuum?: string;
|
|
17
|
+
last_autovacuum?: string;
|
|
18
|
+
last_analyze?: string;
|
|
19
|
+
last_autoanalyze?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface GetTableStatsOutput {
|
|
22
|
+
stats?: TableStatsInfo[];
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function getTableStatsTool(input: unknown): Promise<GetTableStatsOutput>;
|
|
26
|
+
//# sourceMappingURL=performance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/tools/performance.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,kBAAkB,CAAC,CAsD7B;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,mBAAmB,CAAC,CAyE9B"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { getDb, getPool } from "../db.js";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
import { ExplainQueryInputSchema, GetTableStatsInputSchema, validateInput, } from "../validation.js";
|
|
4
|
+
export async function explainQueryTool(input) {
|
|
5
|
+
try {
|
|
6
|
+
const validation = validateInput(ExplainQueryInputSchema, input);
|
|
7
|
+
if (!validation.success) {
|
|
8
|
+
return { error: `Input validation failed: ${validation.error}` };
|
|
9
|
+
}
|
|
10
|
+
const validatedInput = validation.data;
|
|
11
|
+
// Safety check - prevent potentially dangerous queries
|
|
12
|
+
const trimmedSql = validatedInput.sql.trim().toUpperCase();
|
|
13
|
+
if (trimmedSql.includes("INSERT") ||
|
|
14
|
+
trimmedSql.includes("UPDATE") ||
|
|
15
|
+
trimmedSql.includes("DELETE") ||
|
|
16
|
+
trimmedSql.includes("DROP") ||
|
|
17
|
+
trimmedSql.includes("CREATE") ||
|
|
18
|
+
trimmedSql.includes("ALTER") ||
|
|
19
|
+
trimmedSql.includes("TRUNCATE")) {
|
|
20
|
+
return {
|
|
21
|
+
error: "EXPLAIN is only allowed for SELECT queries and read-only operations",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// Build EXPLAIN options safely
|
|
25
|
+
const options = [];
|
|
26
|
+
if (validatedInput.analyze)
|
|
27
|
+
options.push("ANALYZE true");
|
|
28
|
+
if (validatedInput.buffers)
|
|
29
|
+
options.push("BUFFERS true");
|
|
30
|
+
if (validatedInput.costs !== false)
|
|
31
|
+
options.push("COSTS true");
|
|
32
|
+
if (validatedInput.format) {
|
|
33
|
+
// Validate format to prevent injection
|
|
34
|
+
const validFormats = ["TEXT", "JSON", "XML", "YAML"];
|
|
35
|
+
const upperFormat = validatedInput.format.toUpperCase();
|
|
36
|
+
if (validFormats.includes(upperFormat)) {
|
|
37
|
+
options.push(`FORMAT ${upperFormat}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const optionsStr = options.length > 0 ? `(${options.join(", ")})` : "";
|
|
41
|
+
const explainSql = `EXPLAIN ${optionsStr} ${validatedInput.sql}`;
|
|
42
|
+
const pool = getPool();
|
|
43
|
+
const result = await pool.query(explainSql);
|
|
44
|
+
return {
|
|
45
|
+
plan: result.rows,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
error: error instanceof Error ? error.message : "Unknown error occurred",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function getTableStatsTool(input) {
|
|
55
|
+
try {
|
|
56
|
+
// Validate input
|
|
57
|
+
const validation = validateInput(GetTableStatsInputSchema, input);
|
|
58
|
+
if (!validation.success) {
|
|
59
|
+
return { error: `Input validation failed: ${validation.error}` };
|
|
60
|
+
}
|
|
61
|
+
const validatedInput = validation.data;
|
|
62
|
+
const db = getDb();
|
|
63
|
+
let query;
|
|
64
|
+
if (validatedInput.table) {
|
|
65
|
+
query = sql `
|
|
66
|
+
SELECT
|
|
67
|
+
schemaname as schema_name,
|
|
68
|
+
relname as table_name,
|
|
69
|
+
(COALESCE(n_tup_ins, 0) + COALESCE(n_tup_upd, 0) + COALESCE(n_tup_del, 0))::bigint as row_count,
|
|
70
|
+
pg_relation_size(schemaname||'.'||relname) as table_size_bytes,
|
|
71
|
+
pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) as table_size_pretty,
|
|
72
|
+
pg_indexes_size(schemaname||'.'||relname) as index_size_bytes,
|
|
73
|
+
pg_size_pretty(pg_indexes_size(schemaname||'.'||relname)) as index_size_pretty,
|
|
74
|
+
pg_total_relation_size(schemaname||'.'||relname) as total_size_bytes,
|
|
75
|
+
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) as total_size_pretty,
|
|
76
|
+
last_vacuum::text,
|
|
77
|
+
last_autovacuum::text,
|
|
78
|
+
last_analyze::text,
|
|
79
|
+
last_autoanalyze::text
|
|
80
|
+
FROM pg_stat_user_tables
|
|
81
|
+
WHERE schemaname = ${validatedInput.schema}
|
|
82
|
+
AND relname = ${validatedInput.table}
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
query = sql `
|
|
87
|
+
SELECT
|
|
88
|
+
schemaname as schema_name,
|
|
89
|
+
relname as table_name,
|
|
90
|
+
(COALESCE(n_tup_ins, 0) + COALESCE(n_tup_upd, 0) + COALESCE(n_tup_del, 0))::bigint as row_count,
|
|
91
|
+
pg_relation_size(schemaname||'.'||relname) as table_size_bytes,
|
|
92
|
+
pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) as table_size_pretty,
|
|
93
|
+
pg_indexes_size(schemaname||'.'||relname) as index_size_bytes,
|
|
94
|
+
pg_size_pretty(pg_indexes_size(schemaname||'.'||relname)) as index_size_pretty,
|
|
95
|
+
pg_total_relation_size(schemaname||'.'||relname) as total_size_bytes,
|
|
96
|
+
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) as total_size_pretty,
|
|
97
|
+
last_vacuum::text,
|
|
98
|
+
last_autovacuum::text,
|
|
99
|
+
last_analyze::text,
|
|
100
|
+
last_autoanalyze::text
|
|
101
|
+
FROM pg_stat_user_tables
|
|
102
|
+
WHERE schemaname = ${validatedInput.schema}
|
|
103
|
+
ORDER BY total_size_bytes DESC
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
const result = await query.execute(db);
|
|
107
|
+
// Convert string numbers to actual numbers
|
|
108
|
+
const stats = result.rows.map((row) => ({
|
|
109
|
+
...row,
|
|
110
|
+
row_count: Number(row.row_count),
|
|
111
|
+
table_size_bytes: Number(row.table_size_bytes),
|
|
112
|
+
index_size_bytes: Number(row.index_size_bytes),
|
|
113
|
+
total_size_bytes: Number(row.total_size_bytes),
|
|
114
|
+
}));
|
|
115
|
+
return {
|
|
116
|
+
stats,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
return {
|
|
121
|
+
error: error instanceof Error ? error.message : "Unknown error occurred",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=performance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.js","sourceRoot":"","sources":["../../src/tools/performance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type QueryOutput } from "../validation.js";
|
|
2
|
+
export declare const MAX_PAGE_SIZE: number;
|
|
3
|
+
export declare const DEFAULT_PAGE_SIZE: number;
|
|
4
|
+
export declare const MAX_PAYLOAD_SIZE: number;
|
|
5
|
+
export declare function queryTool(input: unknown): Promise<QueryOutput>;
|
|
6
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/tools/query.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAG1B,eAAO,MAAM,aAAa,QAA2B,CAAC;AACtD,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAI9D,eAAO,MAAM,gBAAgB,QAA8B,CAAC;AAiS5D,wBAAsB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CA2FpE"}
|