@neverinfamous/postgres-mcp 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +515 -0
- package/dist/__tests__/mocks/adapter.d.ts +80 -0
- package/dist/__tests__/mocks/adapter.d.ts.map +1 -0
- package/dist/__tests__/mocks/adapter.js +225 -0
- package/dist/__tests__/mocks/adapter.js.map +1 -0
- package/dist/__tests__/mocks/index.d.ts +11 -0
- package/dist/__tests__/mocks/index.d.ts.map +1 -0
- package/dist/__tests__/mocks/index.js +11 -0
- package/dist/__tests__/mocks/index.js.map +1 -0
- package/dist/__tests__/mocks/pool.d.ts +43 -0
- package/dist/__tests__/mocks/pool.d.ts.map +1 -0
- package/dist/__tests__/mocks/pool.js +71 -0
- package/dist/__tests__/mocks/pool.js.map +1 -0
- package/dist/adapters/DatabaseAdapter.d.ts +139 -0
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -0
- package/dist/adapters/DatabaseAdapter.js +250 -0
- package/dist/adapters/DatabaseAdapter.js.map +1 -0
- package/dist/adapters/postgresql/PostgresAdapter.d.ts +119 -0
- package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -0
- package/dist/adapters/postgresql/PostgresAdapter.js +902 -0
- package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -0
- package/dist/adapters/postgresql/index.d.ts +5 -0
- package/dist/adapters/postgresql/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/index.js +5 -0
- package/dist/adapters/postgresql/index.js.map +1 -0
- package/dist/adapters/postgresql/prompts/backup.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/backup.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/backup.js +132 -0
- package/dist/adapters/postgresql/prompts/backup.js.map +1 -0
- package/dist/adapters/postgresql/prompts/citext.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/citext.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/citext.js +227 -0
- package/dist/adapters/postgresql/prompts/citext.js.map +1 -0
- package/dist/adapters/postgresql/prompts/extensionSetup.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/extensionSetup.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/extensionSetup.js +282 -0
- package/dist/adapters/postgresql/prompts/extensionSetup.js.map +1 -0
- package/dist/adapters/postgresql/prompts/health.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/health.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/health.js +118 -0
- package/dist/adapters/postgresql/prompts/health.js.map +1 -0
- package/dist/adapters/postgresql/prompts/index.d.ts +13 -0
- package/dist/adapters/postgresql/prompts/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/index.js +308 -0
- package/dist/adapters/postgresql/prompts/index.js.map +1 -0
- package/dist/adapters/postgresql/prompts/indexTuning.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/indexTuning.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/indexTuning.js +130 -0
- package/dist/adapters/postgresql/prompts/indexTuning.js.map +1 -0
- package/dist/adapters/postgresql/prompts/kcache.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/kcache.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/kcache.js +227 -0
- package/dist/adapters/postgresql/prompts/kcache.js.map +1 -0
- package/dist/adapters/postgresql/prompts/ltree.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/ltree.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/ltree.js +286 -0
- package/dist/adapters/postgresql/prompts/ltree.js.map +1 -0
- package/dist/adapters/postgresql/prompts/partman.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/partman.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/partman.js +211 -0
- package/dist/adapters/postgresql/prompts/partman.js.map +1 -0
- package/dist/adapters/postgresql/prompts/pgcron.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/pgcron.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/pgcron.js +233 -0
- package/dist/adapters/postgresql/prompts/pgcron.js.map +1 -0
- package/dist/adapters/postgresql/prompts/pgcrypto.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/pgcrypto.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/pgcrypto.js +299 -0
- package/dist/adapters/postgresql/prompts/pgcrypto.js.map +1 -0
- package/dist/adapters/postgresql/prompts/pgvector.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/pgvector.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/pgvector.js +148 -0
- package/dist/adapters/postgresql/prompts/pgvector.js.map +1 -0
- package/dist/adapters/postgresql/prompts/postgis.d.ts +8 -0
- package/dist/adapters/postgresql/prompts/postgis.d.ts.map +1 -0
- package/dist/adapters/postgresql/prompts/postgis.js +200 -0
- package/dist/adapters/postgresql/prompts/postgis.js.map +1 -0
- package/dist/adapters/postgresql/resources/activity.d.ts +9 -0
- package/dist/adapters/postgresql/resources/activity.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/activity.js +118 -0
- package/dist/adapters/postgresql/resources/activity.js.map +1 -0
- package/dist/adapters/postgresql/resources/capabilities.d.ts +9 -0
- package/dist/adapters/postgresql/resources/capabilities.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/capabilities.js +182 -0
- package/dist/adapters/postgresql/resources/capabilities.js.map +1 -0
- package/dist/adapters/postgresql/resources/cron.d.ts +9 -0
- package/dist/adapters/postgresql/resources/cron.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/cron.js +156 -0
- package/dist/adapters/postgresql/resources/cron.js.map +1 -0
- package/dist/adapters/postgresql/resources/crypto.d.ts +9 -0
- package/dist/adapters/postgresql/resources/crypto.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/crypto.js +191 -0
- package/dist/adapters/postgresql/resources/crypto.js.map +1 -0
- package/dist/adapters/postgresql/resources/extensions.d.ts +9 -0
- package/dist/adapters/postgresql/resources/extensions.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/extensions.js +85 -0
- package/dist/adapters/postgresql/resources/extensions.js.map +1 -0
- package/dist/adapters/postgresql/resources/health.d.ts +9 -0
- package/dist/adapters/postgresql/resources/health.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/health.js +185 -0
- package/dist/adapters/postgresql/resources/health.js.map +1 -0
- package/dist/adapters/postgresql/resources/index.d.ts +40 -0
- package/dist/adapters/postgresql/resources/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/index.js +87 -0
- package/dist/adapters/postgresql/resources/index.js.map +1 -0
- package/dist/adapters/postgresql/resources/indexes.d.ts +9 -0
- package/dist/adapters/postgresql/resources/indexes.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/indexes.js +130 -0
- package/dist/adapters/postgresql/resources/indexes.js.map +1 -0
- package/dist/adapters/postgresql/resources/kcache.d.ts +9 -0
- package/dist/adapters/postgresql/resources/kcache.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/kcache.js +219 -0
- package/dist/adapters/postgresql/resources/kcache.js.map +1 -0
- package/dist/adapters/postgresql/resources/locks.d.ts +9 -0
- package/dist/adapters/postgresql/resources/locks.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/locks.js +89 -0
- package/dist/adapters/postgresql/resources/locks.js.map +1 -0
- package/dist/adapters/postgresql/resources/partman.d.ts +9 -0
- package/dist/adapters/postgresql/resources/partman.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/partman.js +149 -0
- package/dist/adapters/postgresql/resources/partman.js.map +1 -0
- package/dist/adapters/postgresql/resources/performance.d.ts +9 -0
- package/dist/adapters/postgresql/resources/performance.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/performance.js +170 -0
- package/dist/adapters/postgresql/resources/performance.js.map +1 -0
- package/dist/adapters/postgresql/resources/pool.d.ts +9 -0
- package/dist/adapters/postgresql/resources/pool.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/pool.js +93 -0
- package/dist/adapters/postgresql/resources/pool.js.map +1 -0
- package/dist/adapters/postgresql/resources/postgis.d.ts +9 -0
- package/dist/adapters/postgresql/resources/postgis.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/postgis.js +232 -0
- package/dist/adapters/postgresql/resources/postgis.js.map +1 -0
- package/dist/adapters/postgresql/resources/replication.d.ts +9 -0
- package/dist/adapters/postgresql/resources/replication.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/replication.js +126 -0
- package/dist/adapters/postgresql/resources/replication.js.map +1 -0
- package/dist/adapters/postgresql/resources/schema.d.ts +10 -0
- package/dist/adapters/postgresql/resources/schema.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/schema.js +80 -0
- package/dist/adapters/postgresql/resources/schema.js.map +1 -0
- package/dist/adapters/postgresql/resources/settings.d.ts +9 -0
- package/dist/adapters/postgresql/resources/settings.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/settings.js +184 -0
- package/dist/adapters/postgresql/resources/settings.js.map +1 -0
- package/dist/adapters/postgresql/resources/stats.d.ts +10 -0
- package/dist/adapters/postgresql/resources/stats.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/stats.js +124 -0
- package/dist/adapters/postgresql/resources/stats.js.map +1 -0
- package/dist/adapters/postgresql/resources/tables.d.ts +9 -0
- package/dist/adapters/postgresql/resources/tables.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/tables.js +20 -0
- package/dist/adapters/postgresql/resources/tables.js.map +1 -0
- package/dist/adapters/postgresql/resources/vacuum.d.ts +9 -0
- package/dist/adapters/postgresql/resources/vacuum.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/vacuum.js +122 -0
- package/dist/adapters/postgresql/resources/vacuum.js.map +1 -0
- package/dist/adapters/postgresql/resources/vector.d.ts +9 -0
- package/dist/adapters/postgresql/resources/vector.d.ts.map +1 -0
- package/dist/adapters/postgresql/resources/vector.js +185 -0
- package/dist/adapters/postgresql/resources/vector.js.map +1 -0
- package/dist/adapters/postgresql/schemas/admin.d.ts +74 -0
- package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/admin.js +180 -0
- package/dist/adapters/postgresql/schemas/admin.js.map +1 -0
- package/dist/adapters/postgresql/schemas/backup.d.ts +68 -0
- package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/backup.js +114 -0
- package/dist/adapters/postgresql/schemas/backup.js.map +1 -0
- package/dist/adapters/postgresql/schemas/core.d.ts +443 -0
- package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/core.js +628 -0
- package/dist/adapters/postgresql/schemas/core.js.map +1 -0
- package/dist/adapters/postgresql/schemas/cron.d.ts +131 -0
- package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/cron.js +218 -0
- package/dist/adapters/postgresql/schemas/cron.js.map +1 -0
- package/dist/adapters/postgresql/schemas/extensions.d.ts +403 -0
- package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/extensions.js +600 -0
- package/dist/adapters/postgresql/schemas/extensions.js.map +1 -0
- package/dist/adapters/postgresql/schemas/index.d.ts +21 -0
- package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/index.js +72 -0
- package/dist/adapters/postgresql/schemas/index.js.map +1 -0
- package/dist/adapters/postgresql/schemas/jsonb.d.ts +94 -0
- package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/jsonb.js +198 -0
- package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -0
- package/dist/adapters/postgresql/schemas/monitoring.d.ts +28 -0
- package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/monitoring.js +45 -0
- package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -0
- package/dist/adapters/postgresql/schemas/partitioning.d.ts +152 -0
- package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/partitioning.js +399 -0
- package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -0
- package/dist/adapters/postgresql/schemas/partman.d.ts +94 -0
- package/dist/adapters/postgresql/schemas/partman.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/partman.js +264 -0
- package/dist/adapters/postgresql/schemas/partman.js.map +1 -0
- package/dist/adapters/postgresql/schemas/performance.d.ts +52 -0
- package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/performance.js +57 -0
- package/dist/adapters/postgresql/schemas/performance.js.map +1 -0
- package/dist/adapters/postgresql/schemas/postgis.d.ts +693 -0
- package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/postgis.js +662 -0
- package/dist/adapters/postgresql/schemas/postgis.js.map +1 -0
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +171 -0
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/schema-mgmt.js +235 -0
- package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -0
- package/dist/adapters/postgresql/schemas/stats.d.ts +229 -0
- package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/stats.js +587 -0
- package/dist/adapters/postgresql/schemas/stats.js.map +1 -0
- package/dist/adapters/postgresql/schemas/text-search.d.ts +83 -0
- package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/text-search.js +132 -0
- package/dist/adapters/postgresql/schemas/text-search.js.map +1 -0
- package/dist/adapters/postgresql/schemas/vector.d.ts +143 -0
- package/dist/adapters/postgresql/schemas/vector.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/vector.js +123 -0
- package/dist/adapters/postgresql/schemas/vector.js.map +1 -0
- package/dist/adapters/postgresql/tools/admin.d.ts +13 -0
- package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/admin.js +417 -0
- package/dist/adapters/postgresql/tools/admin.js.map +1 -0
- package/dist/adapters/postgresql/tools/backup/dump.d.ts +12 -0
- package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/backup/dump.js +546 -0
- package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -0
- package/dist/adapters/postgresql/tools/backup/index.d.ts +16 -0
- package/dist/adapters/postgresql/tools/backup/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/backup/index.js +29 -0
- package/dist/adapters/postgresql/tools/backup/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/backup/planning.d.ts +22 -0
- package/dist/adapters/postgresql/tools/backup/planning.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/backup/planning.js +411 -0
- package/dist/adapters/postgresql/tools/backup/planning.js.map +1 -0
- package/dist/adapters/postgresql/tools/citext.d.ts +18 -0
- package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/citext.js +568 -0
- package/dist/adapters/postgresql/tools/citext.js.map +1 -0
- package/dist/adapters/postgresql/tools/codemode/index.d.ts +27 -0
- package/dist/adapters/postgresql/tools/codemode/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/codemode/index.js +171 -0
- package/dist/adapters/postgresql/tools/codemode/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/convenience.d.ts +192 -0
- package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/convenience.js +617 -0
- package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/health.d.ts +20 -0
- package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/health.js +360 -0
- package/dist/adapters/postgresql/tools/core/health.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/index.d.ts +15 -0
- package/dist/adapters/postgresql/tools/core/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/index.js +40 -0
- package/dist/adapters/postgresql/tools/core/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/indexes.d.ts +30 -0
- package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/indexes.js +232 -0
- package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/objects.d.ts +20 -0
- package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/objects.js +361 -0
- package/dist/adapters/postgresql/tools/core/objects.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/query.d.ts +16 -0
- package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/query.js +87 -0
- package/dist/adapters/postgresql/tools/core/query.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/schemas.d.ts +135 -0
- package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/schemas.js +221 -0
- package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/tables.d.ts +24 -0
- package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/tables.js +219 -0
- package/dist/adapters/postgresql/tools/core/tables.js.map +1 -0
- package/dist/adapters/postgresql/tools/cron.d.ts +16 -0
- package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/cron.js +440 -0
- package/dist/adapters/postgresql/tools/cron.js.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts +33 -0
- package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/advanced.js +681 -0
- package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/basic.d.ts +20 -0
- package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/basic.js +654 -0
- package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/index.d.ts +16 -0
- package/dist/adapters/postgresql/tools/jsonb/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/index.js +39 -0
- package/dist/adapters/postgresql/tools/jsonb/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/kcache.d.ts +20 -0
- package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/kcache.js +574 -0
- package/dist/adapters/postgresql/tools/kcache.js.map +1 -0
- package/dist/adapters/postgresql/tools/ltree.d.ts +8 -0
- package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/ltree.js +390 -0
- package/dist/adapters/postgresql/tools/ltree.js.map +1 -0
- package/dist/adapters/postgresql/tools/monitoring.d.ts +13 -0
- package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/monitoring.js +753 -0
- package/dist/adapters/postgresql/tools/monitoring.js.map +1 -0
- package/dist/adapters/postgresql/tools/partitioning.d.ts +13 -0
- package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/partitioning.js +500 -0
- package/dist/adapters/postgresql/tools/partitioning.js.map +1 -0
- package/dist/adapters/postgresql/tools/partman/index.d.ts +19 -0
- package/dist/adapters/postgresql/tools/partman/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/partman/index.js +33 -0
- package/dist/adapters/postgresql/tools/partman/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/partman/management.d.ts +28 -0
- package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/partman/management.js +563 -0
- package/dist/adapters/postgresql/tools/partman/management.js.map +1 -0
- package/dist/adapters/postgresql/tools/partman/operations.d.ts +28 -0
- package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/partman/operations.js +632 -0
- package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -0
- package/dist/adapters/postgresql/tools/performance/analysis.d.ts +9 -0
- package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/performance/analysis.js +383 -0
- package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -0
- package/dist/adapters/postgresql/tools/performance/explain.d.ts +13 -0
- package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/performance/explain.js +71 -0
- package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -0
- package/dist/adapters/postgresql/tools/performance/index.d.ts +13 -0
- package/dist/adapters/postgresql/tools/performance/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/performance/index.js +40 -0
- package/dist/adapters/postgresql/tools/performance/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/performance/monitoring.d.ts +9 -0
- package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/performance/monitoring.js +122 -0
- package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -0
- package/dist/adapters/postgresql/tools/performance/optimization.d.ts +9 -0
- package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/performance/optimization.js +315 -0
- package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -0
- package/dist/adapters/postgresql/tools/performance/stats.d.ts +14 -0
- package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/performance/stats.js +559 -0
- package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -0
- package/dist/adapters/postgresql/tools/pgcrypto.d.ts +8 -0
- package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/pgcrypto.js +239 -0
- package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/advanced.d.ts +21 -0
- package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/advanced.js +383 -0
- package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/basic.d.ts +16 -0
- package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/basic.js +479 -0
- package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/index.d.ts +17 -0
- package/dist/adapters/postgresql/tools/postgis/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/index.js +46 -0
- package/dist/adapters/postgresql/tools/postgis/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/standalone.d.ts +21 -0
- package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/postgis/standalone.js +150 -0
- package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -0
- package/dist/adapters/postgresql/tools/schema.d.ts +13 -0
- package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/schema.js +515 -0
- package/dist/adapters/postgresql/tools/schema.js.map +1 -0
- package/dist/adapters/postgresql/tools/stats/advanced.d.ts +24 -0
- package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/stats/advanced.js +876 -0
- package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -0
- package/dist/adapters/postgresql/tools/stats/basic.d.ts +24 -0
- package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/stats/basic.js +501 -0
- package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -0
- package/dist/adapters/postgresql/tools/stats/index.d.ts +17 -0
- package/dist/adapters/postgresql/tools/stats/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/stats/index.js +30 -0
- package/dist/adapters/postgresql/tools/stats/index.js.map +1 -0
- package/dist/adapters/postgresql/tools/text.d.ts +13 -0
- package/dist/adapters/postgresql/tools/text.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/text.js +708 -0
- package/dist/adapters/postgresql/tools/text.js.map +1 -0
- package/dist/adapters/postgresql/tools/transactions.d.ts +13 -0
- package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/transactions.js +201 -0
- package/dist/adapters/postgresql/tools/transactions.js.map +1 -0
- package/dist/adapters/postgresql/tools/vector/advanced.d.ts +12 -0
- package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/vector/advanced.js +902 -0
- package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -0
- package/dist/adapters/postgresql/tools/vector/basic.d.ts +25 -0
- package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/vector/basic.js +1000 -0
- package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -0
- package/dist/adapters/postgresql/tools/vector/index.d.ts +13 -0
- package/dist/adapters/postgresql/tools/vector/index.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/vector/index.js +33 -0
- package/dist/adapters/postgresql/tools/vector/index.js.map +1 -0
- package/dist/auth/AuthorizationServerDiscovery.d.ts +44 -0
- package/dist/auth/AuthorizationServerDiscovery.d.ts.map +1 -0
- package/dist/auth/AuthorizationServerDiscovery.js +117 -0
- package/dist/auth/AuthorizationServerDiscovery.js.map +1 -0
- package/dist/auth/OAuthResourceServer.d.ts +42 -0
- package/dist/auth/OAuthResourceServer.d.ts.map +1 -0
- package/dist/auth/OAuthResourceServer.js +80 -0
- package/dist/auth/OAuthResourceServer.js.map +1 -0
- package/dist/auth/TokenValidator.d.ts +36 -0
- package/dist/auth/TokenValidator.d.ts.map +1 -0
- package/dist/auth/TokenValidator.js +139 -0
- package/dist/auth/TokenValidator.js.map +1 -0
- package/dist/auth/errors.d.ts +63 -0
- package/dist/auth/errors.d.ts.map +1 -0
- package/dist/auth/errors.js +102 -0
- package/dist/auth/errors.js.map +1 -0
- package/dist/auth/index.d.ts +15 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +16 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware.d.ts +61 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +156 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/scopes.d.ts +65 -0
- package/dist/auth/scopes.d.ts.map +1 -0
- package/dist/auth/scopes.js +189 -0
- package/dist/auth/scopes.js.map +1 -0
- package/dist/auth/types.d.ts +208 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +8 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/cli/args.d.ts +34 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +308 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +341 -0
- package/dist/cli.js.map +1 -0
- package/dist/codemode/api.d.ts +62 -0
- package/dist/codemode/api.d.ts.map +1 -0
- package/dist/codemode/api.js +1505 -0
- package/dist/codemode/api.js.map +1 -0
- package/dist/codemode/index.d.ts +13 -0
- package/dist/codemode/index.d.ts.map +1 -0
- package/dist/codemode/index.js +17 -0
- package/dist/codemode/index.js.map +1 -0
- package/dist/codemode/sandbox-factory.d.ts +72 -0
- package/dist/codemode/sandbox-factory.d.ts.map +1 -0
- package/dist/codemode/sandbox-factory.js +88 -0
- package/dist/codemode/sandbox-factory.js.map +1 -0
- package/dist/codemode/sandbox.d.ts +96 -0
- package/dist/codemode/sandbox.d.ts.map +1 -0
- package/dist/codemode/sandbox.js +345 -0
- package/dist/codemode/sandbox.js.map +1 -0
- package/dist/codemode/security.d.ts +44 -0
- package/dist/codemode/security.d.ts.map +1 -0
- package/dist/codemode/security.js +149 -0
- package/dist/codemode/security.js.map +1 -0
- package/dist/codemode/types.d.ts +137 -0
- package/dist/codemode/types.d.ts.map +1 -0
- package/dist/codemode/types.js +46 -0
- package/dist/codemode/types.js.map +1 -0
- package/dist/codemode/worker-sandbox.d.ts +82 -0
- package/dist/codemode/worker-sandbox.d.ts.map +1 -0
- package/dist/codemode/worker-sandbox.js +244 -0
- package/dist/codemode/worker-sandbox.js.map +1 -0
- package/dist/codemode/worker-script.d.ts +8 -0
- package/dist/codemode/worker-script.d.ts.map +1 -0
- package/dist/codemode/worker-script.js +113 -0
- package/dist/codemode/worker-script.js.map +1 -0
- package/dist/constants/ServerInstructions.d.ts +13 -0
- package/dist/constants/ServerInstructions.d.ts.map +1 -0
- package/dist/constants/ServerInstructions.js +405 -0
- package/dist/constants/ServerInstructions.js.map +1 -0
- package/dist/filtering/ToolConstants.d.ts +43 -0
- package/dist/filtering/ToolConstants.d.ts.map +1 -0
- package/dist/filtering/ToolConstants.js +352 -0
- package/dist/filtering/ToolConstants.js.map +1 -0
- package/dist/filtering/ToolFilter.d.ts +90 -0
- package/dist/filtering/ToolFilter.d.ts.map +1 -0
- package/dist/filtering/ToolFilter.js +315 -0
- package/dist/filtering/ToolFilter.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/pool/ConnectionPool.d.ts +70 -0
- package/dist/pool/ConnectionPool.d.ts.map +1 -0
- package/dist/pool/ConnectionPool.js +254 -0
- package/dist/pool/ConnectionPool.js.map +1 -0
- package/dist/server/McpServer.d.ts +50 -0
- package/dist/server/McpServer.d.ts.map +1 -0
- package/dist/server/McpServer.js +108 -0
- package/dist/server/McpServer.js.map +1 -0
- package/dist/transports/http.d.ts +126 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +303 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/index.d.ts +8 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +7 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/types/adapters.d.ts +136 -0
- package/dist/types/adapters.d.ts.map +1 -0
- package/dist/types/adapters.js +7 -0
- package/dist/types/adapters.js.map +1 -0
- package/dist/types/database.d.ts +204 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +7 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/errors.d.ts +62 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +91 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/filtering.d.ts +39 -0
- package/dist/types/filtering.d.ts.map +1 -0
- package/dist/types/filtering.js +7 -0
- package/dist/types/filtering.js.map +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/mcp.d.ts +31 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +7 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/types/oauth.d.ts +65 -0
- package/dist/types/oauth.d.ts.map +1 -0
- package/dist/types/oauth.js +7 -0
- package/dist/types/oauth.js.map +1 -0
- package/dist/types/schema.d.ts +110 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +7 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/utils/annotations.d.ts +42 -0
- package/dist/utils/annotations.d.ts.map +1 -0
- package/dist/utils/annotations.js +75 -0
- package/dist/utils/annotations.js.map +1 -0
- package/dist/utils/icons.d.ts +25 -0
- package/dist/utils/icons.d.ts.map +1 -0
- package/dist/utils/icons.js +212 -0
- package/dist/utils/icons.js.map +1 -0
- package/dist/utils/identifiers.d.ts +111 -0
- package/dist/utils/identifiers.d.ts.map +1 -0
- package/dist/utils/identifiers.js +270 -0
- package/dist/utils/identifiers.js.map +1 -0
- package/dist/utils/logger.d.ts +141 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +304 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/promptGenerator.d.ts +20 -0
- package/dist/utils/promptGenerator.d.ts.map +1 -0
- package/dist/utils/promptGenerator.js +81 -0
- package/dist/utils/promptGenerator.js.map +1 -0
- package/dist/utils/resourceAnnotations.d.ts +36 -0
- package/dist/utils/resourceAnnotations.d.ts.map +1 -0
- package/dist/utils/resourceAnnotations.js +57 -0
- package/dist/utils/resourceAnnotations.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL JSONB Tools - Advanced Operations
|
|
3
|
+
*
|
|
4
|
+
* Advanced JSONB operations including path validation, merge, normalize, diff, index suggestions, security scanning, and statistics.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { readOnly } from "../../../../utils/annotations.js";
|
|
8
|
+
import { getToolIcons } from "../../../../utils/icons.js";
|
|
9
|
+
import { sanitizeIdentifier, sanitizeTableName, } from "../../../../utils/identifiers.js";
|
|
10
|
+
/**
|
|
11
|
+
* Convert value to a valid JSON string for PostgreSQL's ::jsonb cast
|
|
12
|
+
* Always uses JSON.stringify to ensure proper encoding
|
|
13
|
+
*/
|
|
14
|
+
function toJsonString(value) {
|
|
15
|
+
return JSON.stringify(value);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validate JSON path expression
|
|
19
|
+
*/
|
|
20
|
+
export function createJsonbValidatePathTool(adapter) {
|
|
21
|
+
return {
|
|
22
|
+
name: "pg_jsonb_validate_path",
|
|
23
|
+
description: "Validate a JSONPath expression and test it against sample data. Supports vars for parameterized paths.",
|
|
24
|
+
group: "jsonb",
|
|
25
|
+
inputSchema: z.object({
|
|
26
|
+
path: z.string().describe("JSONPath expression to validate"),
|
|
27
|
+
testValue: z
|
|
28
|
+
.unknown()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Optional JSONB value to test against"),
|
|
31
|
+
vars: z
|
|
32
|
+
.record(z.string(), z.unknown())
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Variables for parameterized paths (e.g., {x: 5})"),
|
|
35
|
+
}),
|
|
36
|
+
annotations: readOnly("JSONB Validate Path"),
|
|
37
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Validate Path")),
|
|
38
|
+
handler: async (params, _context) => {
|
|
39
|
+
const parsed = params;
|
|
40
|
+
try {
|
|
41
|
+
if (parsed.testValue !== undefined) {
|
|
42
|
+
const varsJson = parsed.vars ? JSON.stringify(parsed.vars) : "{}";
|
|
43
|
+
const sql = `SELECT jsonb_path_query($1::jsonb, $2::jsonpath, $3::jsonb) as result`;
|
|
44
|
+
const result = await adapter.executeQuery(sql, [
|
|
45
|
+
toJsonString(parsed.testValue),
|
|
46
|
+
parsed.path,
|
|
47
|
+
varsJson,
|
|
48
|
+
]);
|
|
49
|
+
return {
|
|
50
|
+
valid: true,
|
|
51
|
+
path: parsed.path,
|
|
52
|
+
results: result.rows?.map((r) => r["result"]),
|
|
53
|
+
varsUsed: parsed.vars !== undefined,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const sql = `SELECT $1::jsonpath as path`;
|
|
58
|
+
await adapter.executeQuery(sql, [parsed.path]);
|
|
59
|
+
return { valid: true, path: parsed.path };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
valid: false,
|
|
65
|
+
path: parsed.path,
|
|
66
|
+
error: error instanceof Error ? error.message : "Invalid path",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Recursively deep merge two objects
|
|
74
|
+
* @param mergeArrays - If true, concatenate arrays instead of replacing
|
|
75
|
+
*/
|
|
76
|
+
function deepMergeObjects(base, overlay, mergeArrays = false) {
|
|
77
|
+
// If both are arrays and mergeArrays is true, concatenate them
|
|
78
|
+
if (Array.isArray(base) && Array.isArray(overlay) && mergeArrays) {
|
|
79
|
+
return [...base, ...overlay];
|
|
80
|
+
}
|
|
81
|
+
// If either is not an object, overlay wins
|
|
82
|
+
if (typeof base !== "object" || base === null || Array.isArray(base)) {
|
|
83
|
+
return overlay;
|
|
84
|
+
}
|
|
85
|
+
if (typeof overlay !== "object" ||
|
|
86
|
+
overlay === null ||
|
|
87
|
+
Array.isArray(overlay)) {
|
|
88
|
+
return overlay;
|
|
89
|
+
}
|
|
90
|
+
const result = {
|
|
91
|
+
...base,
|
|
92
|
+
};
|
|
93
|
+
const overlayObj = overlay;
|
|
94
|
+
for (const key of Object.keys(overlayObj)) {
|
|
95
|
+
const baseVal = result[key];
|
|
96
|
+
const overlayVal = overlayObj[key];
|
|
97
|
+
// Recursively merge if both are objects (pass mergeArrays through)
|
|
98
|
+
if (typeof baseVal === "object" &&
|
|
99
|
+
baseVal !== null &&
|
|
100
|
+
typeof overlayVal === "object" &&
|
|
101
|
+
overlayVal !== null) {
|
|
102
|
+
result[key] = deepMergeObjects(baseVal, overlayVal, mergeArrays);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
result[key] = overlayVal;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
// Schema for pg_jsonb_merge - direct schema for MCP visibility
|
|
111
|
+
const JsonbMergeSchema = z.object({
|
|
112
|
+
base: z.unknown().describe("Base JSONB document (required)"),
|
|
113
|
+
overlay: z.unknown().describe("JSONB to merge on top (required)"),
|
|
114
|
+
deep: z
|
|
115
|
+
.boolean()
|
|
116
|
+
.optional()
|
|
117
|
+
.describe("Deep merge nested objects (default: true)"),
|
|
118
|
+
mergeArrays: z
|
|
119
|
+
.boolean()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("Concatenate arrays instead of replacing (default: false)"),
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Preprocess merge params to parse JSON strings and validate objects
|
|
125
|
+
*/
|
|
126
|
+
function parseMergeParams(params) {
|
|
127
|
+
const parsed = JsonbMergeSchema.parse(params);
|
|
128
|
+
// Parse JSON strings if needed
|
|
129
|
+
let base = parsed.base;
|
|
130
|
+
let overlay = parsed.overlay;
|
|
131
|
+
if (typeof base === "string") {
|
|
132
|
+
try {
|
|
133
|
+
base = JSON.parse(base);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
/* keep as string */
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (typeof overlay === "string") {
|
|
140
|
+
try {
|
|
141
|
+
overlay = JSON.parse(overlay);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
/* keep as string */
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (base === undefined) {
|
|
148
|
+
throw new Error("pg_jsonb_merge requires base document");
|
|
149
|
+
}
|
|
150
|
+
if (overlay === undefined) {
|
|
151
|
+
throw new Error("pg_jsonb_merge requires overlay document");
|
|
152
|
+
}
|
|
153
|
+
// Validate base and overlay are objects (not primitives or arrays)
|
|
154
|
+
if (typeof base !== "object" || base === null || Array.isArray(base)) {
|
|
155
|
+
throw new Error("pg_jsonb_merge base must be an object. For arrays or primitives, use pg_jsonb_set.");
|
|
156
|
+
}
|
|
157
|
+
if (typeof overlay !== "object" ||
|
|
158
|
+
overlay === null ||
|
|
159
|
+
Array.isArray(overlay)) {
|
|
160
|
+
throw new Error("pg_jsonb_merge overlay must be an object. For arrays or primitives, use pg_jsonb_set.");
|
|
161
|
+
}
|
|
162
|
+
return { base, overlay, deep: parsed.deep, mergeArrays: parsed.mergeArrays };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Deep merge two JSONB documents
|
|
166
|
+
*/
|
|
167
|
+
export function createJsonbMergeTool(adapter) {
|
|
168
|
+
return {
|
|
169
|
+
name: "pg_jsonb_merge",
|
|
170
|
+
description: "Merge two JSONB objects. deep=true (default) recursively merges. mergeArrays=true concatenates arrays.",
|
|
171
|
+
group: "jsonb",
|
|
172
|
+
inputSchema: JsonbMergeSchema,
|
|
173
|
+
annotations: readOnly("JSONB Merge"),
|
|
174
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Merge")),
|
|
175
|
+
handler: async (params, _context) => {
|
|
176
|
+
const parsed = parseMergeParams(params);
|
|
177
|
+
const useDeep = parsed.deep !== false;
|
|
178
|
+
const useMergeArrays = parsed.mergeArrays === true;
|
|
179
|
+
if (useDeep) {
|
|
180
|
+
// Perform recursive deep merge in TypeScript
|
|
181
|
+
const merged = deepMergeObjects(parsed.base, parsed.overlay, useMergeArrays);
|
|
182
|
+
// Return the merged result directly (no PostgreSQL round-trip needed)
|
|
183
|
+
return { merged, deep: true, mergeArrays: useMergeArrays };
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
// Shallow merge using PostgreSQL || operator
|
|
187
|
+
const sql = `SELECT $1::jsonb || $2::jsonb as result`;
|
|
188
|
+
const result = await adapter.executeQuery(sql, [
|
|
189
|
+
toJsonString(parsed.base),
|
|
190
|
+
toJsonString(parsed.overlay),
|
|
191
|
+
]);
|
|
192
|
+
return { merged: result.rows?.[0]?.["result"], deep: false };
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Normalize JSONB to relational form (key-value pairs)
|
|
199
|
+
*/
|
|
200
|
+
export function createJsonbNormalizeTool(adapter) {
|
|
201
|
+
return {
|
|
202
|
+
name: "pg_jsonb_normalize",
|
|
203
|
+
description: 'Normalize JSONB to key-value pairs. Use idColumn to specify row identifier (default: "id" if exists, else ctid).',
|
|
204
|
+
group: "jsonb",
|
|
205
|
+
inputSchema: z.object({
|
|
206
|
+
table: z.string().describe("Table name"),
|
|
207
|
+
column: z.string().describe("JSONB column"),
|
|
208
|
+
mode: z
|
|
209
|
+
.enum(["keys", "array", "pairs", "flatten"])
|
|
210
|
+
.optional()
|
|
211
|
+
.describe("keys: text values (all converted to string). pairs: JSONB types preserved. array: for arrays. flatten: recursive."),
|
|
212
|
+
where: z.string().optional(),
|
|
213
|
+
idColumn: z
|
|
214
|
+
.string()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe('Column to use for row identification (e.g., "id"). If omitted, defaults to "id" if it exists, else uses ctid.'),
|
|
217
|
+
}),
|
|
218
|
+
annotations: readOnly("JSONB Normalize"),
|
|
219
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Normalize")),
|
|
220
|
+
handler: async (params, _context) => {
|
|
221
|
+
const parsed = params;
|
|
222
|
+
const whereClause = parsed.where ? ` WHERE ${parsed.where}` : "";
|
|
223
|
+
const mode = parsed.mode ?? "keys";
|
|
224
|
+
// Validate mode parameter
|
|
225
|
+
const validModes = ["keys", "array", "pairs", "flatten"];
|
|
226
|
+
if (!validModes.includes(mode)) {
|
|
227
|
+
throw new Error(`pg_jsonb_normalize: Invalid mode '${mode}'. Valid modes: ${validModes.join(", ")}`);
|
|
228
|
+
}
|
|
229
|
+
const tableName = sanitizeTableName(parsed.table);
|
|
230
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
231
|
+
// Determine row identifier column
|
|
232
|
+
let rowIdExpr;
|
|
233
|
+
let rowIdAlias = "source_id";
|
|
234
|
+
if (parsed.idColumn) {
|
|
235
|
+
// User specified - use it directly
|
|
236
|
+
rowIdExpr = sanitizeIdentifier(parsed.idColumn);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// Try to detect 'id' column, fall back to ctid
|
|
240
|
+
try {
|
|
241
|
+
const checkSql = `SELECT column_name FROM information_schema.columns WHERE table_name = $1 AND column_name = 'id' LIMIT 1`;
|
|
242
|
+
const checkResult = await adapter.executeQuery(checkSql, [
|
|
243
|
+
parsed.table,
|
|
244
|
+
]);
|
|
245
|
+
if (checkResult.rows && checkResult.rows.length > 0) {
|
|
246
|
+
rowIdExpr = '"id"';
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
rowIdExpr = "ctid::text";
|
|
250
|
+
rowIdAlias = "source_ctid";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
rowIdExpr = "ctid::text";
|
|
255
|
+
rowIdAlias = "source_ctid";
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
let sql;
|
|
259
|
+
if (mode === "array") {
|
|
260
|
+
sql = `SELECT ${rowIdExpr} as ${rowIdAlias}, jsonb_array_elements(${columnName}) as element FROM ${tableName}${whereClause}`;
|
|
261
|
+
}
|
|
262
|
+
else if (mode === "flatten") {
|
|
263
|
+
// Recursive CTE to flatten nested objects to dot-notation keys
|
|
264
|
+
sql = `
|
|
265
|
+
WITH RECURSIVE
|
|
266
|
+
source_rows AS (
|
|
267
|
+
SELECT ${rowIdExpr} as ${rowIdAlias}, ${columnName} as doc
|
|
268
|
+
FROM ${tableName}${whereClause}
|
|
269
|
+
),
|
|
270
|
+
flattened AS (
|
|
271
|
+
-- Base case: start with top-level keys
|
|
272
|
+
SELECT
|
|
273
|
+
sr.${rowIdAlias},
|
|
274
|
+
kv.key as path,
|
|
275
|
+
kv.value,
|
|
276
|
+
jsonb_typeof(kv.value) as value_type
|
|
277
|
+
FROM source_rows sr, jsonb_each(sr.doc) kv
|
|
278
|
+
|
|
279
|
+
UNION ALL
|
|
280
|
+
|
|
281
|
+
-- Recursive case: expand nested objects
|
|
282
|
+
SELECT
|
|
283
|
+
f.${rowIdAlias},
|
|
284
|
+
f.path || '.' || kv.key,
|
|
285
|
+
kv.value,
|
|
286
|
+
jsonb_typeof(kv.value)
|
|
287
|
+
FROM flattened f, jsonb_each(f.value) kv
|
|
288
|
+
WHERE jsonb_typeof(f.value) = 'object'
|
|
289
|
+
)
|
|
290
|
+
SELECT ${rowIdAlias}, path as key, value, value_type FROM flattened
|
|
291
|
+
WHERE value_type != 'object' OR value = '{}'::jsonb
|
|
292
|
+
ORDER BY ${rowIdAlias}, path
|
|
293
|
+
`;
|
|
294
|
+
}
|
|
295
|
+
else if (mode === "pairs") {
|
|
296
|
+
sql = `SELECT ${rowIdExpr} as ${rowIdAlias}, key, value FROM ${tableName}, jsonb_each(${columnName}) ${whereClause}`;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
// Default 'keys' mode
|
|
300
|
+
sql = `SELECT ${rowIdExpr} as ${rowIdAlias}, key, value FROM ${tableName}, jsonb_each_text(${columnName}) ${whereClause}`;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const result = await adapter.executeQuery(sql);
|
|
304
|
+
// Check for empty flatten results on array columns
|
|
305
|
+
if (mode === "flatten" && (result.rows?.length ?? 0) === 0) {
|
|
306
|
+
// Verify if this is because column contains arrays
|
|
307
|
+
const typeCheckSql = `SELECT jsonb_typeof(${columnName}) as type FROM ${tableName}${whereClause} LIMIT 1`;
|
|
308
|
+
const typeResult = await adapter.executeQuery(typeCheckSql);
|
|
309
|
+
if (typeResult.rows?.[0]?.["type"] === "array") {
|
|
310
|
+
throw new Error(`pg_jsonb_normalize flatten mode requires object columns. Column appears to contain arrays - use 'array' mode instead.`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return { rows: result.rows, count: result.rows?.length ?? 0, mode };
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
// Improve error for array columns with object-only modes
|
|
317
|
+
if (error instanceof Error &&
|
|
318
|
+
error.message.includes("cannot call jsonb_each")) {
|
|
319
|
+
throw new Error(`pg_jsonb_normalize '${mode}' mode requires object columns. For array columns, use mode: 'array'.`);
|
|
320
|
+
}
|
|
321
|
+
// Improve error for object columns with array mode
|
|
322
|
+
if (error instanceof Error &&
|
|
323
|
+
error.message.includes("cannot extract elements from an object")) {
|
|
324
|
+
throw new Error(`pg_jsonb_normalize 'array' mode requires array columns. For object columns, use mode: 'keys' or 'pairs'.`);
|
|
325
|
+
}
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Diff two JSONB documents
|
|
333
|
+
* Note: Uses jsonb_each() which requires object inputs, not arrays or primitives
|
|
334
|
+
*/
|
|
335
|
+
// Schema for pg_jsonb_diff - requires objects (not arrays or primitives)
|
|
336
|
+
const JsonbDiffSchema = z.object({
|
|
337
|
+
doc1: z
|
|
338
|
+
.record(z.string(), z.unknown())
|
|
339
|
+
.describe("First JSONB object to compare"),
|
|
340
|
+
doc2: z
|
|
341
|
+
.record(z.string(), z.unknown())
|
|
342
|
+
.describe("Second JSONB object to compare"),
|
|
343
|
+
});
|
|
344
|
+
export function createJsonbDiffTool(adapter) {
|
|
345
|
+
return {
|
|
346
|
+
name: "pg_jsonb_diff",
|
|
347
|
+
description: "Compare two JSONB objects. Returns top-level key differences only (shallow comparison, not recursive).",
|
|
348
|
+
group: "jsonb",
|
|
349
|
+
inputSchema: JsonbDiffSchema,
|
|
350
|
+
annotations: readOnly("JSONB Diff"),
|
|
351
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Diff")),
|
|
352
|
+
handler: async (params, _context) => {
|
|
353
|
+
let parsed;
|
|
354
|
+
try {
|
|
355
|
+
parsed = JsonbDiffSchema.parse(params);
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
// Provide friendly error for array/non-object inputs
|
|
359
|
+
throw new Error("pg_jsonb_diff requires two JSONB objects. Arrays and primitive values are not supported. Use {} format for both doc1 and doc2.");
|
|
360
|
+
}
|
|
361
|
+
const sql = `
|
|
362
|
+
WITH
|
|
363
|
+
j1 AS (SELECT key, value FROM jsonb_each($1::jsonb)),
|
|
364
|
+
j2 AS (SELECT key, value FROM jsonb_each($2::jsonb))
|
|
365
|
+
SELECT
|
|
366
|
+
COALESCE(j1.key, j2.key) as key,
|
|
367
|
+
j1.value as value1,
|
|
368
|
+
j2.value as value2,
|
|
369
|
+
CASE
|
|
370
|
+
WHEN j1.key IS NULL THEN 'added'
|
|
371
|
+
WHEN j2.key IS NULL THEN 'removed'
|
|
372
|
+
WHEN j1.value = j2.value THEN 'unchanged'
|
|
373
|
+
ELSE 'modified'
|
|
374
|
+
END as status
|
|
375
|
+
FROM j1 FULL OUTER JOIN j2 ON j1.key = j2.key
|
|
376
|
+
WHERE j1.value IS DISTINCT FROM j2.value
|
|
377
|
+
`;
|
|
378
|
+
const result = await adapter.executeQuery(sql, [
|
|
379
|
+
toJsonString(parsed.doc1),
|
|
380
|
+
toJsonString(parsed.doc2),
|
|
381
|
+
]);
|
|
382
|
+
return {
|
|
383
|
+
differences: result.rows,
|
|
384
|
+
hasDifferences: (result.rows?.length ?? 0) > 0,
|
|
385
|
+
comparison: "shallow",
|
|
386
|
+
hint: "Compares top-level keys only. Nested object changes show as modified.",
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Suggest JSONB indexes based on query patterns
|
|
393
|
+
*/
|
|
394
|
+
export function createJsonbIndexSuggestTool(adapter) {
|
|
395
|
+
return {
|
|
396
|
+
name: "pg_jsonb_index_suggest",
|
|
397
|
+
description: "Analyze JSONB column and suggest indexes. Only works on object-type JSONB (not arrays).",
|
|
398
|
+
group: "jsonb",
|
|
399
|
+
inputSchema: z.object({
|
|
400
|
+
table: z.string().describe("Table name"),
|
|
401
|
+
column: z.string().describe("JSONB column"),
|
|
402
|
+
sampleSize: z.number().optional().describe("Sample rows to analyze"),
|
|
403
|
+
where: z.string().optional().describe("WHERE clause to filter rows"),
|
|
404
|
+
}),
|
|
405
|
+
annotations: readOnly("JSONB Index Suggest"),
|
|
406
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Index Suggest")),
|
|
407
|
+
handler: async (params, _context) => {
|
|
408
|
+
const parsed = params;
|
|
409
|
+
const sample = parsed.sampleSize ?? 1000;
|
|
410
|
+
const whereClause = parsed.where ? ` WHERE ${parsed.where}` : "";
|
|
411
|
+
const tableName = sanitizeTableName(parsed.table);
|
|
412
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
413
|
+
const keySql = `
|
|
414
|
+
SELECT key, COUNT(*) as frequency,
|
|
415
|
+
jsonb_typeof(value) as value_type
|
|
416
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t,
|
|
417
|
+
jsonb_each(${columnName})
|
|
418
|
+
GROUP BY key, jsonb_typeof(value)
|
|
419
|
+
ORDER BY frequency DESC
|
|
420
|
+
LIMIT 20
|
|
421
|
+
`;
|
|
422
|
+
let keyResult;
|
|
423
|
+
try {
|
|
424
|
+
keyResult = await adapter.executeQuery(keySql);
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
if (error instanceof Error &&
|
|
428
|
+
(error.message.includes("function jsonb_each") ||
|
|
429
|
+
error.message.includes("cannot call jsonb_each"))) {
|
|
430
|
+
throw new Error(`pg_jsonb_index_suggest requires JSONB objects (not arrays). Column '${parsed.column}' may not be JSONB type or contains arrays.`);
|
|
431
|
+
}
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
const indexSql = `
|
|
435
|
+
SELECT indexname, indexdef
|
|
436
|
+
FROM pg_indexes
|
|
437
|
+
WHERE tablename = $1
|
|
438
|
+
AND indexdef LIKE '%' || $2 || '%'
|
|
439
|
+
`;
|
|
440
|
+
const indexResult = await adapter.executeQuery(indexSql, [
|
|
441
|
+
parsed.table,
|
|
442
|
+
parsed.column,
|
|
443
|
+
]);
|
|
444
|
+
const recommendations = [];
|
|
445
|
+
// Cast frequency to number (PostgreSQL returns bigint as string)
|
|
446
|
+
const keys = (keyResult.rows ?? []).map((row) => ({
|
|
447
|
+
key: row["key"],
|
|
448
|
+
frequency: Number(row["frequency"]),
|
|
449
|
+
value_type: row["value_type"],
|
|
450
|
+
}));
|
|
451
|
+
// Only recommend GIN index if there's data to analyze
|
|
452
|
+
if ((indexResult.rows?.length ?? 0) === 0 && keys.length > 0) {
|
|
453
|
+
recommendations.push(`CREATE INDEX ON ${tableName} USING GIN (${columnName})`);
|
|
454
|
+
}
|
|
455
|
+
for (const keyInfo of keys.slice(0, 5)) {
|
|
456
|
+
if (keyInfo.frequency > sample * 0.5) {
|
|
457
|
+
recommendations.push(`CREATE INDEX ON ${tableName} ((${columnName} ->> '${keyInfo.key.replace(/'/g, "''")}'))`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Build response with helpful hints
|
|
461
|
+
const response = {
|
|
462
|
+
keyDistribution: keys,
|
|
463
|
+
existingIndexes: indexResult.rows,
|
|
464
|
+
recommendations,
|
|
465
|
+
};
|
|
466
|
+
// Add explanation when no recommendations
|
|
467
|
+
if (recommendations.length === 0) {
|
|
468
|
+
if ((indexResult.rows?.length ?? 0) > 0) {
|
|
469
|
+
response.hint =
|
|
470
|
+
"No new recommendations - existing indexes already cover this column";
|
|
471
|
+
}
|
|
472
|
+
else if (keys.length === 0) {
|
|
473
|
+
response.hint =
|
|
474
|
+
"No recommendations - table is empty or column has no keys to analyze";
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
response.hint =
|
|
478
|
+
"No recommendations - no keys appeared in >50% of sampled rows";
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return response;
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Scan JSONB for security issues
|
|
487
|
+
*/
|
|
488
|
+
export function createJsonbSecurityScanTool(adapter) {
|
|
489
|
+
return {
|
|
490
|
+
name: "pg_jsonb_security_scan",
|
|
491
|
+
description: "Scan JSONB for security issues. Only works on object-type JSONB (not arrays). Use larger sampleSize for thorough scans.",
|
|
492
|
+
group: "jsonb",
|
|
493
|
+
inputSchema: z.object({
|
|
494
|
+
table: z.string().describe("Table name"),
|
|
495
|
+
column: z.string().describe("JSONB column"),
|
|
496
|
+
sampleSize: z.number().optional().describe("Sample rows to scan"),
|
|
497
|
+
where: z.string().optional().describe("WHERE clause to filter rows"),
|
|
498
|
+
}),
|
|
499
|
+
annotations: readOnly("JSONB Security Scan"),
|
|
500
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Security Scan")),
|
|
501
|
+
handler: async (params, _context) => {
|
|
502
|
+
const parsed = params;
|
|
503
|
+
const sample = parsed.sampleSize ?? 100;
|
|
504
|
+
const whereClause = parsed.where ? ` WHERE ${parsed.where}` : "";
|
|
505
|
+
const issues = [];
|
|
506
|
+
const tableName = sanitizeTableName(parsed.table);
|
|
507
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
508
|
+
// Count actual rows scanned (may be less than sample if table is small)
|
|
509
|
+
const countSql = `SELECT COUNT(*) as count FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t`;
|
|
510
|
+
const countResult = await adapter.executeQuery(countSql);
|
|
511
|
+
const actualRowsScanned = Number(countResult.rows?.[0]?.["count"] ?? 0);
|
|
512
|
+
const sensitiveKeysSql = `
|
|
513
|
+
SELECT key, COUNT(*) as count
|
|
514
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t,
|
|
515
|
+
jsonb_each_text(${columnName})
|
|
516
|
+
WHERE lower(key) IN ('password', 'secret', 'token', 'api_key', 'apikey',
|
|
517
|
+
'auth', 'credential', 'ssn', 'credit_card', 'cvv')
|
|
518
|
+
GROUP BY key
|
|
519
|
+
`;
|
|
520
|
+
let sensitiveResult;
|
|
521
|
+
try {
|
|
522
|
+
sensitiveResult = await adapter.executeQuery(sensitiveKeysSql);
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
if (error instanceof Error &&
|
|
526
|
+
(error.message.includes("function jsonb_each") ||
|
|
527
|
+
error.message.includes("cannot call jsonb_each"))) {
|
|
528
|
+
throw new Error(`pg_jsonb_security_scan requires JSONB objects (not arrays). Column '${parsed.column}' may not be JSONB type or contains arrays.`);
|
|
529
|
+
}
|
|
530
|
+
throw error;
|
|
531
|
+
}
|
|
532
|
+
for (const row of (sensitiveResult.rows ?? [])) {
|
|
533
|
+
issues.push({
|
|
534
|
+
type: "sensitive_key",
|
|
535
|
+
key: row.key,
|
|
536
|
+
count: Number(row.count),
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
const injectionSql = `
|
|
540
|
+
SELECT key, COUNT(*) as count
|
|
541
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t,
|
|
542
|
+
jsonb_each_text(${columnName})
|
|
543
|
+
WHERE value ~* '(\\bSELECT\\s+.+\\bFROM\\b|\\bINSERT\\s+INTO\\b|\\bUPDATE\\s+.+\\bSET\\b|\\bDELETE\\s+FROM\\b|\\bDROP\\s+(TABLE|DATABASE|INDEX)\\b|\\bUNION\\s+(ALL\\s+)?SELECT\\b|--\\s*$|;\\s*(SELECT|INSERT|UPDATE|DELETE))'
|
|
544
|
+
GROUP BY key
|
|
545
|
+
`;
|
|
546
|
+
const injectionResult = await adapter.executeQuery(injectionSql);
|
|
547
|
+
for (const row of (injectionResult.rows ?? [])) {
|
|
548
|
+
issues.push({
|
|
549
|
+
type: "sql_injection_pattern",
|
|
550
|
+
key: row.key,
|
|
551
|
+
count: Number(row.count),
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
// XSS pattern detection
|
|
555
|
+
const xssSql = `
|
|
556
|
+
SELECT key, COUNT(*) as count
|
|
557
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t,
|
|
558
|
+
jsonb_each_text(${columnName})
|
|
559
|
+
WHERE value ~* '(<script|javascript:|on(click|load|error|mouseover)\\s*=|<iframe|<object|<embed|<svg[^>]+on|<img[^>]+onerror)'
|
|
560
|
+
GROUP BY key
|
|
561
|
+
`;
|
|
562
|
+
const xssResult = await adapter.executeQuery(xssSql);
|
|
563
|
+
for (const row of (xssResult.rows ?? [])) {
|
|
564
|
+
issues.push({
|
|
565
|
+
type: "xss_pattern",
|
|
566
|
+
key: row.key,
|
|
567
|
+
count: Number(row.count),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
scannedRows: actualRowsScanned,
|
|
572
|
+
issues,
|
|
573
|
+
riskLevel: issues.length === 0 ? "low" : issues.length < 3 ? "medium" : "high",
|
|
574
|
+
};
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Get JSONB column statistics
|
|
580
|
+
*/
|
|
581
|
+
export function createJsonbStatsTool(adapter) {
|
|
582
|
+
return {
|
|
583
|
+
name: "pg_jsonb_stats",
|
|
584
|
+
description: "Get statistics about JSONB column usage. Note: topKeys only applies to object-type JSONB, not arrays.",
|
|
585
|
+
group: "jsonb",
|
|
586
|
+
inputSchema: z.object({
|
|
587
|
+
table: z.string().describe("Table name"),
|
|
588
|
+
column: z.string().describe("JSONB column"),
|
|
589
|
+
sampleSize: z.number().optional().describe("Sample rows to analyze"),
|
|
590
|
+
where: z.string().optional().describe("WHERE clause to filter rows"),
|
|
591
|
+
}),
|
|
592
|
+
annotations: readOnly("JSONB Stats"),
|
|
593
|
+
icons: getToolIcons("jsonb", readOnly("JSONB Stats")),
|
|
594
|
+
handler: async (params, _context) => {
|
|
595
|
+
const parsed = params;
|
|
596
|
+
const sample = parsed.sampleSize ?? 1000;
|
|
597
|
+
const whereClause = parsed.where ? ` WHERE ${parsed.where}` : "";
|
|
598
|
+
const tableName = sanitizeTableName(parsed.table);
|
|
599
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
600
|
+
const basicSql = `
|
|
601
|
+
SELECT
|
|
602
|
+
COUNT(*) as total_rows,
|
|
603
|
+
COUNT(${columnName}) as non_null_count,
|
|
604
|
+
AVG(length(${columnName}::text))::int as avg_size_bytes,
|
|
605
|
+
MAX(length(${columnName}::text)) as max_size_bytes
|
|
606
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t
|
|
607
|
+
`;
|
|
608
|
+
const basicResult = await adapter.executeQuery(basicSql);
|
|
609
|
+
// Cast bigint values to numbers (PostgreSQL returns bigint as string)
|
|
610
|
+
const basics = basicResult.rows?.[0];
|
|
611
|
+
const basicsNormalized = basics
|
|
612
|
+
? {
|
|
613
|
+
total_rows: Number(basics["total_rows"]),
|
|
614
|
+
non_null_count: Number(basics["non_null_count"]),
|
|
615
|
+
avg_size_bytes: Number(basics["avg_size_bytes"]),
|
|
616
|
+
max_size_bytes: Number(basics["max_size_bytes"]),
|
|
617
|
+
}
|
|
618
|
+
: undefined;
|
|
619
|
+
const keySql = `
|
|
620
|
+
SELECT key, COUNT(*) as frequency
|
|
621
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t,
|
|
622
|
+
jsonb_object_keys(${columnName}) key
|
|
623
|
+
GROUP BY key
|
|
624
|
+
ORDER BY frequency DESC
|
|
625
|
+
LIMIT 20
|
|
626
|
+
`;
|
|
627
|
+
let topKeys = [];
|
|
628
|
+
try {
|
|
629
|
+
const keyResult = await adapter.executeQuery(keySql);
|
|
630
|
+
// Cast frequency to number
|
|
631
|
+
topKeys = (keyResult.rows ?? []).map((row) => ({
|
|
632
|
+
key: row["key"],
|
|
633
|
+
frequency: Number(row["frequency"]),
|
|
634
|
+
}));
|
|
635
|
+
}
|
|
636
|
+
catch (error) {
|
|
637
|
+
// Gracefully handle array columns (jsonb_object_keys fails on arrays)
|
|
638
|
+
if (error instanceof Error &&
|
|
639
|
+
error.message.includes("cannot call jsonb_object_keys")) {
|
|
640
|
+
// Leave topKeys empty for array columns - this is valid
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
throw error;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const typeSql = `
|
|
647
|
+
SELECT jsonb_typeof(${columnName}) as type, COUNT(*) as count
|
|
648
|
+
FROM (SELECT * FROM ${tableName}${whereClause} LIMIT ${String(sample)}) t
|
|
649
|
+
GROUP BY jsonb_typeof(${columnName})
|
|
650
|
+
`;
|
|
651
|
+
const typeResult = await adapter.executeQuery(typeSql);
|
|
652
|
+
// Cast count to number
|
|
653
|
+
const typeDistribution = (typeResult.rows ?? []).map((row) => ({
|
|
654
|
+
type: row["type"],
|
|
655
|
+
count: Number(row["count"]),
|
|
656
|
+
}));
|
|
657
|
+
// Calculate SQL NULL count for disambiguation
|
|
658
|
+
const sqlNullCount = typeDistribution.find((t) => t.type === null)?.count ?? 0;
|
|
659
|
+
const hasNullColumns = sqlNullCount > 0;
|
|
660
|
+
const isArrayColumn = typeDistribution.some((t) => t.type === "array");
|
|
661
|
+
// Determine appropriate hint
|
|
662
|
+
let hint;
|
|
663
|
+
if (hasNullColumns) {
|
|
664
|
+
hint =
|
|
665
|
+
"typeDistribution null type represents SQL NULL columns, not JSON null values";
|
|
666
|
+
}
|
|
667
|
+
else if (topKeys.length === 0 && isArrayColumn) {
|
|
668
|
+
hint =
|
|
669
|
+
'topKeys empty for array columns - use pg_jsonb_normalize mode: "array" to analyze elements';
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
basics: basicsNormalized,
|
|
673
|
+
topKeys,
|
|
674
|
+
typeDistribution,
|
|
675
|
+
sqlNullCount,
|
|
676
|
+
hint,
|
|
677
|
+
};
|
|
678
|
+
},
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
//# sourceMappingURL=advanced.js.map
|