@neverinfamous/postgres-mcp 1.2.0 → 2.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/README.md +202 -148
- package/dist/__tests__/benchmarks/codemode.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/codemode.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/codemode.bench.js +159 -0
- package/dist/__tests__/benchmarks/codemode.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.js +123 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.d.ts +11 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.js +199 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.d.ts +15 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.js +155 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.js +181 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.d.ts +11 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.js +209 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.d.ts +9 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.js +83 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.js +128 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/utilities.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/utilities.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/utilities.bench.js +164 -0
- package/dist/__tests__/benchmarks/utilities.bench.js.map +1 -0
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +12 -0
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.js +56 -3
- package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
- package/dist/adapters/postgresql/prompts/ltree.js +2 -2
- package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
- package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.js +10 -5
- package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.d.ts +45 -27
- package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.js +64 -26
- package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
- package/dist/adapters/postgresql/schemas/core.d.ts +53 -19
- package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/core.js +61 -17
- package/dist/adapters/postgresql/schemas/core.js.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
- package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.js +64 -44
- package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.d.ts +224 -110
- package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.js +245 -96
- package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
- package/dist/adapters/postgresql/schemas/index.d.ts +7 -6
- package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/index.js +16 -8
- package/dist/adapters/postgresql/schemas/index.js.map +1 -1
- package/dist/adapters/postgresql/schemas/introspection.d.ts +445 -0
- package/dist/adapters/postgresql/schemas/introspection.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/introspection.js +478 -0
- package/dist/adapters/postgresql/schemas/introspection.js.map +1 -0
- package/dist/adapters/postgresql/schemas/jsonb.d.ts +102 -42
- package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/jsonb.js +125 -30
- package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.d.ts +69 -36
- package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.js +98 -40
- package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.d.ts +21 -24
- package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.js +26 -14
- package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/schemas/partman.d.ts +69 -0
- package/dist/adapters/postgresql/schemas/partman.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partman.js +46 -33
- package/dist/adapters/postgresql/schemas/partman.js.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.d.ts +97 -49
- package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.js +139 -34
- package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
- package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
- package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/postgis.js +40 -0
- package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +50 -30
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.js +105 -33
- package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
- package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
- package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/stats.js +36 -20
- package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.d.ts +34 -19
- package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.js +52 -13
- package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
- package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/admin.js +272 -186
- package/dist/adapters/postgresql/tools/admin.js.map +1 -1
- package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/backup/dump.js +376 -350
- package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
- package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/citext.js +333 -243
- package/dist/adapters/postgresql/tools/citext.js.map +1 -1
- package/dist/adapters/postgresql/tools/codemode/index.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/codemode/index.js +2 -11
- package/dist/adapters/postgresql/tools/codemode/index.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
- package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/convenience.js +101 -19
- package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/health.js +18 -4
- package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.js +48 -6
- package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/objects.js +104 -85
- package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/query.js +100 -42
- package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.d.ts +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.js +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.js +72 -32
- package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
- package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/cron.js +333 -206
- package/dist/adapters/postgresql/tools/cron.js.map +1 -1
- package/dist/adapters/postgresql/tools/introspection.d.ts +15 -0
- package/dist/adapters/postgresql/tools/introspection.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/introspection.js +1682 -0
- package/dist/adapters/postgresql/tools/introspection.js.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/advanced.js +394 -297
- package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/basic.js +686 -398
- package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/kcache.js +278 -246
- package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.js +137 -38
- package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
- package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/monitoring.js +86 -55
- package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.js +79 -15
- package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.js +43 -56
- package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/operations.js +137 -24
- package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/analysis.js +276 -165
- package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
- package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/monitoring.js +52 -12
- package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
- package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/stats.js +182 -60
- package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
- package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/pgcrypto.js +277 -102
- package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/advanced.js +298 -230
- package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/basic.js +370 -251
- package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
- package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
- package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/schema.js +580 -233
- package/dist/adapters/postgresql/tools/schema.js.map +1 -1
- package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/stats/advanced.js +567 -506
- package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/stats/basic.js +340 -316
- package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/text.js +690 -337
- package/dist/adapters/postgresql/tools/text.js.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.js +157 -50
- package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.js +18 -0
- package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.js +100 -53
- package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
- package/dist/auth/auth-context.d.ts +28 -0
- package/dist/auth/auth-context.d.ts.map +1 -0
- package/dist/auth/auth-context.js +37 -0
- package/dist/auth/auth-context.js.map +1 -0
- package/dist/auth/scope-map.d.ts +20 -0
- package/dist/auth/scope-map.d.ts.map +1 -0
- package/dist/auth/scope-map.js +40 -0
- package/dist/auth/scope-map.js.map +1 -0
- package/dist/auth/scopes.d.ts.map +1 -1
- package/dist/auth/scopes.js +2 -0
- package/dist/auth/scopes.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/codemode/api.d.ts +1 -0
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +35 -1
- package/dist/codemode/api.js.map +1 -1
- package/dist/codemode/index.d.ts +0 -2
- package/dist/codemode/index.d.ts.map +1 -1
- package/dist/codemode/index.js +0 -4
- package/dist/codemode/index.js.map +1 -1
- package/dist/codemode/sandbox.d.ts +14 -1
- package/dist/codemode/sandbox.d.ts.map +1 -1
- package/dist/codemode/sandbox.js +58 -19
- package/dist/codemode/sandbox.js.map +1 -1
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/types.js +3 -0
- package/dist/codemode/types.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +117 -31
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +22 -19
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +48 -37
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +10 -13
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/pool/ConnectionPool.js +1 -1
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/transports/http.d.ts +1 -0
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +75 -21
- package/dist/transports/http.js.map +1 -1
- package/dist/types/filtering.d.ts +2 -2
- package/dist/types/filtering.d.ts.map +1 -1
- package/dist/utils/icons.d.ts.map +1 -1
- package/dist/utils/icons.js +5 -0
- package/dist/utils/icons.js.map +1 -1
- package/dist/utils/where-clause.d.ts.map +1 -1
- package/dist/utils/where-clause.js +24 -0
- package/dist/utils/where-clause.js.map +1 -1
- package/package.json +20 -13
- package/dist/codemode/sandbox-factory.d.ts +0 -72
- package/dist/codemode/sandbox-factory.d.ts.map +0 -1
- package/dist/codemode/sandbox-factory.js +0 -88
- package/dist/codemode/sandbox-factory.js.map +0 -1
- package/dist/codemode/worker-sandbox.d.ts +0 -82
- package/dist/codemode/worker-sandbox.d.ts.map +0 -1
- package/dist/codemode/worker-sandbox.js +0 -244
- package/dist/codemode/worker-sandbox.js.map +0 -1
- package/dist/codemode/worker-script.d.ts +0 -8
- package/dist/codemode/worker-script.d.ts.map +0 -1
- package/dist/codemode/worker-script.js +0 -113
- package/dist/codemode/worker-script.js.map +0 -1
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Text processing, FTS, trigrams, and fuzzy matching.
|
|
5
5
|
* 14 tools total.
|
|
6
6
|
*/
|
|
7
|
-
import { z } from "zod";
|
|
7
|
+
import { z, ZodError } from "zod";
|
|
8
8
|
import { readOnly, write } from "../../../utils/annotations.js";
|
|
9
9
|
import { getToolIcons } from "../../../utils/icons.js";
|
|
10
|
+
import { formatPostgresError } from "./core/error-helpers.js";
|
|
10
11
|
import { sanitizeIdentifier, sanitizeIdentifiers, sanitizeTableName, } from "../../../utils/identifiers.js";
|
|
11
12
|
import { sanitizeFtsConfig } from "../../../utils/fts-config.js";
|
|
12
13
|
import { sanitizeWhereClause } from "../../../utils/where-clause.js";
|
|
@@ -43,43 +44,80 @@ function createTextSearchTool(adapter) {
|
|
|
43
44
|
annotations: readOnly("Full-Text Search"),
|
|
44
45
|
icons: getToolIcons("text", readOnly("Full-Text Search")),
|
|
45
46
|
handler: async (params, _context) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
47
|
+
try {
|
|
48
|
+
const parsed = TextSearchSchema.parse(params);
|
|
49
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
50
|
+
// Handle both column (string) and columns (array) parameters
|
|
51
|
+
// The preprocessor converts column → columns, but we handle both for safety
|
|
52
|
+
let cols;
|
|
53
|
+
if (parsed.columns !== undefined && parsed.columns.length > 0) {
|
|
54
|
+
cols = parsed.columns;
|
|
55
|
+
}
|
|
56
|
+
else if (parsed.column !== undefined) {
|
|
57
|
+
cols = [parsed.column];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: "Either 'columns' (array) or 'column' (string) is required",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Build qualified table name with schema support
|
|
66
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
67
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
68
|
+
if (!resolvedTable) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
error: "Either 'table' or 'tableName' is required",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
75
|
+
const sanitizedCols = sanitizeIdentifiers(cols);
|
|
76
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
77
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
78
|
+
: "*";
|
|
79
|
+
const tsvector = sanitizedCols
|
|
80
|
+
.map((c) => `coalesce(${c}, '')`)
|
|
81
|
+
.join(" || ' ' || ");
|
|
82
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
83
|
+
const limitVal = parsed.limit === 0
|
|
84
|
+
? null
|
|
85
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
86
|
+
? parsed.limit
|
|
87
|
+
: 100;
|
|
88
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
89
|
+
const sql = `SELECT ${selectCols}, ts_rank_cd(to_tsvector('${cfg}', ${tsvector}), plainto_tsquery('${cfg}', $1)) as rank
|
|
78
90
|
FROM ${tableName}
|
|
79
91
|
WHERE to_tsvector('${cfg}', ${tsvector}) @@ plainto_tsquery('${cfg}', $1)
|
|
80
92
|
ORDER BY rank DESC${limitClause}`;
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
const result = await adapter.executeQuery(sql, [parsed.query]);
|
|
94
|
+
const count = result.rows?.length ?? 0;
|
|
95
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
96
|
+
return {
|
|
97
|
+
rows: result.rows,
|
|
98
|
+
count,
|
|
99
|
+
...(truncated
|
|
100
|
+
? {
|
|
101
|
+
truncated: true,
|
|
102
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
103
|
+
}
|
|
104
|
+
: {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof ZodError) {
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
error: `pg_text_search validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
error: formatPostgresError(error, {
|
|
117
|
+
tool: "pg_text_search",
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
83
121
|
},
|
|
84
122
|
};
|
|
85
123
|
}
|
|
@@ -115,42 +153,79 @@ function createTextRankTool(adapter) {
|
|
|
115
153
|
annotations: readOnly("Text Rank"),
|
|
116
154
|
icons: getToolIcons("text", readOnly("Text Rank")),
|
|
117
155
|
handler: async (params, _context) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
156
|
+
try {
|
|
157
|
+
const parsed = TextRankSchema.parse(params);
|
|
158
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
159
|
+
const norm = parsed.normalization ?? 0;
|
|
160
|
+
// Handle both column (string) and columns (array) parameters
|
|
161
|
+
let cols;
|
|
162
|
+
if (parsed.columns !== undefined && parsed.columns.length > 0) {
|
|
163
|
+
cols = parsed.columns;
|
|
164
|
+
}
|
|
165
|
+
else if (parsed.column !== undefined) {
|
|
166
|
+
cols = [parsed.column];
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
return {
|
|
170
|
+
success: false,
|
|
171
|
+
error: "Either column or columns parameter is required",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
175
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
176
|
+
if (!resolvedTable) {
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
error: "Either 'table' or 'tableName' is required",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
183
|
+
const sanitizedCols = sanitizeIdentifiers(cols);
|
|
184
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
185
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
186
|
+
: "*";
|
|
187
|
+
const tsvector = sanitizedCols
|
|
188
|
+
.map((c) => `coalesce(${c}, '')`)
|
|
189
|
+
.join(" || ' ' || ");
|
|
190
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
191
|
+
const limitVal = parsed.limit === 0
|
|
192
|
+
? null
|
|
193
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
194
|
+
? parsed.limit
|
|
195
|
+
: 100;
|
|
196
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
197
|
+
const sql = `SELECT ${selectCols}, ts_rank_cd(to_tsvector('${cfg}', ${tsvector}), plainto_tsquery('${cfg}', $1), ${String(norm)}) as rank
|
|
149
198
|
FROM ${tableName}
|
|
150
199
|
WHERE to_tsvector('${cfg}', ${tsvector}) @@ plainto_tsquery('${cfg}', $1)
|
|
151
200
|
ORDER BY rank DESC${limitClause}`;
|
|
152
|
-
|
|
153
|
-
|
|
201
|
+
const result = await adapter.executeQuery(sql, [parsed.query]);
|
|
202
|
+
const count = result.rows?.length ?? 0;
|
|
203
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
204
|
+
return {
|
|
205
|
+
rows: result.rows,
|
|
206
|
+
count,
|
|
207
|
+
...(truncated
|
|
208
|
+
? {
|
|
209
|
+
truncated: true,
|
|
210
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
211
|
+
}
|
|
212
|
+
: {}),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (error instanceof ZodError) {
|
|
217
|
+
return {
|
|
218
|
+
success: false,
|
|
219
|
+
error: `pg_text_rank validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: formatPostgresError(error, {
|
|
225
|
+
tool: "pg_text_rank",
|
|
226
|
+
}),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
154
229
|
},
|
|
155
230
|
};
|
|
156
231
|
}
|
|
@@ -164,29 +239,64 @@ function createTrigramSimilarityTool(adapter) {
|
|
|
164
239
|
annotations: readOnly("Trigram Similarity"),
|
|
165
240
|
icons: getToolIcons("text", readOnly("Trigram Similarity")),
|
|
166
241
|
handler: async (params, _context) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
242
|
+
try {
|
|
243
|
+
const parsed = TrigramSimilaritySchema.parse(params);
|
|
244
|
+
const thresh = parsed.threshold ?? 0.3;
|
|
245
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
246
|
+
const limitVal = parsed.limit === 0
|
|
247
|
+
? null
|
|
248
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
249
|
+
? parsed.limit
|
|
250
|
+
: 100;
|
|
251
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
252
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
253
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
254
|
+
if (!resolvedTable) {
|
|
255
|
+
return {
|
|
256
|
+
success: false,
|
|
257
|
+
error: "Either 'table' or 'tableName' is required",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
261
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
262
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
263
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
264
|
+
: "*";
|
|
265
|
+
const additionalWhere = parsed.where
|
|
266
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
267
|
+
: "";
|
|
268
|
+
const sql = `SELECT ${selectCols}, similarity(${columnName}, $1) as similarity
|
|
185
269
|
FROM ${tableName}
|
|
186
270
|
WHERE similarity(${columnName}, $1) > ${String(thresh)}${additionalWhere}
|
|
187
|
-
ORDER BY similarity DESC
|
|
188
|
-
|
|
189
|
-
|
|
271
|
+
ORDER BY similarity DESC${limitClause}`;
|
|
272
|
+
const result = await adapter.executeQuery(sql, [parsed.value]);
|
|
273
|
+
const count = result.rows?.length ?? 0;
|
|
274
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
275
|
+
return {
|
|
276
|
+
rows: result.rows,
|
|
277
|
+
count,
|
|
278
|
+
...(truncated
|
|
279
|
+
? {
|
|
280
|
+
truncated: true,
|
|
281
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
282
|
+
}
|
|
283
|
+
: {}),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
if (error instanceof ZodError) {
|
|
288
|
+
return {
|
|
289
|
+
success: false,
|
|
290
|
+
error: `pg_trigram_similarity validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
error: formatPostgresError(error, {
|
|
296
|
+
tool: "pg_trigram_similarity",
|
|
297
|
+
}),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
190
300
|
},
|
|
191
301
|
};
|
|
192
302
|
}
|
|
@@ -198,7 +308,10 @@ function createFuzzyMatchTool(adapter) {
|
|
|
198
308
|
tableName: z.string().optional().describe("Table name (alias for table)"),
|
|
199
309
|
column: z.string(),
|
|
200
310
|
value: z.string(),
|
|
201
|
-
method: z
|
|
311
|
+
method: z
|
|
312
|
+
.string()
|
|
313
|
+
.optional()
|
|
314
|
+
.describe("Fuzzy match method (default: levenshtein). Valid: soundex, levenshtein, metaphone"),
|
|
202
315
|
maxDistance: z
|
|
203
316
|
.number()
|
|
204
317
|
.optional()
|
|
@@ -225,37 +338,84 @@ function createFuzzyMatchTool(adapter) {
|
|
|
225
338
|
annotations: readOnly("Fuzzy Match"),
|
|
226
339
|
icons: getToolIcons("text", readOnly("Fuzzy Match")),
|
|
227
340
|
handler: async (params, _context) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
341
|
+
try {
|
|
342
|
+
const parsed = FuzzyMatchSchema.parse(params);
|
|
343
|
+
// Validate method (moved from z.enum to handler for structured error)
|
|
344
|
+
const VALID_METHODS = [
|
|
345
|
+
"levenshtein",
|
|
346
|
+
"soundex",
|
|
347
|
+
"metaphone",
|
|
348
|
+
];
|
|
349
|
+
const rawMethod = parsed.method ?? "levenshtein";
|
|
350
|
+
if (!VALID_METHODS.includes(rawMethod)) {
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
error: `Invalid method "${rawMethod}". Valid methods: ${VALID_METHODS.join(", ")}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const method = rawMethod;
|
|
357
|
+
const maxDist = parsed.maxDistance ?? 3;
|
|
358
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
359
|
+
const limitVal = parsed.limit === 0
|
|
360
|
+
? null
|
|
361
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
362
|
+
? parsed.limit
|
|
363
|
+
: 100;
|
|
364
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
365
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
366
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
367
|
+
if (!resolvedTable) {
|
|
368
|
+
return {
|
|
369
|
+
success: false,
|
|
370
|
+
error: "Either 'table' or 'tableName' is required",
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
374
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
375
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
376
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
377
|
+
: "*";
|
|
378
|
+
const additionalWhere = parsed.where
|
|
379
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
380
|
+
: "";
|
|
381
|
+
let sql;
|
|
382
|
+
if (method === "soundex") {
|
|
383
|
+
sql = `SELECT ${selectCols}, soundex(${columnName}) as code FROM ${tableName} WHERE soundex(${columnName}) = soundex($1)${additionalWhere}${limitClause}`;
|
|
384
|
+
}
|
|
385
|
+
else if (method === "metaphone") {
|
|
386
|
+
sql = `SELECT ${selectCols}, metaphone(${columnName}, 10) as code FROM ${tableName} WHERE metaphone(${columnName}, 10) = metaphone($1, 10)${additionalWhere}${limitClause}`;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
sql = `SELECT ${selectCols}, levenshtein(${columnName}, $1) as distance FROM ${tableName} WHERE levenshtein(${columnName}, $1) <= ${String(maxDist)}${additionalWhere} ORDER BY distance${limitClause}`;
|
|
390
|
+
}
|
|
391
|
+
const result = await adapter.executeQuery(sql, [parsed.value]);
|
|
392
|
+
const count = result.rows?.length ?? 0;
|
|
393
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
394
|
+
return {
|
|
395
|
+
rows: result.rows,
|
|
396
|
+
count,
|
|
397
|
+
...(truncated
|
|
398
|
+
? {
|
|
399
|
+
truncated: true,
|
|
400
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
401
|
+
}
|
|
402
|
+
: {}),
|
|
403
|
+
};
|
|
238
404
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
405
|
+
catch (error) {
|
|
406
|
+
if (error instanceof ZodError) {
|
|
407
|
+
return {
|
|
408
|
+
success: false,
|
|
409
|
+
error: `pg_fuzzy_match validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
success: false,
|
|
414
|
+
error: formatPostgresError(error, {
|
|
415
|
+
tool: "pg_fuzzy_match",
|
|
416
|
+
}),
|
|
417
|
+
};
|
|
250
418
|
}
|
|
251
|
-
else if (method === "metaphone") {
|
|
252
|
-
sql = `SELECT ${selectCols}, metaphone(${columnName}, 10) as code FROM ${tableName} WHERE metaphone(${columnName}, 10) = metaphone($1, 10)${additionalWhere} LIMIT ${String(limitVal)}`;
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
sql = `SELECT ${selectCols}, levenshtein(${columnName}, $1) as distance FROM ${tableName} WHERE levenshtein(${columnName}, $1) <= ${String(maxDist)}${additionalWhere} ORDER BY distance LIMIT ${String(limitVal)}`;
|
|
256
|
-
}
|
|
257
|
-
const result = await adapter.executeQuery(sql, [parsed.value]);
|
|
258
|
-
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
259
419
|
},
|
|
260
420
|
};
|
|
261
421
|
}
|
|
@@ -269,27 +429,61 @@ function createRegexpMatchTool(adapter) {
|
|
|
269
429
|
annotations: readOnly("Regexp Match"),
|
|
270
430
|
icons: getToolIcons("text", readOnly("Regexp Match")),
|
|
271
431
|
handler: async (params, _context) => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
432
|
+
try {
|
|
433
|
+
const parsed = RegexpMatchSchema.parse(params);
|
|
434
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
435
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
436
|
+
if (!resolvedTable) {
|
|
437
|
+
return {
|
|
438
|
+
success: false,
|
|
439
|
+
error: "Either 'table' or 'tableName' is required",
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
443
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
444
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
445
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
446
|
+
: "*";
|
|
447
|
+
const op = parsed.flags?.includes("i") ? "~*" : "~";
|
|
448
|
+
const additionalWhere = parsed.where
|
|
449
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
450
|
+
: "";
|
|
451
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
452
|
+
const limitVal = parsed.limit === 0
|
|
453
|
+
? null
|
|
454
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
455
|
+
? parsed.limit
|
|
456
|
+
: 100;
|
|
457
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
458
|
+
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
459
|
+
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
460
|
+
const count = result.rows?.length ?? 0;
|
|
461
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
462
|
+
return {
|
|
463
|
+
rows: result.rows,
|
|
464
|
+
count,
|
|
465
|
+
...(truncated
|
|
466
|
+
? {
|
|
467
|
+
truncated: true,
|
|
468
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
469
|
+
}
|
|
470
|
+
: {}),
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
if (error instanceof ZodError) {
|
|
475
|
+
return {
|
|
476
|
+
success: false,
|
|
477
|
+
error: `pg_regexp_match validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
return {
|
|
481
|
+
success: false,
|
|
482
|
+
error: formatPostgresError(error, {
|
|
483
|
+
tool: "pg_regexp_match",
|
|
484
|
+
}),
|
|
485
|
+
};
|
|
277
486
|
}
|
|
278
|
-
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
279
|
-
const columnName = sanitizeIdentifier(parsed.column);
|
|
280
|
-
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
281
|
-
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
282
|
-
: "*";
|
|
283
|
-
const op = parsed.flags?.includes("i") ? "~*" : "~";
|
|
284
|
-
const additionalWhere = parsed.where
|
|
285
|
-
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
286
|
-
: "";
|
|
287
|
-
// Default limit to 100 to prevent large payloads
|
|
288
|
-
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
289
|
-
const limitClause = ` LIMIT ${String(limitVal)}`;
|
|
290
|
-
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
291
|
-
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
292
|
-
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
293
487
|
},
|
|
294
488
|
};
|
|
295
489
|
}
|
|
@@ -327,27 +521,61 @@ function createLikeSearchTool(adapter) {
|
|
|
327
521
|
annotations: readOnly("LIKE Search"),
|
|
328
522
|
icons: getToolIcons("text", readOnly("LIKE Search")),
|
|
329
523
|
handler: async (params, _context) => {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
524
|
+
try {
|
|
525
|
+
const parsed = LikeSearchSchema.parse(params);
|
|
526
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
527
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
528
|
+
if (!resolvedTable) {
|
|
529
|
+
return {
|
|
530
|
+
success: false,
|
|
531
|
+
error: "Either 'table' or 'tableName' is required",
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
535
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
536
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
537
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
538
|
+
: "*";
|
|
539
|
+
const op = parsed.caseSensitive === true ? "LIKE" : "ILIKE";
|
|
540
|
+
const additionalWhere = parsed.where
|
|
541
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
542
|
+
: "";
|
|
543
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
544
|
+
const limitVal = parsed.limit === 0
|
|
545
|
+
? null
|
|
546
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
547
|
+
? parsed.limit
|
|
548
|
+
: 100;
|
|
549
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
550
|
+
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
551
|
+
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
552
|
+
const count = result.rows?.length ?? 0;
|
|
553
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
554
|
+
return {
|
|
555
|
+
rows: result.rows,
|
|
556
|
+
count,
|
|
557
|
+
...(truncated
|
|
558
|
+
? {
|
|
559
|
+
truncated: true,
|
|
560
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
561
|
+
}
|
|
562
|
+
: {}),
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
if (error instanceof ZodError) {
|
|
567
|
+
return {
|
|
568
|
+
success: false,
|
|
569
|
+
error: `pg_like_search validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
success: false,
|
|
574
|
+
error: formatPostgresError(error, {
|
|
575
|
+
tool: "pg_like_search",
|
|
576
|
+
}),
|
|
577
|
+
};
|
|
335
578
|
}
|
|
336
|
-
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
337
|
-
const columnName = sanitizeIdentifier(parsed.column);
|
|
338
|
-
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
339
|
-
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
340
|
-
: "*";
|
|
341
|
-
const op = parsed.caseSensitive === true ? "LIKE" : "ILIKE";
|
|
342
|
-
const additionalWhere = parsed.where
|
|
343
|
-
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
344
|
-
: "";
|
|
345
|
-
// Default limit to 100 to prevent large payloads
|
|
346
|
-
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
347
|
-
const limitClause = ` LIMIT ${String(limitVal)}`;
|
|
348
|
-
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
349
|
-
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
350
|
-
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
351
579
|
},
|
|
352
580
|
};
|
|
353
581
|
}
|
|
@@ -395,40 +623,74 @@ function createTextHeadlineTool(adapter) {
|
|
|
395
623
|
annotations: readOnly("Text Headline"),
|
|
396
624
|
icons: getToolIcons("text", readOnly("Text Headline")),
|
|
397
625
|
handler: async (params, _context) => {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
626
|
+
try {
|
|
627
|
+
const parsed = HeadlineSchema.parse(params);
|
|
628
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
629
|
+
// Build options string from individual params or use provided options
|
|
630
|
+
let opts;
|
|
631
|
+
if (parsed.options) {
|
|
632
|
+
opts = parsed.options;
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
const optParts = [];
|
|
636
|
+
optParts.push(`StartSel=${parsed.startSel ?? "<b>"}`);
|
|
637
|
+
optParts.push(`StopSel=${parsed.stopSel ?? "</b>"}`);
|
|
638
|
+
optParts.push(`MaxWords=${String(parsed.maxWords ?? 35)}`);
|
|
639
|
+
optParts.push(`MinWords=${String(parsed.minWords ?? 15)}`);
|
|
640
|
+
opts = optParts.join(", ");
|
|
641
|
+
}
|
|
642
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
643
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
644
|
+
if (!resolvedTable) {
|
|
645
|
+
return {
|
|
646
|
+
success: false,
|
|
647
|
+
error: "Either 'table' or 'tableName' is required",
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
651
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
652
|
+
// Use provided select columns, or default to * (user should specify PK for stable identification)
|
|
653
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
654
|
+
? sanitizeIdentifiers(parsed.select).join(", ") + ", "
|
|
655
|
+
: "";
|
|
656
|
+
// Default limit to 100 to prevent large payloads; limit: 0 means no limit
|
|
657
|
+
const limitVal = parsed.limit === 0
|
|
658
|
+
? null
|
|
659
|
+
: parsed.limit !== undefined && parsed.limit > 0
|
|
660
|
+
? parsed.limit
|
|
661
|
+
: 100;
|
|
662
|
+
const limitClause = limitVal !== null ? ` LIMIT ${String(limitVal)}` : "";
|
|
663
|
+
const sql = `SELECT ${selectCols}ts_headline('${cfg}', ${columnName}, plainto_tsquery('${cfg}', $1), '${opts}') as headline
|
|
428
664
|
FROM ${tableName}
|
|
429
665
|
WHERE to_tsvector('${cfg}', ${columnName}) @@ plainto_tsquery('${cfg}', $1)${limitClause}`;
|
|
430
|
-
|
|
431
|
-
|
|
666
|
+
const result = await adapter.executeQuery(sql, [parsed.query]);
|
|
667
|
+
const count = result.rows?.length ?? 0;
|
|
668
|
+
const truncated = limitVal !== null && count === limitVal;
|
|
669
|
+
return {
|
|
670
|
+
rows: result.rows,
|
|
671
|
+
count,
|
|
672
|
+
...(truncated
|
|
673
|
+
? {
|
|
674
|
+
truncated: true,
|
|
675
|
+
hint: `Results limited to ${String(limitVal)}. Use limit: 0 for all rows.`,
|
|
676
|
+
}
|
|
677
|
+
: {}),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
if (error instanceof ZodError) {
|
|
682
|
+
return {
|
|
683
|
+
success: false,
|
|
684
|
+
error: `pg_text_headline validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
success: false,
|
|
689
|
+
error: formatPostgresError(error, {
|
|
690
|
+
tool: "pg_text_headline",
|
|
691
|
+
}),
|
|
692
|
+
};
|
|
693
|
+
}
|
|
432
694
|
},
|
|
433
695
|
};
|
|
434
696
|
}
|
|
@@ -461,36 +723,55 @@ function createFtsIndexTool(adapter) {
|
|
|
461
723
|
annotations: write("Create FTS Index"),
|
|
462
724
|
icons: getToolIcons("text", write("Create FTS Index")),
|
|
463
725
|
handler: async (params, _context) => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
726
|
+
try {
|
|
727
|
+
const parsed = FtsIndexSchema.parse(params);
|
|
728
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
729
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
730
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
731
|
+
if (!resolvedTable) {
|
|
732
|
+
return {
|
|
733
|
+
success: false,
|
|
734
|
+
error: "Either 'table' or 'tableName' is required",
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
const defaultIndexName = `idx_${resolvedTable}_${parsed.column}_fts`;
|
|
738
|
+
const resolvedIndexName = parsed.name ?? defaultIndexName;
|
|
739
|
+
const indexName = sanitizeIdentifier(resolvedIndexName);
|
|
740
|
+
// Default to IF NOT EXISTS for safer operation (skip existing indexes)
|
|
741
|
+
const useIfNotExists = parsed.ifNotExists !== false;
|
|
742
|
+
const ifNotExists = useIfNotExists ? "IF NOT EXISTS " : "";
|
|
743
|
+
// Build qualified table name with schema support
|
|
744
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
745
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
746
|
+
// Check if index exists before creation (to accurately report 'skipped')
|
|
747
|
+
let existedBefore = false;
|
|
748
|
+
if (useIfNotExists) {
|
|
749
|
+
const checkResult = await adapter.executeQuery(`SELECT 1 FROM pg_indexes WHERE indexname = $1 LIMIT 1`, [resolvedIndexName]);
|
|
750
|
+
existedBefore = (checkResult.rows?.length ?? 0) > 0;
|
|
751
|
+
}
|
|
752
|
+
const sql = `CREATE INDEX ${ifNotExists}${indexName} ON ${tableName} USING gin(to_tsvector('${cfg}', ${columnName}))`;
|
|
753
|
+
await adapter.executeQuery(sql);
|
|
754
|
+
return {
|
|
755
|
+
success: true,
|
|
756
|
+
index: resolvedIndexName,
|
|
757
|
+
config: cfg,
|
|
758
|
+
skipped: existedBefore,
|
|
759
|
+
};
|
|
470
760
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
existedBefore = (checkResult.rows?.length ?? 0) > 0;
|
|
761
|
+
catch (error) {
|
|
762
|
+
if (error instanceof ZodError) {
|
|
763
|
+
return {
|
|
764
|
+
success: false,
|
|
765
|
+
error: `pg_create_fts_index validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
return {
|
|
769
|
+
success: false,
|
|
770
|
+
error: formatPostgresError(error, {
|
|
771
|
+
tool: "pg_create_fts_index",
|
|
772
|
+
}),
|
|
773
|
+
};
|
|
485
774
|
}
|
|
486
|
-
const sql = `CREATE INDEX ${ifNotExists}${indexName} ON ${tableName} USING gin(to_tsvector('${cfg}', ${columnName}))`;
|
|
487
|
-
await adapter.executeQuery(sql);
|
|
488
|
-
return {
|
|
489
|
-
success: true,
|
|
490
|
-
index: resolvedIndexName,
|
|
491
|
-
config: cfg,
|
|
492
|
-
skipped: existedBefore,
|
|
493
|
-
};
|
|
494
775
|
},
|
|
495
776
|
};
|
|
496
777
|
}
|
|
@@ -507,11 +788,27 @@ function createTextNormalizeTool(adapter) {
|
|
|
507
788
|
annotations: readOnly("Text Normalize"),
|
|
508
789
|
icons: getToolIcons("text", readOnly("Text Normalize")),
|
|
509
790
|
handler: async (params, _context) => {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
791
|
+
try {
|
|
792
|
+
const parsed = NormalizeSchema.parse(params ?? {});
|
|
793
|
+
// Ensure unaccent extension is available
|
|
794
|
+
await adapter.executeQuery("CREATE EXTENSION IF NOT EXISTS unaccent");
|
|
795
|
+
const result = await adapter.executeQuery(`SELECT unaccent($1) as normalized`, [parsed.text]);
|
|
796
|
+
return { normalized: result.rows?.[0]?.["normalized"] };
|
|
797
|
+
}
|
|
798
|
+
catch (error) {
|
|
799
|
+
if (error instanceof ZodError) {
|
|
800
|
+
return {
|
|
801
|
+
success: false,
|
|
802
|
+
error: `pg_text_normalize validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
return {
|
|
806
|
+
success: false,
|
|
807
|
+
error: formatPostgresError(error, {
|
|
808
|
+
tool: "pg_text_normalize",
|
|
809
|
+
}),
|
|
810
|
+
};
|
|
811
|
+
}
|
|
515
812
|
},
|
|
516
813
|
};
|
|
517
814
|
}
|
|
@@ -535,95 +832,109 @@ function createTextSentimentTool(_adapter) {
|
|
|
535
832
|
annotations: readOnly("Text Sentiment"),
|
|
536
833
|
icons: getToolIcons("text", readOnly("Text Sentiment")),
|
|
537
834
|
handler: (params, _context) => {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
835
|
+
try {
|
|
836
|
+
const parsed = SentimentSchema.parse(params ?? {});
|
|
837
|
+
const text = parsed.text.toLowerCase();
|
|
838
|
+
const positiveWords = [
|
|
839
|
+
"good",
|
|
840
|
+
"great",
|
|
841
|
+
"excellent",
|
|
842
|
+
"amazing",
|
|
843
|
+
"wonderful",
|
|
844
|
+
"fantastic",
|
|
845
|
+
"love",
|
|
846
|
+
"happy",
|
|
847
|
+
"positive",
|
|
848
|
+
"best",
|
|
849
|
+
"beautiful",
|
|
850
|
+
"awesome",
|
|
851
|
+
"perfect",
|
|
852
|
+
"nice",
|
|
853
|
+
"helpful",
|
|
854
|
+
"thank",
|
|
855
|
+
"thanks",
|
|
856
|
+
"pleased",
|
|
857
|
+
"satisfied",
|
|
858
|
+
"recommend",
|
|
859
|
+
"enjoy",
|
|
860
|
+
"impressive",
|
|
861
|
+
"brilliant",
|
|
862
|
+
];
|
|
863
|
+
const negativeWords = [
|
|
864
|
+
"bad",
|
|
865
|
+
"terrible",
|
|
866
|
+
"awful",
|
|
867
|
+
"horrible",
|
|
868
|
+
"worst",
|
|
869
|
+
"hate",
|
|
870
|
+
"angry",
|
|
871
|
+
"disappointed",
|
|
872
|
+
"poor",
|
|
873
|
+
"wrong",
|
|
874
|
+
"problem",
|
|
875
|
+
"issue",
|
|
876
|
+
"fail",
|
|
877
|
+
"failed",
|
|
878
|
+
"broken",
|
|
879
|
+
"useless",
|
|
880
|
+
"waste",
|
|
881
|
+
"frustrating",
|
|
882
|
+
"annoyed",
|
|
883
|
+
"unhappy",
|
|
884
|
+
"negative",
|
|
885
|
+
"complaint",
|
|
886
|
+
"slow",
|
|
887
|
+
];
|
|
888
|
+
const words = text.split(/\s+/);
|
|
889
|
+
const matchedPositive = words
|
|
890
|
+
.map((w) => w.replace(/[^a-z]/g, ""))
|
|
891
|
+
.filter((w) => positiveWords.includes(w));
|
|
892
|
+
const matchedNegative = words
|
|
893
|
+
.map((w) => w.replace(/[^a-z]/g, ""))
|
|
894
|
+
.filter((w) => negativeWords.includes(w));
|
|
895
|
+
const positiveScore = matchedPositive.length;
|
|
896
|
+
const negativeScore = matchedNegative.length;
|
|
897
|
+
const totalScore = positiveScore - negativeScore;
|
|
898
|
+
let sentiment;
|
|
899
|
+
if (totalScore > 2)
|
|
900
|
+
sentiment = "very_positive";
|
|
901
|
+
else if (totalScore > 0)
|
|
902
|
+
sentiment = "positive";
|
|
903
|
+
else if (totalScore < -2)
|
|
904
|
+
sentiment = "very_negative";
|
|
905
|
+
else if (totalScore < 0)
|
|
906
|
+
sentiment = "negative";
|
|
907
|
+
else
|
|
908
|
+
sentiment = "neutral";
|
|
909
|
+
const result = {
|
|
910
|
+
sentiment,
|
|
911
|
+
score: totalScore,
|
|
912
|
+
positiveCount: positiveScore,
|
|
913
|
+
negativeCount: negativeScore,
|
|
914
|
+
confidence: positiveScore + negativeScore > 3
|
|
915
|
+
? "high"
|
|
916
|
+
: positiveScore + negativeScore > 1
|
|
917
|
+
? "medium"
|
|
918
|
+
: "low",
|
|
919
|
+
};
|
|
920
|
+
if (parsed.returnWords) {
|
|
921
|
+
result.matchedPositive = matchedPositive;
|
|
922
|
+
result.matchedNegative = matchedNegative;
|
|
923
|
+
}
|
|
924
|
+
return Promise.resolve(result);
|
|
925
|
+
}
|
|
926
|
+
catch (error) {
|
|
927
|
+
if (error instanceof ZodError) {
|
|
928
|
+
return Promise.resolve({
|
|
929
|
+
success: false,
|
|
930
|
+
error: `pg_text_sentiment validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
return Promise.resolve({
|
|
934
|
+
success: false,
|
|
935
|
+
error: error instanceof Error ? error.message : "Unknown error occurred",
|
|
936
|
+
});
|
|
625
937
|
}
|
|
626
|
-
return Promise.resolve(result);
|
|
627
938
|
},
|
|
628
939
|
};
|
|
629
940
|
}
|
|
@@ -647,10 +958,26 @@ function createTextToVectorTool(adapter) {
|
|
|
647
958
|
annotations: readOnly("Text to Vector"),
|
|
648
959
|
icons: getToolIcons("text", readOnly("Text to Vector")),
|
|
649
960
|
handler: async (params, _context) => {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
961
|
+
try {
|
|
962
|
+
const parsed = ToVectorSchema.parse(params ?? {});
|
|
963
|
+
const cfg = parsed.config ?? "english";
|
|
964
|
+
const result = await adapter.executeQuery(`SELECT to_tsvector($1, $2) as vector`, [cfg, parsed.text]);
|
|
965
|
+
return { vector: result.rows?.[0]?.["vector"] };
|
|
966
|
+
}
|
|
967
|
+
catch (error) {
|
|
968
|
+
if (error instanceof ZodError) {
|
|
969
|
+
return {
|
|
970
|
+
success: false,
|
|
971
|
+
error: `pg_text_to_vector validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
success: false,
|
|
976
|
+
error: formatPostgresError(error, {
|
|
977
|
+
tool: "pg_text_to_vector",
|
|
978
|
+
}),
|
|
979
|
+
};
|
|
980
|
+
}
|
|
654
981
|
},
|
|
655
982
|
};
|
|
656
983
|
}
|
|
@@ -678,22 +1005,38 @@ function createTextToQueryTool(adapter) {
|
|
|
678
1005
|
annotations: readOnly("Text to Query"),
|
|
679
1006
|
icons: getToolIcons("text", readOnly("Text to Query")),
|
|
680
1007
|
handler: async (params, _context) => {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1008
|
+
try {
|
|
1009
|
+
const parsed = ToQuerySchema.parse(params ?? {});
|
|
1010
|
+
const cfg = parsed.config ?? "english";
|
|
1011
|
+
const mode = parsed.mode ?? "plain";
|
|
1012
|
+
let fn;
|
|
1013
|
+
switch (mode) {
|
|
1014
|
+
case "phrase":
|
|
1015
|
+
fn = "phraseto_tsquery";
|
|
1016
|
+
break;
|
|
1017
|
+
case "websearch":
|
|
1018
|
+
fn = "websearch_to_tsquery";
|
|
1019
|
+
break;
|
|
1020
|
+
default:
|
|
1021
|
+
fn = "plainto_tsquery";
|
|
1022
|
+
}
|
|
1023
|
+
const result = await adapter.executeQuery(`SELECT ${fn}($1, $2) as query`, [cfg, parsed.text]);
|
|
1024
|
+
return { query: result.rows?.[0]?.["query"], mode };
|
|
1025
|
+
}
|
|
1026
|
+
catch (error) {
|
|
1027
|
+
if (error instanceof ZodError) {
|
|
1028
|
+
return {
|
|
1029
|
+
success: false,
|
|
1030
|
+
error: `pg_text_to_query validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
success: false,
|
|
1035
|
+
error: formatPostgresError(error, {
|
|
1036
|
+
tool: "pg_text_to_query",
|
|
1037
|
+
}),
|
|
1038
|
+
};
|
|
694
1039
|
}
|
|
695
|
-
const result = await adapter.executeQuery(`SELECT ${fn}($1, $2) as query`, [cfg, parsed.text]);
|
|
696
|
-
return { query: result.rows?.[0]?.["query"], mode };
|
|
697
1040
|
},
|
|
698
1041
|
};
|
|
699
1042
|
}
|
|
@@ -710,8 +1053,9 @@ function createTextSearchConfigTool(adapter) {
|
|
|
710
1053
|
annotations: readOnly("Search Configurations"),
|
|
711
1054
|
icons: getToolIcons("text", readOnly("Search Configurations")),
|
|
712
1055
|
handler: async (_params, _context) => {
|
|
713
|
-
|
|
714
|
-
|
|
1056
|
+
try {
|
|
1057
|
+
const result = await adapter.executeQuery(`
|
|
1058
|
+
SELECT
|
|
715
1059
|
c.cfgname as name,
|
|
716
1060
|
n.nspname as schema,
|
|
717
1061
|
obj_description(c.oid, 'pg_ts_config') as description
|
|
@@ -719,10 +1063,19 @@ function createTextSearchConfigTool(adapter) {
|
|
|
719
1063
|
JOIN pg_namespace n ON n.oid = c.cfgnamespace
|
|
720
1064
|
ORDER BY c.cfgname
|
|
721
1065
|
`);
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
1066
|
+
return {
|
|
1067
|
+
configs: result.rows ?? [],
|
|
1068
|
+
count: result.rows?.length ?? 0,
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
catch (error) {
|
|
1072
|
+
return {
|
|
1073
|
+
success: false,
|
|
1074
|
+
error: formatPostgresError(error, {
|
|
1075
|
+
tool: "pg_text_search_config",
|
|
1076
|
+
}),
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
726
1079
|
},
|
|
727
1080
|
};
|
|
728
1081
|
}
|