schema-navigator-mcp 0.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +215 -0
  3. package/dist/db/pg-adapter.d.ts +17 -0
  4. package/dist/db/pg-adapter.js +74 -0
  5. package/dist/db/pg-adapter.js.map +1 -0
  6. package/dist/db/postgres.d.ts +19 -0
  7. package/dist/db/postgres.js +34 -0
  8. package/dist/db/postgres.js.map +1 -0
  9. package/dist/db/supabase-adapter.d.ts +45 -0
  10. package/dist/db/supabase-adapter.js +148 -0
  11. package/dist/db/supabase-adapter.js.map +1 -0
  12. package/dist/db/types.d.ts +25 -0
  13. package/dist/db/types.js +2 -0
  14. package/dist/db/types.js.map +1 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +80 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/server.d.ts +4 -0
  19. package/dist/server.js +129 -0
  20. package/dist/server.js.map +1 -0
  21. package/dist/tools/constraints.d.ts +22 -0
  22. package/dist/tools/constraints.js +80 -0
  23. package/dist/tools/constraints.js.map +1 -0
  24. package/dist/tools/functions.d.ts +20 -0
  25. package/dist/tools/functions.js +118 -0
  26. package/dist/tools/functions.js.map +1 -0
  27. package/dist/tools/overview.d.ts +29 -0
  28. package/dist/tools/overview.js +142 -0
  29. package/dist/tools/overview.js.map +1 -0
  30. package/dist/tools/policies.d.ts +29 -0
  31. package/dist/tools/policies.js +96 -0
  32. package/dist/tools/policies.js.map +1 -0
  33. package/dist/tools/relations.d.ts +18 -0
  34. package/dist/tools/relations.js +72 -0
  35. package/dist/tools/relations.js.map +1 -0
  36. package/dist/tools/search.d.ts +13 -0
  37. package/dist/tools/search.js +77 -0
  38. package/dist/tools/search.js.map +1 -0
  39. package/dist/tools/table-profile.d.ts +68 -0
  40. package/dist/tools/table-profile.js +477 -0
  41. package/dist/tools/table-profile.js.map +1 -0
  42. package/dist/utils/check-parser.d.ts +17 -0
  43. package/dist/utils/check-parser.js +61 -0
  44. package/dist/utils/check-parser.js.map +1 -0
  45. package/dist/utils/pg-array.d.ts +6 -0
  46. package/dist/utils/pg-array.js +42 -0
  47. package/dist/utils/pg-array.js.map +1 -0
  48. package/package.json +67 -0
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { createSchemaNavigator } from "./server.js";
4
+ import { shutdownPool } from "./db/postgres.js";
5
+ import { extractRefFromUrl } from "./db/supabase-adapter.js";
6
+ function resolveConfig() {
7
+ // Priority 1: Direct database connection (any PostgreSQL)
8
+ const databaseUrl = process.env.DATABASE_URL;
9
+ if (databaseUrl) {
10
+ if (!/^postgres(ql)?:\/\//i.test(databaseUrl)) {
11
+ console.error("Error: DATABASE_URL must be a postgresql:// connection string");
12
+ process.exit(1);
13
+ }
14
+ return databaseUrl;
15
+ }
16
+ // Priority 2: Supabase Management API (no password needed)
17
+ const accessToken = process.env.SUPABASE_ACCESS_TOKEN;
18
+ if (accessToken) {
19
+ const projectRef = resolveProjectRef();
20
+ if (!projectRef) {
21
+ console.error("Error: SUPABASE_ACCESS_TOKEN is set but no project reference found.\n" +
22
+ "Set one of:\n" +
23
+ " SUPABASE_PROJECT_REF=your-project-ref\n" +
24
+ " SUPABASE_URL=https://your-project-ref.supabase.co\n" +
25
+ " NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co");
26
+ process.exit(1);
27
+ }
28
+ return { accessToken, projectRef };
29
+ }
30
+ // No config found — give context-aware error
31
+ const hasSupabaseVars = !!(process.env.SUPABASE_URL ||
32
+ process.env.NEXT_PUBLIC_SUPABASE_URL ||
33
+ process.env.SUPABASE_PROJECT_REF);
34
+ if (hasSupabaseVars) {
35
+ console.error("Error: Found Supabase project info but no credentials.\n" +
36
+ "Either:\n" +
37
+ " 1. Set DATABASE_URL for direct connection (requires db password)\n" +
38
+ " 2. Set SUPABASE_ACCESS_TOKEN for Management API (no password needed)\n" +
39
+ " Get a token at: https://supabase.com/dashboard/account/tokens");
40
+ }
41
+ else {
42
+ console.error("Error: No database connection configured.\n\n" +
43
+ "Option 1 — Direct connection (any PostgreSQL):\n" +
44
+ " DATABASE_URL=postgresql://user:pass@host:5432/db\n\n" +
45
+ "Option 2 — Supabase (no password needed):\n" +
46
+ " SUPABASE_ACCESS_TOKEN=sbp_... (from supabase.com/dashboard/account/tokens)\n" +
47
+ " SUPABASE_PROJECT_REF=your-project-ref (from your dashboard URL)");
48
+ }
49
+ process.exit(1);
50
+ }
51
+ /**
52
+ * Extract project ref from available env vars.
53
+ */
54
+ function resolveProjectRef() {
55
+ if (process.env.SUPABASE_PROJECT_REF) {
56
+ return process.env.SUPABASE_PROJECT_REF;
57
+ }
58
+ const url = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL;
59
+ if (url) {
60
+ return extractRefFromUrl(url);
61
+ }
62
+ return null;
63
+ }
64
+ async function main() {
65
+ const config = resolveConfig();
66
+ const server = await createSchemaNavigator(config);
67
+ const transport = new StdioServerTransport();
68
+ const shutdown = async () => {
69
+ await shutdownPool();
70
+ process.exit(0);
71
+ };
72
+ process.on("SIGINT", shutdown);
73
+ process.on("SIGTERM", shutdown);
74
+ await server.connect(transport);
75
+ }
76
+ main().catch((err) => {
77
+ console.error("Fatal error:", err instanceof Error ? err.message : err);
78
+ process.exit(1);
79
+ });
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { SupabaseAdapterConfig } from "./db/supabase-adapter.js";
3
+ export type SchemaNavigatorConfig = string | SupabaseAdapterConfig;
4
+ export declare function createSchemaNavigator(config: SchemaNavigatorConfig): Promise<McpServer>;
package/dist/server.js ADDED
@@ -0,0 +1,129 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { setAdapter, shutdownPool } from "./db/postgres.js";
4
+ import { PgAdapter } from "./db/pg-adapter.js";
5
+ import { SupabaseAdapter } from "./db/supabase-adapter.js";
6
+ import { schemaOverview } from "./tools/overview.js";
7
+ import { schemaTable } from "./tools/table-profile.js";
8
+ import { schemaFind } from "./tools/search.js";
9
+ import { schemaRelations } from "./tools/relations.js";
10
+ import { schemaFunctions } from "./tools/functions.js";
11
+ import { schemaConstraints } from "./tools/constraints.js";
12
+ import { schemaPolicies } from "./tools/policies.js";
13
+ function errorMessage(err) {
14
+ return err instanceof Error ? err.message : String(err);
15
+ }
16
+ function toolError(err) {
17
+ return {
18
+ content: [{ type: "text", text: JSON.stringify({ error: errorMessage(err) }) }],
19
+ isError: true,
20
+ };
21
+ }
22
+ export async function createSchemaNavigator(config) {
23
+ if (typeof config === "string") {
24
+ const pgAdapter = new PgAdapter(config);
25
+ await pgAdapter.detectVersion();
26
+ setAdapter(pgAdapter);
27
+ }
28
+ else {
29
+ setAdapter(new SupabaseAdapter(config));
30
+ }
31
+ const server = new McpServer({
32
+ name: "schema-navigator",
33
+ version: "0.2.0",
34
+ });
35
+ // 1. schema_overview — Database dashboard
36
+ server.tool("schema_overview", "List all tables, views, enums, extensions with sizes and health warnings", { schema: z.string().default("public").describe("Database schema name") }, async ({ schema }) => {
37
+ try {
38
+ const result = await schemaOverview(schema);
39
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
40
+ }
41
+ catch (err) {
42
+ return toolError(err);
43
+ }
44
+ });
45
+ // 2. schema_table — Complete object profile
46
+ server.tool("schema_table", "Profile a table/view/matview: columns, constraints, FKs, indexes, RLS, size", {
47
+ table_name: z.string().describe("Table, view, or materialized view name"),
48
+ schema: z.string().default("public").describe("Database schema name"),
49
+ }, async ({ table_name, schema }) => {
50
+ try {
51
+ const result = await schemaTable(table_name, schema);
52
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
53
+ }
54
+ catch (err) {
55
+ return toolError(err);
56
+ }
57
+ });
58
+ // 3. schema_find — Cross-schema search
59
+ server.tool("schema_find", "Search tables, columns, functions, views, policies, enums by name pattern", {
60
+ term: z.string().describe("Search term (case-insensitive partial match)"),
61
+ schema: z.string().default("public").describe("Database schema name"),
62
+ }, async ({ term, schema }) => {
63
+ try {
64
+ const result = await schemaFind(term, schema);
65
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
66
+ }
67
+ catch (err) {
68
+ return toolError(err);
69
+ }
70
+ });
71
+ // 4. schema_relations — FK graph
72
+ server.tool("schema_relations", "Show foreign key relationships: outbound and inbound references", {
73
+ table_name: z.string().describe("Table name to get relations for"),
74
+ schema: z.string().default("public").describe("Database schema name"),
75
+ }, async ({ table_name, schema }) => {
76
+ try {
77
+ const result = await schemaRelations(table_name, schema);
78
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
79
+ }
80
+ catch (err) {
81
+ return toolError(err);
82
+ }
83
+ });
84
+ // 5. schema_functions — Function inspector
85
+ server.tool("schema_functions", "Inspect function: args, return type, language, security, source code", {
86
+ function_name: z.string().describe("Function or procedure name"),
87
+ schema: z.string().default("public").describe("Database schema name"),
88
+ }, async ({ function_name, schema }) => {
89
+ try {
90
+ const result = await schemaFunctions(function_name, schema);
91
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
92
+ }
93
+ catch (err) {
94
+ return toolError(err);
95
+ }
96
+ });
97
+ // 6. schema_constraints — Value extractor (CHECK + ENUM)
98
+ server.tool("schema_constraints", "Extract allowed values from CHECK constraints and ENUM types on a table", {
99
+ table_name: z.string().describe("Table name"),
100
+ schema: z.string().default("public").describe("Database schema name"),
101
+ }, async ({ table_name, schema }) => {
102
+ try {
103
+ const result = await schemaConstraints(table_name, schema);
104
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
105
+ }
106
+ catch (err) {
107
+ return toolError(err);
108
+ }
109
+ });
110
+ // 7. schema_policies — RLS analyzer
111
+ server.tool("schema_policies", "Analyze RLS: enabled status, policies, role access matrix", {
112
+ table_name: z.string().describe("Table name"),
113
+ schema: z.string().default("public").describe("Database schema name"),
114
+ }, async ({ table_name, schema }) => {
115
+ try {
116
+ const result = await schemaPolicies(table_name, schema);
117
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
118
+ }
119
+ catch (err) {
120
+ return toolError(err);
121
+ }
122
+ });
123
+ // Cleanup on close
124
+ server.server.onclose = async () => {
125
+ await shutdownPool();
126
+ };
127
+ return server;
128
+ }
129
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ interface CheckConstraint {
2
+ constraint_name: string;
3
+ column: string | null;
4
+ type: "values" | "range" | "unknown";
5
+ allowed_values?: string[];
6
+ expression: string;
7
+ }
8
+ interface EnumColumn {
9
+ column: string;
10
+ enum_name: string;
11
+ values: string[];
12
+ }
13
+ interface ConstraintsResult {
14
+ table: string;
15
+ schema: string;
16
+ check_constraints: CheckConstraint[];
17
+ enum_columns: EnumColumn[];
18
+ }
19
+ export declare function schemaConstraints(tableName: string, schema: string): Promise<ConstraintsResult | {
20
+ error: string;
21
+ }>;
22
+ export {};
@@ -0,0 +1,80 @@
1
+ import { readOnlyMultiQuery, getIsPostgres } from "../db/postgres.js";
2
+ import { parseCheckExpression } from "../utils/check-parser.js";
3
+ export async function schemaConstraints(tableName, schema) {
4
+ const isPg = getIsPostgres();
5
+ const queries = [
6
+ // Check table existence
7
+ {
8
+ sql: `SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2`,
9
+ params: [schema, tableName],
10
+ },
11
+ // CHECK constraints
12
+ {
13
+ sql: `
14
+ SELECT tc.constraint_name, cc.check_clause
15
+ FROM information_schema.table_constraints tc
16
+ JOIN information_schema.check_constraints cc
17
+ ON cc.constraint_name = tc.constraint_name AND cc.constraint_schema = tc.constraint_schema
18
+ WHERE tc.table_schema = $1 AND tc.table_name = $2 AND tc.constraint_type = 'CHECK'
19
+ ORDER BY tc.constraint_name
20
+ `,
21
+ params: [schema, tableName],
22
+ },
23
+ ];
24
+ // ENUM columns (Postgres only)
25
+ if (isPg) {
26
+ queries.push({
27
+ sql: `
28
+ SELECT
29
+ a.attname AS column_name,
30
+ t.typname AS enum_name,
31
+ array_agg(e.enumlabel ORDER BY e.enumsortorder) AS enum_values
32
+ FROM pg_attribute a
33
+ JOIN pg_class c ON c.oid = a.attrelid
34
+ JOIN pg_namespace n ON n.oid = c.relnamespace
35
+ JOIN pg_type t ON t.oid = a.atttypid
36
+ JOIN pg_enum e ON e.enumtypid = t.oid
37
+ WHERE n.nspname = $1 AND c.relname = $2
38
+ AND a.attnum > 0 AND NOT a.attisdropped
39
+ GROUP BY a.attname, t.typname
40
+ ORDER BY a.attname
41
+ `,
42
+ params: [schema, tableName],
43
+ });
44
+ }
45
+ const results = await readOnlyMultiQuery(queries);
46
+ if (results[0].rows.length === 0) {
47
+ return { error: `Table '${tableName}' not found in schema '${schema}'` };
48
+ }
49
+ // Parse CHECK constraints
50
+ const checkConstraints = results[1].rows
51
+ .map((r) => {
52
+ const parsed = parseCheckExpression(r.check_clause);
53
+ // Skip auto-generated NOT NULL checks
54
+ if (parsed.column === null && parsed.type === "unknown")
55
+ return null;
56
+ return {
57
+ constraint_name: r.constraint_name,
58
+ column: parsed.column,
59
+ type: parsed.type,
60
+ ...(parsed.allowed_values ? { allowed_values: parsed.allowed_values } : {}),
61
+ expression: r.check_clause,
62
+ };
63
+ })
64
+ .filter((c) => c !== null);
65
+ // ENUM columns
66
+ const enumColumns = isPg
67
+ ? results[2].rows.map((r) => ({
68
+ column: r.column_name,
69
+ enum_name: r.enum_name,
70
+ values: r.enum_values,
71
+ }))
72
+ : [];
73
+ return {
74
+ table: tableName,
75
+ schema,
76
+ check_constraints: checkConstraints,
77
+ enum_columns: enumColumns,
78
+ };
79
+ }
80
+ //# sourceMappingURL=constraints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../src/tools/constraints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAuBhE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAiB,EACjB,MAAc;IAEd,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAE7B,MAAM,OAAO,GAA+C;QAC1D,wBAAwB;QACxB;YACE,GAAG,EAAE,qFAAqF;YAC1F,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;SAC5B;QACD,oBAAoB;QACpB;YACE,GAAG,EAAE;;;;;;;OAOJ;YACD,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;SAC5B;KACF,CAAC;IAEF,+BAA+B;IAC/B,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,IAAI,CAAC;YACX,GAAG,EAAE;;;;;;;;;;;;;;OAcJ;YACD,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAElD,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,UAAU,SAAS,0BAA0B,MAAM,GAAG,EAAE,CAAC;IAC3E,CAAC;IAED,0BAA0B;IAC1B,MAAM,gBAAgB,GAAsB,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;SACxD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,YAAsB,CAAC,CAAC;QAC9D,sCAAsC;QACtC,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACrE,OAAO;YACL,eAAe,EAAE,CAAC,CAAC,eAAyB;YAC5C,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,UAAU,EAAE,CAAC,CAAC,YAAsB;SACrC,CAAC;IACJ,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAwB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAEnD,eAAe;IACf,MAAM,WAAW,GAAiB,IAAI;QACpC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1B,MAAM,EAAE,CAAC,CAAC,WAAqB;YAC/B,SAAS,EAAE,CAAC,CAAC,SAAmB;YAChC,MAAM,EAAE,CAAC,CAAC,WAAuB;SAClC,CAAC,CAAC;QACL,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;QACL,KAAK,EAAE,SAAS;QAChB,MAAM;QACN,iBAAiB,EAAE,gBAAgB;QACnC,YAAY,EAAE,WAAW;KAC1B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ interface FunctionArg {
2
+ name: string;
3
+ type: string;
4
+ mode: string;
5
+ default: string | null;
6
+ }
7
+ interface FunctionResult {
8
+ name: string;
9
+ schema: string;
10
+ args: FunctionArg[];
11
+ return_type: string;
12
+ language: string;
13
+ security_definer: boolean;
14
+ volatility: string | null;
15
+ source_code: string | null;
16
+ }
17
+ export declare function schemaFunctions(functionName: string, schema: string): Promise<FunctionResult[] | {
18
+ error: string;
19
+ }>;
20
+ export {};
@@ -0,0 +1,118 @@
1
+ import { readOnlyQuery, getIsPostgres } from "../db/postgres.js";
2
+ export async function schemaFunctions(functionName, schema) {
3
+ const isPg = getIsPostgres();
4
+ if (isPg) {
5
+ // Full function details via pg_catalog
6
+ const result = await readOnlyQuery(`
7
+ SELECT
8
+ p.proname AS name,
9
+ pg_get_function_arguments(p.oid) AS args_text,
10
+ pg_get_function_result(p.oid) AS return_type,
11
+ l.lanname AS language,
12
+ p.prosecdef AS security_definer,
13
+ CASE p.provolatile
14
+ WHEN 'i' THEN 'immutable'
15
+ WHEN 's' THEN 'stable'
16
+ WHEN 'v' THEN 'volatile'
17
+ END AS volatility,
18
+ pg_get_functiondef(p.oid) AS source_code
19
+ FROM pg_proc p
20
+ JOIN pg_namespace n ON n.oid = p.pronamespace
21
+ JOIN pg_language l ON l.oid = p.prolang
22
+ WHERE n.nspname = $1 AND p.proname = $2
23
+ ORDER BY p.pronargs
24
+ `, [schema, functionName]);
25
+ if (result.rows.length === 0) {
26
+ return { error: `Function '${functionName}' not found in schema '${schema}'` };
27
+ }
28
+ return result.rows.map((r) => ({
29
+ name: r.name,
30
+ schema,
31
+ args: parseArgsText(r.args_text),
32
+ return_type: r.return_type,
33
+ language: r.language,
34
+ security_definer: r.security_definer,
35
+ volatility: r.volatility,
36
+ source_code: r.source_code,
37
+ }));
38
+ }
39
+ // Fallback: information_schema (signature only, no source)
40
+ const result = await readOnlyQuery(`
41
+ SELECT
42
+ r.routine_name AS name,
43
+ r.data_type AS return_type,
44
+ r.routine_body AS language,
45
+ r.security_type
46
+ FROM information_schema.routines r
47
+ WHERE r.routine_schema = $1 AND r.routine_name = $2
48
+ ORDER BY r.routine_name
49
+ `, [schema, functionName]);
50
+ if (result.rows.length === 0) {
51
+ return { error: `Function '${functionName}' not found in schema '${schema}'` };
52
+ }
53
+ // Get parameters
54
+ const paramsResult = await readOnlyQuery(`
55
+ SELECT
56
+ p.parameter_name, p.data_type, p.parameter_mode, p.parameter_default
57
+ FROM information_schema.parameters p
58
+ WHERE p.specific_schema = $1
59
+ AND p.specific_name LIKE $2
60
+ ORDER BY p.ordinal_position
61
+ `, [schema, `${functionName}%`]);
62
+ const args = paramsResult.rows.map((r) => ({
63
+ name: r.parameter_name ?? "",
64
+ type: r.data_type,
65
+ mode: r.parameter_mode ?? "IN",
66
+ default: r.parameter_default ?? null,
67
+ }));
68
+ return result.rows.map((r) => ({
69
+ name: r.name,
70
+ schema,
71
+ args,
72
+ return_type: r.return_type,
73
+ language: r.language,
74
+ security_definer: r.security_type === "DEFINER",
75
+ volatility: null,
76
+ source_code: null,
77
+ }));
78
+ }
79
+ function parseArgsText(argsText) {
80
+ if (!argsText || argsText.trim() === "")
81
+ return [];
82
+ return argsText.split(",").map((arg) => {
83
+ const trimmed = arg.trim();
84
+ // Format: [mode] [name] type [DEFAULT value]
85
+ const defaultMatch = trimmed.match(/\s+DEFAULT\s+(.+)$/i);
86
+ const withoutDefault = defaultMatch
87
+ ? trimmed.slice(0, -defaultMatch[0].length)
88
+ : trimmed;
89
+ const parts = withoutDefault.split(/\s+/);
90
+ let mode = "IN";
91
+ let name = "";
92
+ let type = "";
93
+ if (["IN", "OUT", "INOUT", "VARIADIC"].includes(parts[0].toUpperCase())) {
94
+ mode = parts[0].toUpperCase();
95
+ if (parts.length >= 3) {
96
+ name = parts[1];
97
+ type = parts.slice(2).join(" ");
98
+ }
99
+ else {
100
+ type = parts.slice(1).join(" ");
101
+ }
102
+ }
103
+ else if (parts.length >= 2) {
104
+ name = parts[0];
105
+ type = parts.slice(1).join(" ");
106
+ }
107
+ else {
108
+ type = parts[0];
109
+ }
110
+ return {
111
+ name,
112
+ type,
113
+ mode,
114
+ default: defaultMatch ? defaultMatch[1] : null,
115
+ };
116
+ });
117
+ }
118
+ //# sourceMappingURL=functions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functions.js","sourceRoot":"","sources":["../../src/tools/functions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ interface TableInfo {
2
+ name: string;
3
+ type: string;
4
+ rows: number | null;
5
+ size: string | null;
6
+ }
7
+ interface EnumType {
8
+ name: string;
9
+ values: string[];
10
+ }
11
+ interface OverviewResult {
12
+ schema: string;
13
+ tables: TableInfo[];
14
+ views: string[];
15
+ materialized_views: string[];
16
+ enum_types: EnumType[];
17
+ extensions: string[];
18
+ counts: {
19
+ tables: number;
20
+ views: number;
21
+ materialized_views: number;
22
+ functions: number;
23
+ enum_types: number;
24
+ truncated?: boolean;
25
+ };
26
+ health_warnings: string[];
27
+ }
28
+ export declare function schemaOverview(schema: string): Promise<OverviewResult>;
29
+ export {};
@@ -0,0 +1,142 @@
1
+ import { readOnlyMultiQuery, getIsPostgres } from "../db/postgres.js";
2
+ export async function schemaOverview(schema) {
3
+ const isPg = getIsPostgres();
4
+ const queries = [];
5
+ // Q0: Tables with row estimates and sizes
6
+ if (isPg) {
7
+ queries.push({
8
+ sql: `
9
+ SELECT
10
+ c.relname AS name,
11
+ CASE c.relkind WHEN 'r' THEN 'table' WHEN 'p' THEN 'partitioned' END AS type,
12
+ c.reltuples::bigint AS rows,
13
+ pg_total_relation_size(c.oid)::bigint AS size_bytes
14
+ FROM pg_class c
15
+ JOIN pg_namespace n ON n.oid = c.relnamespace
16
+ WHERE n.nspname = $1
17
+ AND c.relkind IN ('r', 'p')
18
+ ORDER BY pg_total_relation_size(c.oid) DESC
19
+ LIMIT 100
20
+ `,
21
+ params: [schema],
22
+ });
23
+ }
24
+ else {
25
+ queries.push({
26
+ sql: `SELECT table_name AS name, 'table' AS type, NULL AS rows, NULL AS size_bytes
27
+ FROM information_schema.tables
28
+ WHERE table_schema = $1 AND table_type = 'BASE TABLE'
29
+ ORDER BY table_name LIMIT 100`,
30
+ params: [schema],
31
+ });
32
+ }
33
+ // Q1: Views
34
+ queries.push({
35
+ sql: `SELECT table_name FROM information_schema.views WHERE table_schema = $1 ORDER BY table_name`,
36
+ params: [schema],
37
+ });
38
+ // Q2: Materialized views (Postgres only, empty on others)
39
+ queries.push(isPg
40
+ ? { sql: `SELECT matviewname FROM pg_matviews WHERE schemaname = $1 ORDER BY matviewname`, params: [schema] }
41
+ : { sql: `SELECT null AS matviewname WHERE false` });
42
+ // Q3: ENUMs (Postgres only)
43
+ queries.push(isPg
44
+ ? {
45
+ sql: `SELECT t.typname AS name, array_agg(e.enumlabel ORDER BY e.enumsortorder) AS values
46
+ FROM pg_type t
47
+ JOIN pg_enum e ON e.enumtypid = t.oid
48
+ JOIN pg_namespace n ON n.oid = t.typnamespace
49
+ WHERE n.nspname = $1
50
+ GROUP BY t.typname ORDER BY t.typname`,
51
+ params: [schema],
52
+ }
53
+ : { sql: `SELECT null AS name, null AS values WHERE false` });
54
+ // Q4: Extensions (Postgres only, database-wide)
55
+ queries.push(isPg
56
+ ? { sql: `SELECT extname FROM pg_extension ORDER BY extname` }
57
+ : { sql: `SELECT null AS extname WHERE false` });
58
+ // Q5: Function count
59
+ queries.push({
60
+ sql: `SELECT COUNT(*)::int AS count FROM information_schema.routines WHERE routine_schema = $1`,
61
+ params: [schema],
62
+ });
63
+ // Q6: Total table count
64
+ queries.push({
65
+ sql: `SELECT COUNT(*)::int AS count FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE'`,
66
+ params: [schema],
67
+ });
68
+ // Q7: Health — tables without PK (Postgres only)
69
+ queries.push(isPg
70
+ ? {
71
+ sql: `SELECT c.relname FROM pg_class c
72
+ JOIN pg_namespace n ON n.oid = c.relnamespace
73
+ WHERE n.nspname = $1 AND c.relkind = 'r'
74
+ AND NOT EXISTS (SELECT 1 FROM pg_constraint con WHERE con.conrelid = c.oid AND con.contype = 'p')
75
+ ORDER BY c.relname`,
76
+ params: [schema],
77
+ }
78
+ : { sql: `SELECT null AS relname WHERE false` });
79
+ // Q8: Health — RLS disabled count (Postgres only)
80
+ queries.push(isPg
81
+ ? {
82
+ sql: `SELECT COUNT(*)::int AS count FROM pg_class c
83
+ JOIN pg_namespace n ON n.oid = c.relnamespace
84
+ WHERE n.nspname = $1 AND c.relkind = 'r' AND NOT c.relrowsecurity`,
85
+ params: [schema],
86
+ }
87
+ : { sql: `SELECT 0 AS count` });
88
+ const results = await readOnlyMultiQuery(queries);
89
+ // Parse results
90
+ const tables = results[0].rows.map((r) => ({
91
+ name: r.name,
92
+ type: r.type,
93
+ rows: r.rows != null ? Number(r.rows) : null,
94
+ size: r.size_bytes != null ? formatBytes(Number(r.size_bytes)) : null,
95
+ }));
96
+ const views = results[1].rows.map((r) => r.table_name);
97
+ const matviews = results[2].rows.map((r) => r.matviewname);
98
+ const enumTypes = results[3].rows.map((r) => ({
99
+ name: r.name,
100
+ values: r.values,
101
+ }));
102
+ const extensions = results[4].rows.map((r) => r.extname);
103
+ const fnCount = results[5].rows[0]?.count ?? 0;
104
+ const totalTables = results[6].rows[0]?.count ?? 0;
105
+ // Health warnings
106
+ const warnings = [];
107
+ if (isPg) {
108
+ const noPkTables = results[7].rows.map((r) => r.relname);
109
+ if (noPkTables.length > 0) {
110
+ warnings.push(`Tables without primary key: ${noPkTables.join(", ")}`);
111
+ }
112
+ const noRlsCount = results[8].rows[0]?.count ?? 0;
113
+ if (noRlsCount > 0) {
114
+ warnings.push(`Tables with RLS disabled: ${noRlsCount} of ${totalTables}`);
115
+ }
116
+ }
117
+ return {
118
+ schema,
119
+ tables,
120
+ views,
121
+ materialized_views: matviews,
122
+ enum_types: enumTypes,
123
+ extensions,
124
+ counts: {
125
+ tables: totalTables,
126
+ views: views.length,
127
+ materialized_views: matviews.length,
128
+ functions: fnCount,
129
+ enum_types: enumTypes.length,
130
+ ...(totalTables > 100 ? { truncated: true } : {}),
131
+ },
132
+ health_warnings: warnings,
133
+ };
134
+ }
135
+ function formatBytes(bytes) {
136
+ if (bytes <= 0)
137
+ return "0 B";
138
+ const units = ["B", "KB", "MB", "GB", "TB"];
139
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
140
+ return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
141
+ }
142
+ //# sourceMappingURL=overview.js.map