@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
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Core spatial tools: extension, geometry_column, point_in_polygon, distance, buffer, intersection, bounding_box, spatial_index.
|
|
5
5
|
*/
|
|
6
|
-
import { z } from "zod";
|
|
6
|
+
import { z, ZodError } from "zod";
|
|
7
7
|
import { readOnly, write } from "../../../../utils/annotations.js";
|
|
8
8
|
import { getToolIcons } from "../../../../utils/icons.js";
|
|
9
|
+
import { parsePostgresError, formatPostgresError, } from "../core/error-helpers.js";
|
|
9
10
|
import { sanitizeIdentifier, sanitizeTableName, } from "../../../../utils/identifiers.js";
|
|
11
|
+
import { sanitizeWhereClause } from "../../../../utils/where-clause.js";
|
|
10
12
|
import { GeometryColumnSchemaBase, GeometryColumnSchema, GeometryDistanceSchemaBase, GeometryDistanceSchema, PointInPolygonSchemaBase, PointInPolygonSchema, SpatialIndexSchemaBase, SpatialIndexSchema, BufferSchemaBase, BufferSchema, IntersectionSchemaBase, IntersectionSchema, BoundingBoxSchemaBase, BoundingBoxSchema,
|
|
11
13
|
// Output schemas
|
|
12
14
|
PostgisCreateExtensionOutputSchema, GeometryColumnOutputSchema, PointInPolygonOutputSchema, DistanceOutputSchema, BufferOutputSchema, IntersectionOutputSchema, BoundingBoxOutputSchema, SpatialIndexOutputSchema, } from "../../schemas/index.js";
|
|
@@ -80,8 +82,14 @@ export function createGeometryColumnTool(adapter) {
|
|
|
80
82
|
suggestion: "Create the table first, then add the geometry column.",
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
|
-
const sql = `SELECT AddGeometryColumn(
|
|
84
|
-
await adapter.executeQuery(sql
|
|
85
|
+
const sql = `SELECT AddGeometryColumn($1, $2, $3, $4, $5, 2)`;
|
|
86
|
+
await adapter.executeQuery(sql, [
|
|
87
|
+
schemaName,
|
|
88
|
+
parsed.table,
|
|
89
|
+
parsed.column,
|
|
90
|
+
srid,
|
|
91
|
+
geomType,
|
|
92
|
+
]);
|
|
85
93
|
return {
|
|
86
94
|
success: true,
|
|
87
95
|
table: parsed.table,
|
|
@@ -102,47 +110,65 @@ export function createPointInPolygonTool(adapter) {
|
|
|
102
110
|
annotations: readOnly("Point in Polygon"),
|
|
103
111
|
icons: getToolIcons("postgis", readOnly("Point in Polygon")),
|
|
104
112
|
handler: async (params, _context) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
113
|
+
try {
|
|
114
|
+
const { table, column, point, schema } = PointInPolygonSchema.parse(params ?? {});
|
|
115
|
+
const schemaName = schema ?? "public";
|
|
116
|
+
const tableName = sanitizeTableName(table, schemaName !== "public" ? schemaName : undefined);
|
|
117
|
+
const columnName = sanitizeIdentifier(column);
|
|
118
|
+
// Check geometry type and warn if not polygon
|
|
119
|
+
const typeCheckSql = `SELECT DISTINCT GeometryType(${columnName}) as geom_type FROM ${tableName} WHERE ${columnName} IS NOT NULL LIMIT 1`;
|
|
120
|
+
const typeResult = await adapter.executeQuery(typeCheckSql);
|
|
121
|
+
const geomType = typeResult.rows?.[0]?.["geom_type"];
|
|
122
|
+
const isPolygonType = geomType?.toUpperCase()?.includes("POLYGON") ?? false;
|
|
123
|
+
// Get non-geometry columns to avoid returning raw WKB
|
|
124
|
+
const colQuery = `
|
|
125
|
+
SELECT column_name FROM information_schema.columns
|
|
126
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
127
|
+
AND udt_name NOT IN ('geometry', 'geography')
|
|
128
|
+
ORDER BY ordinal_position
|
|
129
|
+
`;
|
|
130
|
+
const colResult = await adapter.executeQuery(colQuery, [
|
|
131
|
+
schemaName,
|
|
132
|
+
table,
|
|
133
|
+
]);
|
|
134
|
+
const nonGeomCols = (colResult.rows ?? [])
|
|
135
|
+
.map((row) => sanitizeIdentifier(String(row["column_name"])))
|
|
136
|
+
.join(", ");
|
|
137
|
+
// Select non-geometry columns + readable geometry representation
|
|
138
|
+
const selectCols = nonGeomCols.length > 0
|
|
139
|
+
? `${nonGeomCols}, ST_AsText(${columnName}) as geometry_text`
|
|
140
|
+
: `ST_AsText(${columnName}) as geometry_text`;
|
|
141
|
+
const sql = `SELECT ${selectCols}
|
|
142
|
+
FROM ${tableName}
|
|
143
|
+
WHERE ST_Contains(${columnName}, ST_SetSRID(ST_MakePoint($1, $2), 4326))`;
|
|
144
|
+
const result = await adapter.executeQuery(sql, [point.lng, point.lat]);
|
|
145
|
+
const response = {
|
|
146
|
+
containingPolygons: result.rows,
|
|
147
|
+
count: result.rows?.length ?? 0,
|
|
148
|
+
};
|
|
149
|
+
// Add warning if geometry type is not polygon
|
|
150
|
+
if (!isPolygonType && geomType !== undefined) {
|
|
151
|
+
response["warning"] =
|
|
152
|
+
`Column "${column}" contains ${geomType} geometries, not polygons. ST_Contains requires polygons to produce meaningful results.`;
|
|
153
|
+
}
|
|
154
|
+
return response;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (error instanceof ZodError) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
error: error.issues.map((i) => i.message).join("; "),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
error: formatPostgresError(error, {
|
|
166
|
+
tool: "pg_point_in_polygon",
|
|
167
|
+
table: params?.["table"] ??
|
|
168
|
+
undefined,
|
|
169
|
+
}),
|
|
170
|
+
};
|
|
144
171
|
}
|
|
145
|
-
return response;
|
|
146
172
|
},
|
|
147
173
|
};
|
|
148
174
|
}
|
|
@@ -156,34 +182,35 @@ export function createDistanceTool(adapter) {
|
|
|
156
182
|
annotations: readOnly("Distance Search"),
|
|
157
183
|
icons: getToolIcons("postgis", readOnly("Distance Search")),
|
|
158
184
|
handler: async (params, _context) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
try {
|
|
186
|
+
const { table, column, point, limit, maxDistance, schema } = GeometryDistanceSchema.parse(params);
|
|
187
|
+
const schemaName = schema ?? "public";
|
|
188
|
+
const tableName = sanitizeTableName(table, schemaName !== "public" ? schemaName : undefined);
|
|
189
|
+
const columnName = sanitizeIdentifier(column);
|
|
190
|
+
const limitVal = limit ?? 10;
|
|
191
|
+
const distanceFilter = maxDistance !== undefined && maxDistance > 0
|
|
192
|
+
? `WHERE distance_meters <= ${String(maxDistance)}`
|
|
193
|
+
: "";
|
|
194
|
+
// Get non-geometry columns to avoid returning raw WKB
|
|
195
|
+
const colQuery = `
|
|
196
|
+
SELECT column_name FROM information_schema.columns
|
|
197
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
198
|
+
AND udt_name NOT IN ('geometry', 'geography')
|
|
199
|
+
ORDER BY ordinal_position
|
|
200
|
+
`;
|
|
201
|
+
const colResult = await adapter.executeQuery(colQuery, [
|
|
202
|
+
schemaName,
|
|
203
|
+
table,
|
|
204
|
+
]);
|
|
205
|
+
const nonGeomCols = (colResult.rows ?? [])
|
|
206
|
+
.map((row) => sanitizeIdentifier(String(row["column_name"])))
|
|
207
|
+
.join(", ");
|
|
208
|
+
// Select non-geometry columns + readable geometry representation + distance
|
|
209
|
+
const selectCols = nonGeomCols.length > 0
|
|
210
|
+
? `${nonGeomCols}, ST_AsText(${columnName}) as geometry_text, ST_Distance(${columnName}::geography, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography) as distance_meters`
|
|
211
|
+
: `ST_AsText(${columnName}) as geometry_text, ST_Distance(${columnName}::geography, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography) as distance_meters`;
|
|
212
|
+
// Use CTE for consistent distance calculation and filtering
|
|
213
|
+
const sql = `WITH distances AS (
|
|
187
214
|
SELECT ${selectCols}
|
|
188
215
|
FROM ${tableName}
|
|
189
216
|
)
|
|
@@ -191,8 +218,25 @@ export function createDistanceTool(adapter) {
|
|
|
191
218
|
${distanceFilter}
|
|
192
219
|
ORDER BY distance_meters
|
|
193
220
|
LIMIT ${String(limitVal)}`;
|
|
194
|
-
|
|
195
|
-
|
|
221
|
+
const result = await adapter.executeQuery(sql, [point.lng, point.lat]);
|
|
222
|
+
return { results: result.rows, count: result.rows?.length ?? 0 };
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof ZodError) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: error.issues.map((i) => i.message).join("; "),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
error: formatPostgresError(error, {
|
|
234
|
+
tool: "pg_distance",
|
|
235
|
+
table: params?.["table"] ??
|
|
236
|
+
undefined,
|
|
237
|
+
}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
196
240
|
},
|
|
197
241
|
};
|
|
198
242
|
}
|
|
@@ -206,62 +250,82 @@ export function createBufferTool(adapter) {
|
|
|
206
250
|
annotations: readOnly("Buffer Zone"),
|
|
207
251
|
icons: getToolIcons("postgis", readOnly("Buffer Zone")),
|
|
208
252
|
handler: async (params, _context) => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
//
|
|
237
|
-
bufferExpr = `
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
? `${nonGeomCols}, ST_AsText(${columnName}) as geometry_text, ST_AsGeoJSON(${bufferExpr}) as buffer_geojson`
|
|
242
|
-
: `ST_AsText(${columnName}) as geometry_text, ST_AsGeoJSON(${bufferExpr}) as buffer_geojson`;
|
|
243
|
-
const limitClause = effectiveLimit > 0 ? ` LIMIT ${String(effectiveLimit)}` : "";
|
|
244
|
-
const sql = `SELECT ${selectCols} FROM ${qualifiedTable}${whereClause}${limitClause}`;
|
|
245
|
-
const result = await adapter.executeQuery(sql, [parsed.distance]);
|
|
246
|
-
// Build response with truncation indicators if default limit was applied
|
|
247
|
-
const response = { results: result.rows };
|
|
248
|
-
// Check if results were truncated (works for both default and explicit limits)
|
|
249
|
-
if (effectiveLimit > 0) {
|
|
250
|
-
const countSql = `SELECT COUNT(*) as cnt FROM ${qualifiedTable}${whereClause}`;
|
|
251
|
-
const countResult = await adapter.executeQuery(countSql);
|
|
252
|
-
const totalCount = Number(countResult.rows?.[0]?.["cnt"] ?? 0);
|
|
253
|
-
if (totalCount > effectiveLimit) {
|
|
254
|
-
response["truncated"] = true;
|
|
255
|
-
response["totalCount"] = totalCount;
|
|
256
|
-
response["limit"] = effectiveLimit;
|
|
253
|
+
try {
|
|
254
|
+
const parsed = BufferSchema.parse(params ?? {});
|
|
255
|
+
const whereClause = parsed.where !== undefined
|
|
256
|
+
? ` WHERE ${sanitizeWhereClause(parsed.where)}`
|
|
257
|
+
: "";
|
|
258
|
+
const schemaName = parsed.schema ?? "public";
|
|
259
|
+
const qualifiedTable = sanitizeTableName(parsed.table, schemaName !== "public" ? schemaName : undefined);
|
|
260
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
261
|
+
// Default limit of 50 to prevent large payloads, use limit: 0 for all
|
|
262
|
+
const effectiveLimit = parsed.limit ?? 50;
|
|
263
|
+
// Get non-geometry columns to avoid returning raw WKB
|
|
264
|
+
const colQuery = `
|
|
265
|
+
SELECT column_name FROM information_schema.columns
|
|
266
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
267
|
+
AND udt_name NOT IN ('geometry', 'geography')
|
|
268
|
+
ORDER BY ordinal_position
|
|
269
|
+
`;
|
|
270
|
+
const colResult = await adapter.executeQuery(colQuery, [
|
|
271
|
+
schemaName,
|
|
272
|
+
parsed.table,
|
|
273
|
+
]);
|
|
274
|
+
const nonGeomCols = (colResult.rows ?? [])
|
|
275
|
+
.map((row) => sanitizeIdentifier(String(row["column_name"])))
|
|
276
|
+
.join(", ");
|
|
277
|
+
// Default simplify of 10m reduces polygon points for LLM-friendly payloads
|
|
278
|
+
// User can set simplify: 0 to disable or higher values for more aggressive reduction
|
|
279
|
+
const effectiveSimplify = parsed.simplify ?? 10;
|
|
280
|
+
// Build buffer expression with simplification (applied by default)
|
|
281
|
+
let bufferExpr = `ST_Buffer(${columnName}::geography, $1)::geometry`;
|
|
282
|
+
if (effectiveSimplify > 0) {
|
|
283
|
+
// SimplifyPreserveTopology maintains valid geometries
|
|
284
|
+
bufferExpr = `ST_SimplifyPreserveTopology(${bufferExpr}, ${String(effectiveSimplify)})`;
|
|
257
285
|
}
|
|
286
|
+
// Select non-geometry columns + readable geometry representations
|
|
287
|
+
const selectCols = nonGeomCols.length > 0
|
|
288
|
+
? `${nonGeomCols}, ST_AsText(${columnName}) as geometry_text, ST_AsGeoJSON(${bufferExpr}) as buffer_geojson`
|
|
289
|
+
: `ST_AsText(${columnName}) as geometry_text, ST_AsGeoJSON(${bufferExpr}) as buffer_geojson`;
|
|
290
|
+
const limitClause = effectiveLimit > 0 ? ` LIMIT ${String(effectiveLimit)}` : "";
|
|
291
|
+
const sql = `SELECT ${selectCols} FROM ${qualifiedTable}${whereClause}${limitClause}`;
|
|
292
|
+
const result = await adapter.executeQuery(sql, [parsed.distance]);
|
|
293
|
+
// Build response with truncation indicators if default limit was applied
|
|
294
|
+
const response = { results: result.rows };
|
|
295
|
+
// Check if results were truncated (works for both default and explicit limits)
|
|
296
|
+
if (effectiveLimit > 0) {
|
|
297
|
+
const countSql = `SELECT COUNT(*) as cnt FROM ${qualifiedTable}${whereClause}`;
|
|
298
|
+
const countResult = await adapter.executeQuery(countSql);
|
|
299
|
+
const totalCount = Number(countResult.rows?.[0]?.["cnt"] ?? 0);
|
|
300
|
+
if (totalCount > effectiveLimit) {
|
|
301
|
+
response["truncated"] = true;
|
|
302
|
+
response["totalCount"] = totalCount;
|
|
303
|
+
response["limit"] = effectiveLimit;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Add simplify indicator if simplification was applied
|
|
307
|
+
if (effectiveSimplify > 0) {
|
|
308
|
+
response["simplified"] = true;
|
|
309
|
+
response["simplifyTolerance"] = effectiveSimplify;
|
|
310
|
+
}
|
|
311
|
+
return response;
|
|
258
312
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (error instanceof ZodError) {
|
|
315
|
+
return {
|
|
316
|
+
success: false,
|
|
317
|
+
error: error.issues.map((i) => i.message).join("; "),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
success: false,
|
|
322
|
+
error: formatPostgresError(error, {
|
|
323
|
+
tool: "pg_buffer",
|
|
324
|
+
table: params?.["table"] ??
|
|
325
|
+
undefined,
|
|
326
|
+
}),
|
|
327
|
+
};
|
|
263
328
|
}
|
|
264
|
-
return response;
|
|
265
329
|
},
|
|
266
330
|
};
|
|
267
331
|
}
|
|
@@ -275,78 +339,98 @@ export function createIntersectionTool(adapter) {
|
|
|
275
339
|
annotations: readOnly("Intersection Search"),
|
|
276
340
|
icons: getToolIcons("postgis", readOnly("Intersection Search")),
|
|
277
341
|
handler: async (params, _context) => {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const colQuery = `
|
|
290
|
-
SELECT column_name FROM information_schema.columns
|
|
291
|
-
WHERE table_schema = $1 AND table_name = $2
|
|
292
|
-
AND udt_name NOT IN ('geometry', 'geography')
|
|
293
|
-
ORDER BY ordinal_position
|
|
294
|
-
`;
|
|
295
|
-
const colResult = await adapter.executeQuery(colQuery, [
|
|
296
|
-
schemaName,
|
|
297
|
-
parsed.table,
|
|
298
|
-
]);
|
|
299
|
-
const nonGeomCols = (colResult.rows ?? [])
|
|
300
|
-
.map((row) => sanitizeIdentifier(String(row["column_name"])))
|
|
301
|
-
.join(", ");
|
|
302
|
-
selectCols =
|
|
303
|
-
nonGeomCols.length > 0
|
|
304
|
-
? `${nonGeomCols}, ST_AsText(${columnName}) as geometry_text`
|
|
305
|
-
: `ST_AsText(${columnName}) as geometry_text`;
|
|
306
|
-
}
|
|
307
|
-
const isGeoJson = parsed.geometry.trim().startsWith("{");
|
|
308
|
-
// Auto-detect SRID from column if not provided and using WKT
|
|
309
|
-
let srid = parsed.srid;
|
|
310
|
-
if (!isGeoJson && srid === undefined) {
|
|
311
|
-
// Query the column's SRID from geometry_columns or geography_columns
|
|
312
|
-
const sridQuery = `
|
|
313
|
-
SELECT srid FROM geometry_columns
|
|
314
|
-
WHERE f_table_schema = $1 AND f_table_name = $2 AND f_geometry_column = $3
|
|
315
|
-
UNION
|
|
316
|
-
SELECT srid FROM geography_columns
|
|
317
|
-
WHERE f_table_schema = $1 AND f_table_name = $2 AND f_geography_column = $3
|
|
318
|
-
LIMIT 1
|
|
319
|
-
`;
|
|
320
|
-
const sridResult = await adapter.executeQuery(sridQuery, [
|
|
321
|
-
schemaName,
|
|
322
|
-
parsed.table,
|
|
323
|
-
parsed.column,
|
|
324
|
-
]);
|
|
325
|
-
const sridValue = sridResult.rows?.[0]?.["srid"];
|
|
326
|
-
if (sridValue !== undefined && sridValue !== null) {
|
|
327
|
-
srid = Number(sridValue);
|
|
342
|
+
try {
|
|
343
|
+
const parsed = IntersectionSchema.parse(params ?? {});
|
|
344
|
+
const schemaName = parsed.schema ?? "public";
|
|
345
|
+
const qualifiedTable = sanitizeTableName(parsed.table, schemaName !== "public" ? schemaName : undefined);
|
|
346
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
347
|
+
// Build select columns - user-specified or non-geometry columns to avoid raw WKB
|
|
348
|
+
let selectCols;
|
|
349
|
+
if (parsed.select !== undefined && parsed.select.length > 0) {
|
|
350
|
+
selectCols = parsed.select
|
|
351
|
+
.map((c) => sanitizeIdentifier(c))
|
|
352
|
+
.join(", ");
|
|
328
353
|
}
|
|
354
|
+
else {
|
|
355
|
+
// Get non-geometry columns to avoid returning raw WKB
|
|
356
|
+
const colQuery = `
|
|
357
|
+
SELECT column_name FROM information_schema.columns
|
|
358
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
359
|
+
AND udt_name NOT IN ('geometry', 'geography')
|
|
360
|
+
ORDER BY ordinal_position
|
|
361
|
+
`;
|
|
362
|
+
const colResult = await adapter.executeQuery(colQuery, [
|
|
363
|
+
schemaName,
|
|
364
|
+
parsed.table,
|
|
365
|
+
]);
|
|
366
|
+
const nonGeomCols = (colResult.rows ?? [])
|
|
367
|
+
.map((row) => sanitizeIdentifier(String(row["column_name"])))
|
|
368
|
+
.join(", ");
|
|
369
|
+
selectCols =
|
|
370
|
+
nonGeomCols.length > 0
|
|
371
|
+
? `${nonGeomCols}, ST_AsText(${columnName}) as geometry_text`
|
|
372
|
+
: `ST_AsText(${columnName}) as geometry_text`;
|
|
373
|
+
}
|
|
374
|
+
const isGeoJson = parsed.geometry.trim().startsWith("{");
|
|
375
|
+
// Auto-detect SRID from column if not provided and using WKT
|
|
376
|
+
let srid = parsed.srid;
|
|
377
|
+
if (!isGeoJson && srid === undefined) {
|
|
378
|
+
// Query the column's SRID from geometry_columns or geography_columns
|
|
379
|
+
const sridQuery = `
|
|
380
|
+
SELECT srid FROM geometry_columns
|
|
381
|
+
WHERE f_table_schema = $1 AND f_table_name = $2 AND f_geometry_column = $3
|
|
382
|
+
UNION
|
|
383
|
+
SELECT srid FROM geography_columns
|
|
384
|
+
WHERE f_table_schema = $1 AND f_table_name = $2 AND f_geography_column = $3
|
|
385
|
+
LIMIT 1
|
|
386
|
+
`;
|
|
387
|
+
const sridResult = await adapter.executeQuery(sridQuery, [
|
|
388
|
+
schemaName,
|
|
389
|
+
parsed.table,
|
|
390
|
+
parsed.column,
|
|
391
|
+
]);
|
|
392
|
+
const sridValue = sridResult.rows?.[0]?.["srid"];
|
|
393
|
+
if (sridValue !== undefined && sridValue !== null) {
|
|
394
|
+
srid = Number(sridValue);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// Build geometry expression with SRID if available
|
|
398
|
+
let geomExpr;
|
|
399
|
+
if (isGeoJson) {
|
|
400
|
+
geomExpr = `ST_GeomFromGeoJSON($1)`;
|
|
401
|
+
}
|
|
402
|
+
else if (srid !== undefined) {
|
|
403
|
+
geomExpr = `ST_SetSRID(ST_GeomFromText($1), ${String(srid)})`;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
geomExpr = `ST_GeomFromText($1)`;
|
|
407
|
+
}
|
|
408
|
+
const sql = `SELECT ${selectCols}
|
|
409
|
+
FROM ${qualifiedTable}
|
|
410
|
+
WHERE ST_Intersects(${columnName}, ${geomExpr})`;
|
|
411
|
+
const result = await adapter.executeQuery(sql, [parsed.geometry]);
|
|
412
|
+
return {
|
|
413
|
+
intersecting: result.rows,
|
|
414
|
+
count: result.rows?.length ?? 0,
|
|
415
|
+
sridUsed: srid ?? "none (explicit SRID in geometry or GeoJSON)",
|
|
416
|
+
};
|
|
329
417
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
418
|
+
catch (error) {
|
|
419
|
+
if (error instanceof ZodError) {
|
|
420
|
+
return {
|
|
421
|
+
success: false,
|
|
422
|
+
error: error.issues.map((i) => i.message).join("; "),
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
success: false,
|
|
427
|
+
error: formatPostgresError(error, {
|
|
428
|
+
tool: "pg_intersection",
|
|
429
|
+
table: params?.["table"] ??
|
|
430
|
+
undefined,
|
|
431
|
+
}),
|
|
432
|
+
};
|
|
340
433
|
}
|
|
341
|
-
const sql = `SELECT ${selectCols}
|
|
342
|
-
FROM ${qualifiedTable}
|
|
343
|
-
WHERE ST_Intersects(${columnName}, ${geomExpr})`;
|
|
344
|
-
const result = await adapter.executeQuery(sql, [parsed.geometry]);
|
|
345
|
-
return {
|
|
346
|
-
intersecting: result.rows,
|
|
347
|
-
count: result.rows?.length ?? 0,
|
|
348
|
-
sridUsed: srid ?? "none (explicit SRID in geometry or GeoJSON)",
|
|
349
|
-
};
|
|
350
434
|
},
|
|
351
435
|
};
|
|
352
436
|
}
|
|
@@ -360,64 +444,91 @@ export function createBoundingBoxTool(adapter) {
|
|
|
360
444
|
annotations: readOnly("Bounding Box Search"),
|
|
361
445
|
icons: getToolIcons("postgis", readOnly("Bounding Box Search")),
|
|
362
446
|
handler: async (params, _context) => {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
447
|
+
try {
|
|
448
|
+
const parsed = BoundingBoxSchema.parse(params ?? {});
|
|
449
|
+
const schemaName = parsed.schema ?? "public";
|
|
450
|
+
const qualifiedTable = sanitizeTableName(parsed.table, schemaName !== "public" ? schemaName : undefined);
|
|
451
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
452
|
+
// Build select columns - user-specified or non-geometry columns to avoid raw WKB
|
|
453
|
+
let selectCols;
|
|
454
|
+
if (parsed.select !== undefined && parsed.select.length > 0) {
|
|
455
|
+
selectCols = parsed.select
|
|
456
|
+
.map((c) => sanitizeIdentifier(c))
|
|
457
|
+
.join(", ");
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
// Get non-geometry columns to avoid returning raw WKB
|
|
461
|
+
const colQuery = `
|
|
462
|
+
SELECT column_name FROM information_schema.columns
|
|
463
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
464
|
+
AND udt_name NOT IN ('geometry', 'geography')
|
|
465
|
+
ORDER BY ordinal_position
|
|
466
|
+
`;
|
|
467
|
+
const colResult = await adapter.executeQuery(colQuery, [
|
|
468
|
+
schemaName,
|
|
469
|
+
parsed.table,
|
|
470
|
+
]);
|
|
471
|
+
selectCols = (colResult.rows ?? [])
|
|
472
|
+
.map((row) => sanitizeIdentifier(String(row["column_name"])))
|
|
473
|
+
.join(", ");
|
|
474
|
+
// If no columns found, table likely doesn't exist
|
|
475
|
+
if (selectCols.length === 0) {
|
|
476
|
+
return {
|
|
477
|
+
success: false,
|
|
478
|
+
error: `Table or view '${parsed.table}' not found in schema '${schemaName}'. Use pg_list_tables to see available tables.`,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// Auto-correct swapped bounds
|
|
483
|
+
const corrections = [];
|
|
484
|
+
let actualMinLng = parsed.minLng;
|
|
485
|
+
let actualMaxLng = parsed.maxLng;
|
|
486
|
+
let actualMinLat = parsed.minLat;
|
|
487
|
+
let actualMaxLat = parsed.maxLat;
|
|
488
|
+
if (parsed.minLng > parsed.maxLng) {
|
|
489
|
+
actualMinLng = parsed.maxLng;
|
|
490
|
+
actualMaxLng = parsed.minLng;
|
|
491
|
+
corrections.push("minLng/maxLng were swapped");
|
|
492
|
+
}
|
|
493
|
+
if (parsed.minLat > parsed.maxLat) {
|
|
494
|
+
actualMinLat = parsed.maxLat;
|
|
495
|
+
actualMaxLat = parsed.minLat;
|
|
496
|
+
corrections.push("minLat/maxLat were swapped");
|
|
497
|
+
}
|
|
498
|
+
const sql = `SELECT ${selectCols}, ST_AsText(${columnName}) as geometry_text
|
|
499
|
+
FROM ${qualifiedTable}
|
|
500
|
+
WHERE ${columnName} && ST_MakeEnvelope($1, $2, $3, $4, 4326)`;
|
|
501
|
+
const result = await adapter.executeQuery(sql, [
|
|
502
|
+
actualMinLng,
|
|
503
|
+
actualMinLat,
|
|
504
|
+
actualMaxLng,
|
|
505
|
+
actualMaxLat,
|
|
383
506
|
]);
|
|
384
|
-
|
|
385
|
-
.
|
|
386
|
-
.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
let actualMinLat = parsed.minLat;
|
|
393
|
-
let actualMaxLat = parsed.maxLat;
|
|
394
|
-
if (parsed.minLng > parsed.maxLng) {
|
|
395
|
-
actualMinLng = parsed.maxLng;
|
|
396
|
-
actualMaxLng = parsed.minLng;
|
|
397
|
-
corrections.push("minLng/maxLng were swapped");
|
|
398
|
-
}
|
|
399
|
-
if (parsed.minLat > parsed.maxLat) {
|
|
400
|
-
actualMinLat = parsed.maxLat;
|
|
401
|
-
actualMaxLat = parsed.minLat;
|
|
402
|
-
corrections.push("minLat/maxLat were swapped");
|
|
507
|
+
const response = {
|
|
508
|
+
results: result.rows,
|
|
509
|
+
count: result.rows?.length ?? 0,
|
|
510
|
+
};
|
|
511
|
+
if (corrections.length > 0) {
|
|
512
|
+
response["note"] = `Auto-corrected: ${corrections.join(", ")}`;
|
|
513
|
+
}
|
|
514
|
+
return response;
|
|
403
515
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
516
|
+
catch (error) {
|
|
517
|
+
if (error instanceof ZodError) {
|
|
518
|
+
return {
|
|
519
|
+
success: false,
|
|
520
|
+
error: error.issues.map((i) => i.message).join("; "),
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
success: false,
|
|
525
|
+
error: formatPostgresError(error, {
|
|
526
|
+
tool: "pg_bounding_box",
|
|
527
|
+
table: params?.["table"] ??
|
|
528
|
+
undefined,
|
|
529
|
+
}),
|
|
530
|
+
};
|
|
419
531
|
}
|
|
420
|
-
return response;
|
|
421
532
|
},
|
|
422
533
|
};
|
|
423
534
|
}
|
|
@@ -481,7 +592,15 @@ export function createSpatialIndexTool(adapter) {
|
|
|
481
592
|
}
|
|
482
593
|
// Always use IF NOT EXISTS to prevent unclear PostgreSQL errors
|
|
483
594
|
const sql = `CREATE INDEX IF NOT EXISTS ${indexName} ON ${qualifiedTable} USING GIST (${columnName})`;
|
|
484
|
-
|
|
595
|
+
try {
|
|
596
|
+
await adapter.executeQuery(sql);
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
throw parsePostgresError(error, {
|
|
600
|
+
tool: "pg_spatial_index",
|
|
601
|
+
table,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
485
604
|
return { success: true, index: indexNameRaw, table, column };
|
|
486
605
|
},
|
|
487
606
|
};
|