db-mcp 1.1.1 → 3.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.
Files changed (508) hide show
  1. package/README.md +247 -169
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/adapters/database-adapter.d.ts +173 -0
  4. package/dist/adapters/database-adapter.d.ts.map +1 -0
  5. package/dist/adapters/query-validation.d.ts +20 -0
  6. package/dist/adapters/query-validation.d.ts.map +1 -0
  7. package/dist/adapters/registration/index.d.ts +4 -0
  8. package/dist/adapters/registration/index.d.ts.map +1 -0
  9. package/dist/adapters/registration/prompts.d.ts +7 -0
  10. package/dist/adapters/registration/prompts.d.ts.map +1 -0
  11. package/dist/adapters/registration/resources.d.ts +7 -0
  12. package/dist/adapters/registration/resources.d.ts.map +1 -0
  13. package/dist/adapters/registration/tools.d.ts +9 -0
  14. package/dist/adapters/registration/tools.d.ts.map +1 -0
  15. package/dist/adapters/sqlite/index.d.ts +9 -0
  16. package/dist/adapters/sqlite/index.d.ts.map +1 -0
  17. package/dist/adapters/sqlite/json-utils.d.ts +100 -0
  18. package/dist/adapters/sqlite/json-utils.d.ts.map +1 -0
  19. package/dist/adapters/sqlite/prompts/analysis.d.ts +23 -0
  20. package/dist/adapters/sqlite/prompts/analysis.d.ts.map +1 -0
  21. package/dist/adapters/sqlite/prompts/index.d.ts +16 -0
  22. package/dist/adapters/sqlite/prompts/index.d.ts.map +1 -0
  23. package/dist/adapters/sqlite/prompts/query.d.ts +19 -0
  24. package/dist/adapters/sqlite/prompts/query.d.ts.map +1 -0
  25. package/dist/adapters/sqlite/prompts/schema.d.ts +20 -0
  26. package/dist/adapters/sqlite/prompts/schema.d.ts.map +1 -0
  27. package/dist/adapters/sqlite/query-executor.d.ts +38 -0
  28. package/dist/adapters/sqlite/query-executor.d.ts.map +1 -0
  29. package/dist/adapters/sqlite/resources.d.ts +13 -0
  30. package/dist/adapters/sqlite/resources.d.ts.map +1 -0
  31. package/dist/adapters/sqlite/schema-manager.d.ts +69 -0
  32. package/dist/adapters/sqlite/schema-manager.d.ts.map +1 -0
  33. package/dist/adapters/sqlite/schemas/admin.d.ts +459 -0
  34. package/dist/adapters/sqlite/schemas/admin.d.ts.map +1 -0
  35. package/dist/adapters/sqlite/schemas/codemode.d.ts +30 -0
  36. package/dist/adapters/sqlite/schemas/codemode.d.ts.map +1 -0
  37. package/dist/adapters/sqlite/schemas/common.d.ts +9 -0
  38. package/dist/adapters/sqlite/schemas/common.d.ts.map +1 -0
  39. package/dist/adapters/sqlite/schemas/core.d.ts +530 -0
  40. package/dist/adapters/sqlite/schemas/core.d.ts.map +1 -0
  41. package/dist/adapters/sqlite/schemas/error-mixin.d.ts +10 -0
  42. package/dist/adapters/sqlite/schemas/error-mixin.d.ts.map +1 -0
  43. package/dist/adapters/sqlite/schemas/fts.d.ts +106 -0
  44. package/dist/adapters/sqlite/schemas/fts.d.ts.map +1 -0
  45. package/dist/adapters/sqlite/schemas/geo.d.ts +135 -0
  46. package/dist/adapters/sqlite/schemas/geo.d.ts.map +1 -0
  47. package/dist/adapters/sqlite/schemas/index.d.ts +23 -0
  48. package/dist/adapters/sqlite/schemas/index.d.ts.map +1 -0
  49. package/dist/adapters/sqlite/schemas/introspection.d.ts +583 -0
  50. package/dist/adapters/sqlite/schemas/introspection.d.ts.map +1 -0
  51. package/dist/adapters/sqlite/schemas/json.d.ts +849 -0
  52. package/dist/adapters/sqlite/schemas/json.d.ts.map +1 -0
  53. package/dist/adapters/sqlite/schemas/migration.d.ts +220 -0
  54. package/dist/adapters/sqlite/schemas/migration.d.ts.map +1 -0
  55. package/dist/adapters/sqlite/schemas/native.d.ts +235 -0
  56. package/dist/adapters/sqlite/schemas/native.d.ts.map +1 -0
  57. package/dist/adapters/sqlite/schemas/server.d.ts +13 -0
  58. package/dist/adapters/sqlite/schemas/server.d.ts.map +1 -0
  59. package/dist/adapters/sqlite/schemas/spatialite.d.ts +114 -0
  60. package/dist/adapters/sqlite/schemas/spatialite.d.ts.map +1 -0
  61. package/dist/adapters/sqlite/schemas/stats.d.ts +780 -0
  62. package/dist/adapters/sqlite/schemas/stats.d.ts.map +1 -0
  63. package/dist/adapters/sqlite/schemas/text.d.ts +570 -0
  64. package/dist/adapters/sqlite/schemas/text.d.ts.map +1 -0
  65. package/dist/adapters/sqlite/schemas/vector.d.ts +252 -0
  66. package/dist/adapters/sqlite/schemas/vector.d.ts.map +1 -0
  67. package/dist/adapters/sqlite/schemas/virtual.d.ts +257 -0
  68. package/dist/adapters/sqlite/schemas/virtual.d.ts.map +1 -0
  69. package/dist/adapters/sqlite/schemas/where.d.ts +20 -0
  70. package/dist/adapters/sqlite/schemas/where.d.ts.map +1 -0
  71. package/dist/adapters/sqlite/sqlite-adapter/lifecycle.d.ts +13 -0
  72. package/dist/adapters/sqlite/sqlite-adapter/lifecycle.d.ts.map +1 -0
  73. package/dist/adapters/sqlite/sqlite-adapter/schema.d.ts +7 -0
  74. package/dist/adapters/sqlite/sqlite-adapter/schema.d.ts.map +1 -0
  75. package/dist/adapters/sqlite/sqlite-adapter.d.ts +138 -0
  76. package/dist/adapters/sqlite/sqlite-adapter.d.ts.map +1 -0
  77. package/dist/adapters/sqlite/tools/admin/backup/analyze.d.ts +7 -0
  78. package/dist/adapters/sqlite/tools/admin/backup/analyze.d.ts.map +1 -0
  79. package/dist/adapters/sqlite/tools/admin/backup/create.d.ts +11 -0
  80. package/dist/adapters/sqlite/tools/admin/backup/create.d.ts.map +1 -0
  81. package/dist/adapters/sqlite/tools/admin/backup/dump.d.ts +7 -0
  82. package/dist/adapters/sqlite/tools/admin/backup/dump.d.ts.map +1 -0
  83. package/dist/adapters/sqlite/tools/admin/backup/index.d.ts +7 -0
  84. package/dist/adapters/sqlite/tools/admin/backup/index.d.ts.map +1 -0
  85. package/dist/adapters/sqlite/tools/admin/backup/integrity.d.ts +7 -0
  86. package/dist/adapters/sqlite/tools/admin/backup/integrity.d.ts.map +1 -0
  87. package/dist/adapters/sqlite/tools/admin/backup/optimize.d.ts +7 -0
  88. package/dist/adapters/sqlite/tools/admin/backup/optimize.d.ts.map +1 -0
  89. package/dist/adapters/sqlite/tools/admin/backup/restore.d.ts +7 -0
  90. package/dist/adapters/sqlite/tools/admin/backup/restore.d.ts.map +1 -0
  91. package/dist/adapters/sqlite/tools/admin/helpers.d.ts +18 -0
  92. package/dist/adapters/sqlite/tools/admin/helpers.d.ts.map +1 -0
  93. package/dist/adapters/sqlite/tools/admin/index.d.ts +10 -0
  94. package/dist/adapters/sqlite/tools/admin/index.d.ts.map +1 -0
  95. package/dist/adapters/sqlite/tools/admin/pragma.d.ts +37 -0
  96. package/dist/adapters/sqlite/tools/admin/pragma.d.ts.map +1 -0
  97. package/dist/adapters/sqlite/tools/admin/reindex.d.ts +10 -0
  98. package/dist/adapters/sqlite/tools/admin/reindex.d.ts.map +1 -0
  99. package/dist/adapters/sqlite/tools/admin/verify.d.ts +13 -0
  100. package/dist/adapters/sqlite/tools/admin/verify.d.ts.map +1 -0
  101. package/dist/adapters/sqlite/tools/admin/wal.d.ts +10 -0
  102. package/dist/adapters/sqlite/tools/admin/wal.d.ts.map +1 -0
  103. package/dist/adapters/sqlite/tools/codemode.d.ts +22 -0
  104. package/dist/adapters/sqlite/tools/codemode.d.ts.map +1 -0
  105. package/dist/adapters/sqlite/tools/column-validation.d.ts +26 -0
  106. package/dist/adapters/sqlite/tools/column-validation.d.ts.map +1 -0
  107. package/dist/adapters/sqlite/tools/core/alter-table.d.ts +11 -0
  108. package/dist/adapters/sqlite/tools/core/alter-table.d.ts.map +1 -0
  109. package/dist/adapters/sqlite/tools/core/constraints.d.ts +10 -0
  110. package/dist/adapters/sqlite/tools/core/constraints.d.ts.map +1 -0
  111. package/dist/adapters/sqlite/tools/core/convenience-helpers.d.ts +20 -0
  112. package/dist/adapters/sqlite/tools/core/convenience-helpers.d.ts.map +1 -0
  113. package/dist/adapters/sqlite/tools/core/convenience-schemas.d.ts +20 -0
  114. package/dist/adapters/sqlite/tools/core/convenience-schemas.d.ts.map +1 -0
  115. package/dist/adapters/sqlite/tools/core/convenience.d.ts +29 -0
  116. package/dist/adapters/sqlite/tools/core/convenience.d.ts.map +1 -0
  117. package/dist/adapters/sqlite/tools/core/datetime.d.ts +11 -0
  118. package/dist/adapters/sqlite/tools/core/datetime.d.ts.map +1 -0
  119. package/dist/adapters/sqlite/tools/core/index.d.ts +14 -0
  120. package/dist/adapters/sqlite/tools/core/index.d.ts.map +1 -0
  121. package/dist/adapters/sqlite/tools/core/indexes.d.ts +20 -0
  122. package/dist/adapters/sqlite/tools/core/indexes.d.ts.map +1 -0
  123. package/dist/adapters/sqlite/tools/core/queries.d.ts +16 -0
  124. package/dist/adapters/sqlite/tools/core/queries.d.ts.map +1 -0
  125. package/dist/adapters/sqlite/tools/core/tables.d.ts +37 -0
  126. package/dist/adapters/sqlite/tools/core/tables.d.ts.map +1 -0
  127. package/dist/adapters/sqlite/tools/core/triggers.d.ts +18 -0
  128. package/dist/adapters/sqlite/tools/core/triggers.d.ts.map +1 -0
  129. package/dist/adapters/sqlite/tools/fts.d.ts +13 -0
  130. package/dist/adapters/sqlite/tools/fts.d.ts.map +1 -0
  131. package/dist/adapters/sqlite/tools/geo.d.ts +14 -0
  132. package/dist/adapters/sqlite/tools/geo.d.ts.map +1 -0
  133. package/dist/adapters/sqlite/tools/index.d.ts +33 -0
  134. package/dist/adapters/sqlite/tools/index.d.ts.map +1 -0
  135. package/dist/adapters/sqlite/tools/introspection/analysis/constraints.d.ts +10 -0
  136. package/dist/adapters/sqlite/tools/introspection/analysis/constraints.d.ts.map +1 -0
  137. package/dist/adapters/sqlite/tools/introspection/analysis/diff.d.ts +11 -0
  138. package/dist/adapters/sqlite/tools/introspection/analysis/diff.d.ts.map +1 -0
  139. package/dist/adapters/sqlite/tools/introspection/analysis/index.d.ts +10 -0
  140. package/dist/adapters/sqlite/tools/introspection/analysis/index.d.ts.map +1 -0
  141. package/dist/adapters/sqlite/tools/introspection/analysis/risks.d.ts +9 -0
  142. package/dist/adapters/sqlite/tools/introspection/analysis/risks.d.ts.map +1 -0
  143. package/dist/adapters/sqlite/tools/introspection/analysis/snapshot.d.ts +40 -0
  144. package/dist/adapters/sqlite/tools/introspection/analysis/snapshot.d.ts.map +1 -0
  145. package/dist/adapters/sqlite/tools/introspection/diagnostics/index.d.ts +9 -0
  146. package/dist/adapters/sqlite/tools/introspection/diagnostics/index.d.ts.map +1 -0
  147. package/dist/adapters/sqlite/tools/introspection/diagnostics/indexes.d.ts +10 -0
  148. package/dist/adapters/sqlite/tools/introspection/diagnostics/indexes.d.ts.map +1 -0
  149. package/dist/adapters/sqlite/tools/introspection/diagnostics/query-plan.d.ts +10 -0
  150. package/dist/adapters/sqlite/tools/introspection/diagnostics/query-plan.d.ts.map +1 -0
  151. package/dist/adapters/sqlite/tools/introspection/diagnostics/storage.d.ts +10 -0
  152. package/dist/adapters/sqlite/tools/introspection/diagnostics/storage.d.ts.map +1 -0
  153. package/dist/adapters/sqlite/tools/introspection/graph/helpers.d.ts +44 -0
  154. package/dist/adapters/sqlite/tools/introspection/graph/helpers.d.ts.map +1 -0
  155. package/dist/adapters/sqlite/tools/introspection/graph/index.d.ts +9 -0
  156. package/dist/adapters/sqlite/tools/introspection/graph/index.d.ts.map +1 -0
  157. package/dist/adapters/sqlite/tools/introspection/graph/tools.d.ts +12 -0
  158. package/dist/adapters/sqlite/tools/introspection/graph/tools.d.ts.map +1 -0
  159. package/dist/adapters/sqlite/tools/introspection/index.d.ts +17 -0
  160. package/dist/adapters/sqlite/tools/introspection/index.d.ts.map +1 -0
  161. package/dist/adapters/sqlite/tools/json-helpers/helpers.d.ts +21 -0
  162. package/dist/adapters/sqlite/tools/json-helpers/helpers.d.ts.map +1 -0
  163. package/dist/adapters/sqlite/tools/json-helpers/index.d.ts +17 -0
  164. package/dist/adapters/sqlite/tools/json-helpers/index.d.ts.map +1 -0
  165. package/dist/adapters/sqlite/tools/json-helpers/read.d.ts +24 -0
  166. package/dist/adapters/sqlite/tools/json-helpers/read.d.ts.map +1 -0
  167. package/dist/adapters/sqlite/tools/json-helpers/write.d.ts +24 -0
  168. package/dist/adapters/sqlite/tools/json-helpers/write.d.ts.map +1 -0
  169. package/dist/adapters/sqlite/tools/json-operations/crud.d.ts +39 -0
  170. package/dist/adapters/sqlite/tools/json-operations/crud.d.ts.map +1 -0
  171. package/dist/adapters/sqlite/tools/json-operations/diff.d.ts +11 -0
  172. package/dist/adapters/sqlite/tools/json-operations/diff.d.ts.map +1 -0
  173. package/dist/adapters/sqlite/tools/json-operations/helpers.d.ts +18 -0
  174. package/dist/adapters/sqlite/tools/json-operations/helpers.d.ts.map +1 -0
  175. package/dist/adapters/sqlite/tools/json-operations/index.d.ts +10 -0
  176. package/dist/adapters/sqlite/tools/json-operations/index.d.ts.map +1 -0
  177. package/dist/adapters/sqlite/tools/json-operations/query.d.ts +24 -0
  178. package/dist/adapters/sqlite/tools/json-operations/query.d.ts.map +1 -0
  179. package/dist/adapters/sqlite/tools/json-operations/security.d.ts +19 -0
  180. package/dist/adapters/sqlite/tools/json-operations/security.d.ts.map +1 -0
  181. package/dist/adapters/sqlite/tools/json-operations/transform.d.ts +24 -0
  182. package/dist/adapters/sqlite/tools/json-operations/transform.d.ts.map +1 -0
  183. package/dist/adapters/sqlite/tools/migration/helpers.d.ts +34 -0
  184. package/dist/adapters/sqlite/tools/migration/helpers.d.ts.map +1 -0
  185. package/dist/adapters/sqlite/tools/migration/index.d.ts +15 -0
  186. package/dist/adapters/sqlite/tools/migration/index.d.ts.map +1 -0
  187. package/dist/adapters/sqlite/tools/migration/tracking/apply.d.ts +4 -0
  188. package/dist/adapters/sqlite/tools/migration/tracking/apply.d.ts.map +1 -0
  189. package/dist/adapters/sqlite/tools/migration/tracking/history.d.ts +4 -0
  190. package/dist/adapters/sqlite/tools/migration/tracking/history.d.ts.map +1 -0
  191. package/dist/adapters/sqlite/tools/migration/tracking/index.d.ts +7 -0
  192. package/dist/adapters/sqlite/tools/migration/tracking/index.d.ts.map +1 -0
  193. package/dist/adapters/sqlite/tools/migration/tracking/init.d.ts +4 -0
  194. package/dist/adapters/sqlite/tools/migration/tracking/init.d.ts.map +1 -0
  195. package/dist/adapters/sqlite/tools/migration/tracking/record.d.ts +4 -0
  196. package/dist/adapters/sqlite/tools/migration/tracking/record.d.ts.map +1 -0
  197. package/dist/adapters/sqlite/tools/migration/tracking/rollback.d.ts +4 -0
  198. package/dist/adapters/sqlite/tools/migration/tracking/rollback.d.ts.map +1 -0
  199. package/dist/adapters/sqlite/tools/migration/tracking/status.d.ts +4 -0
  200. package/dist/adapters/sqlite/tools/migration/tracking/status.d.ts.map +1 -0
  201. package/dist/adapters/sqlite/tools/stats/advanced.d.ts +28 -0
  202. package/dist/adapters/sqlite/tools/stats/advanced.d.ts.map +1 -0
  203. package/dist/adapters/sqlite/tools/stats/anomaly-detection.d.ts +22 -0
  204. package/dist/adapters/sqlite/tools/stats/anomaly-detection.d.ts.map +1 -0
  205. package/dist/adapters/sqlite/tools/stats/basic.d.ts +32 -0
  206. package/dist/adapters/sqlite/tools/stats/basic.d.ts.map +1 -0
  207. package/dist/adapters/sqlite/tools/stats/helpers.d.ts +27 -0
  208. package/dist/adapters/sqlite/tools/stats/helpers.d.ts.map +1 -0
  209. package/dist/adapters/sqlite/tools/stats/index.d.ts +12 -0
  210. package/dist/adapters/sqlite/tools/stats/index.d.ts.map +1 -0
  211. package/dist/adapters/sqlite/tools/stats/inference/hypothesis.d.ts +7 -0
  212. package/dist/adapters/sqlite/tools/stats/inference/hypothesis.d.ts.map +1 -0
  213. package/dist/adapters/sqlite/tools/stats/inference/index.d.ts +4 -0
  214. package/dist/adapters/sqlite/tools/stats/inference/index.d.ts.map +1 -0
  215. package/dist/adapters/sqlite/tools/stats/inference/outlier.d.ts +7 -0
  216. package/dist/adapters/sqlite/tools/stats/inference/outlier.d.ts.map +1 -0
  217. package/dist/adapters/sqlite/tools/stats/inference/regression.d.ts +7 -0
  218. package/dist/adapters/sqlite/tools/stats/inference/regression.d.ts.map +1 -0
  219. package/dist/adapters/sqlite/tools/stats/math-helpers.d.ts +17 -0
  220. package/dist/adapters/sqlite/tools/stats/math-helpers.d.ts.map +1 -0
  221. package/dist/adapters/sqlite/tools/stats/schema-risks.d.ts +15 -0
  222. package/dist/adapters/sqlite/tools/stats/schema-risks.d.ts.map +1 -0
  223. package/dist/adapters/sqlite/tools/text/formatting.d.ts +48 -0
  224. package/dist/adapters/sqlite/tools/text/formatting.d.ts.map +1 -0
  225. package/dist/adapters/sqlite/tools/text/helpers.d.ts +22 -0
  226. package/dist/adapters/sqlite/tools/text/helpers.d.ts.map +1 -0
  227. package/dist/adapters/sqlite/tools/text/index.d.ts +10 -0
  228. package/dist/adapters/sqlite/tools/text/index.d.ts.map +1 -0
  229. package/dist/adapters/sqlite/tools/text/regex.d.ts +21 -0
  230. package/dist/adapters/sqlite/tools/text/regex.d.ts.map +1 -0
  231. package/dist/adapters/sqlite/tools/text/search.d.ts +17 -0
  232. package/dist/adapters/sqlite/tools/text/search.d.ts.map +1 -0
  233. package/dist/adapters/sqlite/tools/text/sentiment.d.ts +13 -0
  234. package/dist/adapters/sqlite/tools/text/sentiment.d.ts.map +1 -0
  235. package/dist/adapters/sqlite/tools/text/validate.d.ts +11 -0
  236. package/dist/adapters/sqlite/tools/text/validate.d.ts.map +1 -0
  237. package/dist/adapters/sqlite/tools/vector/helpers.d.ts +11 -0
  238. package/dist/adapters/sqlite/tools/vector/helpers.d.ts.map +1 -0
  239. package/dist/adapters/sqlite/tools/vector/index.d.ts +14 -0
  240. package/dist/adapters/sqlite/tools/vector/index.d.ts.map +1 -0
  241. package/dist/adapters/sqlite/tools/vector/metadata.d.ts +28 -0
  242. package/dist/adapters/sqlite/tools/vector/metadata.d.ts.map +1 -0
  243. package/dist/adapters/sqlite/tools/vector/search.d.ts +16 -0
  244. package/dist/adapters/sqlite/tools/vector/search.d.ts.map +1 -0
  245. package/dist/adapters/sqlite/tools/vector/storage.d.ts +24 -0
  246. package/dist/adapters/sqlite/tools/vector/storage.d.ts.map +1 -0
  247. package/dist/adapters/sqlite/tools/vector/tools.d.ts +9 -0
  248. package/dist/adapters/sqlite/tools/vector/tools.d.ts.map +1 -0
  249. package/dist/adapters/sqlite/tools/virtual/analysis.d.ts +24 -0
  250. package/dist/adapters/sqlite/tools/virtual/analysis.d.ts.map +1 -0
  251. package/dist/adapters/sqlite/tools/virtual/extensions.d.ts +13 -0
  252. package/dist/adapters/sqlite/tools/virtual/extensions.d.ts.map +1 -0
  253. package/dist/adapters/sqlite/tools/virtual/helpers.d.ts +18 -0
  254. package/dist/adapters/sqlite/tools/virtual/helpers.d.ts.map +1 -0
  255. package/dist/adapters/sqlite/tools/virtual/index.d.ts +10 -0
  256. package/dist/adapters/sqlite/tools/virtual/index.d.ts.map +1 -0
  257. package/dist/adapters/sqlite/tools/virtual/views.d.ts +21 -0
  258. package/dist/adapters/sqlite/tools/virtual/views.d.ts.map +1 -0
  259. package/dist/adapters/sqlite/tools/virtual/vtable/analyze-csv.d.ts +4 -0
  260. package/dist/adapters/sqlite/tools/virtual/vtable/analyze-csv.d.ts.map +1 -0
  261. package/dist/adapters/sqlite/tools/virtual/vtable/csv.d.ts +4 -0
  262. package/dist/adapters/sqlite/tools/virtual/vtable/csv.d.ts.map +1 -0
  263. package/dist/adapters/sqlite/tools/virtual/vtable/drop.d.ts +4 -0
  264. package/dist/adapters/sqlite/tools/virtual/vtable/drop.d.ts.map +1 -0
  265. package/dist/adapters/sqlite/tools/virtual/vtable/index.d.ts +6 -0
  266. package/dist/adapters/sqlite/tools/virtual/vtable/index.d.ts.map +1 -0
  267. package/dist/adapters/sqlite/tools/virtual/vtable/info.d.ts +4 -0
  268. package/dist/adapters/sqlite/tools/virtual/vtable/info.d.ts.map +1 -0
  269. package/dist/adapters/sqlite/tools/virtual/vtable/list.d.ts +4 -0
  270. package/dist/adapters/sqlite/tools/virtual/vtable/list.d.ts.map +1 -0
  271. package/dist/adapters/sqlite/types.d.ts +89 -0
  272. package/dist/adapters/sqlite/types.d.ts.map +1 -0
  273. package/dist/adapters/sqlite-helpers.d.ts +51 -0
  274. package/dist/adapters/sqlite-helpers.d.ts.map +1 -0
  275. package/dist/adapters/sqlite-native/extensions.d.ts +17 -0
  276. package/dist/adapters/sqlite-native/extensions.d.ts.map +1 -0
  277. package/dist/adapters/sqlite-native/index.d.ts +11 -0
  278. package/dist/adapters/sqlite-native/index.d.ts.map +1 -0
  279. package/dist/adapters/sqlite-native/native-query-executor.d.ts +24 -0
  280. package/dist/adapters/sqlite-native/native-query-executor.d.ts.map +1 -0
  281. package/dist/adapters/sqlite-native/native-sqlite-adapter.d.ts +160 -0
  282. package/dist/adapters/sqlite-native/native-sqlite-adapter.d.ts.map +1 -0
  283. package/dist/adapters/sqlite-native/registration/index.d.ts +12 -0
  284. package/dist/adapters/sqlite-native/registration/index.d.ts.map +1 -0
  285. package/dist/adapters/sqlite-native/tools/spatialite/analysis.d.ts +23 -0
  286. package/dist/adapters/sqlite-native/tools/spatialite/analysis.d.ts.map +1 -0
  287. package/dist/adapters/sqlite-native/tools/spatialite/index.d.ts +15 -0
  288. package/dist/adapters/sqlite-native/tools/spatialite/index.d.ts.map +1 -0
  289. package/dist/adapters/sqlite-native/tools/spatialite/loader.d.ts +22 -0
  290. package/dist/adapters/sqlite-native/tools/spatialite/loader.d.ts.map +1 -0
  291. package/dist/adapters/sqlite-native/tools/spatialite/schemas.d.ts +78 -0
  292. package/dist/adapters/sqlite-native/tools/spatialite/schemas.d.ts.map +1 -0
  293. package/dist/adapters/sqlite-native/tools/spatialite/tools.d.ts +30 -0
  294. package/dist/adapters/sqlite-native/tools/spatialite/tools.d.ts.map +1 -0
  295. package/dist/adapters/sqlite-native/tools/transactions.d.ts +12 -0
  296. package/dist/adapters/sqlite-native/tools/transactions.d.ts.map +1 -0
  297. package/dist/adapters/sqlite-native/tools/window.d.ts +7 -0
  298. package/dist/adapters/sqlite-native/tools/window.d.ts.map +1 -0
  299. package/dist/adapters/sqlite-native/transaction-methods.d.ts +36 -0
  300. package/dist/adapters/sqlite-native/transaction-methods.d.ts.map +1 -0
  301. package/dist/audit/backup-manager.d.ts +90 -0
  302. package/dist/audit/backup-manager.d.ts.map +1 -0
  303. package/dist/audit/index.d.ts +11 -0
  304. package/dist/audit/index.d.ts.map +1 -0
  305. package/dist/audit/interceptor.d.ts +54 -0
  306. package/dist/audit/interceptor.d.ts.map +1 -0
  307. package/dist/audit/logger.d.ts +62 -0
  308. package/dist/audit/logger.d.ts.map +1 -0
  309. package/dist/audit/types.d.ts +112 -0
  310. package/dist/audit/types.d.ts.map +1 -0
  311. package/dist/auth/auth-context.d.ts +28 -0
  312. package/dist/auth/auth-context.d.ts.map +1 -0
  313. package/dist/auth/authorization-server-discovery.d.ts +90 -0
  314. package/dist/auth/authorization-server-discovery.d.ts.map +1 -0
  315. package/dist/auth/errors.d.ts +74 -0
  316. package/dist/auth/errors.d.ts.map +1 -0
  317. package/dist/auth/middleware/core.d.ts +22 -0
  318. package/dist/auth/middleware/core.d.ts.map +1 -0
  319. package/dist/auth/middleware/express-auth.d.ts +13 -0
  320. package/dist/auth/middleware/express-auth.d.ts.map +1 -0
  321. package/dist/auth/middleware/express-scopes.d.ts +6 -0
  322. package/dist/auth/middleware/express-scopes.d.ts.map +1 -0
  323. package/dist/auth/middleware/extraction.d.ts +2 -0
  324. package/dist/auth/middleware/extraction.d.ts.map +1 -0
  325. package/dist/auth/middleware/index.d.ts +5 -0
  326. package/dist/auth/middleware/index.d.ts.map +1 -0
  327. package/dist/auth/oauth-resource-server.d.ts +74 -0
  328. package/dist/auth/oauth-resource-server.d.ts.map +1 -0
  329. package/dist/auth/scope-map.d.ts +24 -0
  330. package/dist/auth/scope-map.d.ts.map +1 -0
  331. package/dist/auth/scopes/constants.d.ts +40 -0
  332. package/dist/auth/scopes/constants.d.ts.map +1 -0
  333. package/dist/auth/scopes/display.d.ts +5 -0
  334. package/dist/auth/scopes/display.d.ts.map +1 -0
  335. package/dist/auth/scopes/enforcement.d.ts +27 -0
  336. package/dist/auth/scopes/enforcement.d.ts.map +1 -0
  337. package/dist/auth/scopes/index.d.ts +6 -0
  338. package/dist/auth/scopes/index.d.ts.map +1 -0
  339. package/dist/auth/scopes/mapping.d.ts +39 -0
  340. package/dist/auth/scopes/mapping.d.ts.map +1 -0
  341. package/dist/auth/scopes/validation.d.ts +27 -0
  342. package/dist/auth/scopes/validation.d.ts.map +1 -0
  343. package/dist/auth/token-validator.d.ts +63 -0
  344. package/dist/auth/token-validator.d.ts.map +1 -0
  345. package/dist/auth/transport-agnostic.d.ts +11 -0
  346. package/dist/auth/transport-agnostic.d.ts.map +1 -0
  347. package/dist/auth/types.d.ts +257 -0
  348. package/dist/auth/types.d.ts.map +1 -0
  349. package/dist/chunk-E5IESRTK.js +489 -0
  350. package/dist/{chunk-TVIZ3XJH.js → chunk-FR65YPAH.js} +8509 -3925
  351. package/dist/{chunk-AOUL5SHS.js → chunk-L552U3QS.js} +880 -252
  352. package/dist/chunk-THATOQRT.js +2624 -0
  353. package/dist/chunk-W5WQVNVX.js +568 -0
  354. package/dist/cli.d.ts +7 -0
  355. package/dist/cli.d.ts.map +1 -0
  356. package/dist/cli.js +114 -19
  357. package/dist/codemode/api-constants.d.ts +40 -0
  358. package/dist/codemode/api-constants.d.ts.map +1 -0
  359. package/dist/codemode/api.d.ts +70 -0
  360. package/dist/codemode/api.d.ts.map +1 -0
  361. package/dist/codemode/auto-return.d.ts +25 -0
  362. package/dist/codemode/auto-return.d.ts.map +1 -0
  363. package/dist/codemode/index.d.ts +12 -0
  364. package/dist/codemode/index.d.ts.map +1 -0
  365. package/dist/codemode/sandbox-factory.d.ts +72 -0
  366. package/dist/codemode/sandbox-factory.d.ts.map +1 -0
  367. package/dist/codemode/sandbox.d.ts +54 -0
  368. package/dist/codemode/sandbox.d.ts.map +1 -0
  369. package/dist/codemode/security.d.ts +45 -0
  370. package/dist/codemode/security.d.ts.map +1 -0
  371. package/dist/codemode/types.d.ts +167 -0
  372. package/dist/codemode/types.d.ts.map +1 -0
  373. package/dist/constants/server-instructions.d.ts +22 -0
  374. package/dist/constants/server-instructions.d.ts.map +1 -0
  375. package/dist/filtering/tool-constants.d.ts +45 -0
  376. package/dist/filtering/tool-constants.d.ts.map +1 -0
  377. package/dist/filtering/tool-filter.d.ts +82 -0
  378. package/dist/filtering/tool-filter.d.ts.map +1 -0
  379. package/dist/{http-VSB7DBJR.js → http-HWTUVFIA.js} +587 -358
  380. package/dist/index.d.ts +9 -842
  381. package/dist/index.d.ts.map +1 -0
  382. package/dist/index.js +4 -5
  383. package/dist/server/mcp-server.d.ts +66 -0
  384. package/dist/server/mcp-server.d.ts.map +1 -0
  385. package/dist/server/registration/audit-tools.d.ts +13 -0
  386. package/dist/server/registration/audit-tools.d.ts.map +1 -0
  387. package/dist/server/registration/built-in-tools.d.ts +8 -0
  388. package/dist/server/registration/built-in-tools.d.ts.map +1 -0
  389. package/dist/server/registration/help-resources.d.ts +7 -0
  390. package/dist/server/registration/help-resources.d.ts.map +1 -0
  391. package/dist/server/registration/index.d.ts +4 -0
  392. package/dist/server/registration/index.d.ts.map +1 -0
  393. package/dist/{sqlite-26V3Y4MK.js → sqlite-6C3AJI4I.js} +115 -147
  394. package/dist/{sqlite-native-5O7FZJGB.js → sqlite-native-ZSSWCTYC.js} +1016 -349
  395. package/dist/transports/http/index.d.ts +8 -0
  396. package/dist/transports/http/index.d.ts.map +1 -0
  397. package/dist/transports/http/middleware.d.ts +25 -0
  398. package/dist/transports/http/middleware.d.ts.map +1 -0
  399. package/dist/transports/http/oauth.d.ts +24 -0
  400. package/dist/transports/http/oauth.d.ts.map +1 -0
  401. package/dist/transports/http/session.d.ts +2 -0
  402. package/dist/transports/http/session.d.ts.map +1 -0
  403. package/dist/transports/http/sessions/index.d.ts +4 -0
  404. package/dist/transports/http/sessions/index.d.ts.map +1 -0
  405. package/dist/transports/http/sessions/legacy-sse.d.ts +6 -0
  406. package/dist/transports/http/sessions/legacy-sse.d.ts.map +1 -0
  407. package/dist/transports/http/sessions/mutex.d.ts +6 -0
  408. package/dist/transports/http/sessions/mutex.d.ts.map +1 -0
  409. package/dist/transports/http/sessions/stateful.d.ts +12 -0
  410. package/dist/transports/http/sessions/stateful.d.ts.map +1 -0
  411. package/dist/transports/http/sessions/stateless.d.ts +6 -0
  412. package/dist/transports/http/sessions/stateless.d.ts.map +1 -0
  413. package/dist/transports/http/transport.d.ts +65 -0
  414. package/dist/transports/http/transport.d.ts.map +1 -0
  415. package/dist/transports/http/type-adapters.d.ts +20 -0
  416. package/dist/transports/http/type-adapters.d.ts.map +1 -0
  417. package/dist/transports/http/types.d.ts +120 -0
  418. package/dist/transports/http/types.d.ts.map +1 -0
  419. package/dist/types/adapter.d.ts +138 -0
  420. package/dist/types/adapter.d.ts.map +1 -0
  421. package/dist/types/auth.d.ts +79 -0
  422. package/dist/types/auth.d.ts.map +1 -0
  423. package/dist/types/database.d.ts +100 -0
  424. package/dist/types/database.d.ts.map +1 -0
  425. package/dist/types/filtering.d.ts +41 -0
  426. package/dist/types/filtering.d.ts.map +1 -0
  427. package/dist/types/index.d.ts +13 -0
  428. package/dist/types/index.d.ts.map +1 -0
  429. package/dist/types/server.d.ts +48 -0
  430. package/dist/types/server.d.ts.map +1 -0
  431. package/dist/utils/annotations.d.ts +62 -0
  432. package/dist/utils/annotations.d.ts.map +1 -0
  433. package/dist/utils/errors/base.d.ts +33 -0
  434. package/dist/utils/errors/base.d.ts.map +1 -0
  435. package/dist/utils/errors/categories.d.ts +41 -0
  436. package/dist/utils/errors/categories.d.ts.map +1 -0
  437. package/dist/utils/errors/classes.d.ts +124 -0
  438. package/dist/utils/errors/classes.d.ts.map +1 -0
  439. package/dist/utils/errors/error-response-fields.d.ts +24 -0
  440. package/dist/utils/errors/error-response-fields.d.ts.map +1 -0
  441. package/dist/utils/errors/format.d.ts +74 -0
  442. package/dist/utils/errors/format.d.ts.map +1 -0
  443. package/dist/utils/errors/index.d.ts +11 -0
  444. package/dist/utils/errors/index.d.ts.map +1 -0
  445. package/dist/utils/errors/suggestions.d.ts +16 -0
  446. package/dist/utils/errors/suggestions.d.ts.map +1 -0
  447. package/dist/utils/icons.d.ts +19 -0
  448. package/dist/utils/icons.d.ts.map +1 -0
  449. package/dist/utils/identifiers.d.ts +121 -0
  450. package/dist/utils/identifiers.d.ts.map +1 -0
  451. package/dist/utils/index.d.ts +9 -0
  452. package/dist/utils/index.d.ts.map +1 -0
  453. package/dist/utils/insights-manager.d.ts +39 -0
  454. package/dist/utils/insights-manager.d.ts.map +1 -0
  455. package/dist/utils/logger/error-codes.d.ts +48 -0
  456. package/dist/utils/logger/error-codes.d.ts.map +1 -0
  457. package/dist/utils/logger/index.d.ts +22 -0
  458. package/dist/utils/logger/index.d.ts.map +1 -0
  459. package/dist/utils/logger/logger.d.ts +94 -0
  460. package/dist/utils/logger/logger.d.ts.map +1 -0
  461. package/dist/utils/logger/module-logger.d.ts +28 -0
  462. package/dist/utils/logger/module-logger.d.ts.map +1 -0
  463. package/dist/utils/logger/types.d.ts +36 -0
  464. package/dist/utils/logger/types.d.ts.map +1 -0
  465. package/dist/utils/progress-utils.d.ts +54 -0
  466. package/dist/utils/progress-utils.d.ts.map +1 -0
  467. package/dist/utils/redaction.d.ts +4 -0
  468. package/dist/utils/redaction.d.ts.map +1 -0
  469. package/dist/utils/resource-annotations.d.ts +36 -0
  470. package/dist/utils/resource-annotations.d.ts.map +1 -0
  471. package/dist/utils/validate-json-path.d.ts +43 -0
  472. package/dist/utils/validate-json-path.d.ts.map +1 -0
  473. package/dist/utils/validate-path.d.ts +37 -0
  474. package/dist/utils/validate-path.d.ts.map +1 -0
  475. package/dist/utils/where-clause.d.ts +42 -0
  476. package/dist/utils/where-clause.d.ts.map +1 -0
  477. package/dist/version.d.ts +10 -0
  478. package/dist/version.d.ts.map +1 -0
  479. package/package.json +33 -20
  480. package/.gitattributes +0 -2
  481. package/dist/chunk-4IA3DB5C.js +0 -135
  482. package/dist/chunk-FW7UCRLN.js +0 -82
  483. package/dist/chunk-RHVEZ42P.js +0 -873
  484. package/dist/chunk-Z2GFQU3G.js +0 -363
  485. package/dist/worker-script.d.ts +0 -2
  486. package/dist/worker-script.js +0 -126
  487. package/playwright.config.ts +0 -101
  488. package/scripts/generate-server-instructions.ts +0 -111
  489. package/server.json +0 -52
  490. package/test-server/README.md +0 -118
  491. package/test-server/code-map.md +0 -409
  492. package/test-server/fixtures/sample.csv +0 -6
  493. package/test-server/reset-database.ps1 +0 -373
  494. package/test-server/sample.csv +0 -11
  495. package/test-server/test-agent-experience.md +0 -243
  496. package/test-server/test-database.sql +0 -388
  497. package/test-server/test-group-tools.md +0 -861
  498. package/test-server/test-help-resources.mjs +0 -238
  499. package/test-server/test-preflight.md +0 -53
  500. package/test-server/test-prompts.md +0 -354
  501. package/test-server/test-resources.md +0 -245
  502. package/test-server/test-tool-annotations.mjs +0 -157
  503. package/test-server/test-tools-advanced-1.md +0 -517
  504. package/test-server/test-tools-advanced-2.md +0 -487
  505. package/test-server/test-tools-codemode.md +0 -629
  506. package/test-server/test-tools.md +0 -176
  507. package/test-server/tool-reference.md +0 -236
  508. package/tsconfig.test.json +0 -9
@@ -0,0 +1,2624 @@
1
+ import { VERSION, NAME, READ_ONLY, SERVER_ICONS, ASSISTANT_FOCUSED, redactObject, getRequiredScope, AuditListBackupsOutputSchema, AuditListBackupsSchema, AuditGetBackupOutputSchema, AuditCleanupOutputSchema, AuditDiffBackupOutputSchema, AuditRestoreBackupOutputSchema } from './chunk-E5IESRTK.js';
2
+ import { parseToolFilter, getToolFilterFromEnv, logger, getFilterSummary, DbMcpError, getAuthContext, scopesGrantToolAccess, registerToolScopes, InsufficientScopeError, sanitizeErrorMessage, formatHandlerError } from './chunk-L552U3QS.js';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { open, stat, mkdir, rename, readdir, unlink, mkdtemp, writeFile, rmdir, readFile, appendFile } from 'fs/promises';
6
+ import { dirname, join, basename, sep } from 'path';
7
+ import { gzip, gunzipSync } from 'zlib';
8
+ import { promisify } from 'util';
9
+ import { performance } from 'perf_hooks';
10
+ import { z } from 'zod';
11
+
12
+ // src/constants/server-instructions.ts
13
+ var INSTRUCTIONS = `# db-mcp (SQLite MCP Server)
14
+
15
+ ## Quick Access
16
+
17
+ | Purpose | Action |
18
+ | --------------- | -------------------------- |
19
+ | Health check | \`server_health\` tool |
20
+ | Server info | \`server_info\` tool |
21
+ | Database schema | \`sqlite://schema\` resource |
22
+ | Tool help | \`sqlite://help\` resource |
23
+
24
+ ## Built-in Tools
25
+
26
+ \`server_info\`, \`server_health\`, \`list_adapters\`, \`sqlite_execute_code\` \u2014 injected into all configurations.
27
+
28
+ ## Help Resources
29
+
30
+ Read \`sqlite://help\` for gotchas and critical usage patterns.
31
+ Read \`sqlite://help/{group}\` for group-specific tool reference (core, json, text, stats, vector, geo, admin, transactions, introspection, migration).
32
+ Only help resources for your enabled tool groups are registered.`;
33
+ var HELP_CONTENT = /* @__PURE__ */ new Map([
34
+ ["admin", `# db-mcp Help \u2014 Database Administration (32N/31W tools) + Server Audit (5 tools)
35
+
36
+ ## Maintenance
37
+
38
+ \`\`\`javascript
39
+ sqlite_integrity_check({ maxErrors: 10 }); // check for corruption
40
+ sqlite_optimize({ analyze: true, reindex: true }); // optimize performance
41
+ sqlite_vacuum(); // reclaim space
42
+ sqlite_analyze({ table: "orders" }); // update statistics for query planner
43
+ sqlite_dbstat({ summarize: true }); // storage stats (\u26A0\uFE0F summarize native-only; WASM returns counts only)
44
+ sqlite_reindex(); // rebuild all indexes
45
+ sqlite_reindex({ target: "idx_users_email" }); // rebuild specific index
46
+ sqlite_reindex({ target: "orders" }); // rebuild all indexes for a table
47
+ \`\`\`
48
+
49
+ ## Backup/Restore (Native only)
50
+
51
+ \`\`\`javascript
52
+ sqlite_backup({ targetPath: "/path/to/backup.db" });
53
+ sqlite_vacuum_into({ outputPath: "/path/to/compact.db" }); // create defragmented copy
54
+ sqlite_dump({ outputPath: "/path/to/dump.sql" }); // export SQL text dump
55
+ sqlite_verify_backup({ backupPath: "/path/to/backup.db" }); // check integrity without restoring
56
+ sqlite_restore({ sourcePath: "/path/to/backup.db" }); // \u26A0\uFE0F WARNING: Replaces current database
57
+ \`\`\`
58
+
59
+ ## Audit Backups
60
+
61
+ \`\`\`javascript
62
+ sqlite_audit_list_backups({ limit: 10, offset: 0 }); // list pre-mutation DDL snapshots
63
+ sqlite_audit_get_backup({ filename: "snapshot_123.json" }); // retrieve specific snapshot
64
+ sqlite_audit_diff_backup({ filename: "snapshot_123.json" }); // compare snapshot against live schema
65
+ sqlite_audit_restore_backup({ filename: "snapshot_123.json", dryRun: true }); // restore schema from snapshot
66
+ sqlite_audit_cleanup(); // apply retention policy and delete old snapshots
67
+ \`\`\`
68
+
69
+ ## PRAGMA
70
+
71
+ \`\`\`javascript
72
+ sqlite_pragma_settings({ pragma: "journal_mode" }); // get value
73
+ sqlite_pragma_settings({ pragma: "cache_size", value: 10000 }); // set value
74
+ sqlite_pragma_table_info({ table: "users" }); // column details
75
+ sqlite_pragma_compile_options({ filter: "FTS" }); // \u26A0\uFE0F WASM may show FTS3, not FTS5
76
+ sqlite_pragma_database_list(); // list attached databases
77
+ sqlite_attach_database({ filepath: "/path/to/other.db", alias: "archive" }); // attach external DB
78
+ sqlite_detach_database({ alias: "archive" }); // detach DB
79
+ sqlite_pragma_optimize(); // run PRAGMA optimize
80
+ \`\`\`
81
+
82
+ ## WAL Management
83
+
84
+ \`\`\`javascript
85
+ sqlite_wal({ action: "status" }); // check current journal mode
86
+ sqlite_wal({ action: "enable" }); // switch to WAL mode
87
+ sqlite_wal({ action: "disable" }); // switch back to DELETE journal mode
88
+ sqlite_wal({ action: "checkpoint" }); // run default checkpoint
89
+ sqlite_wal({ action: "checkpoint", checkpointMode: "TRUNCATE" }); // checkpoint and truncate WAL file
90
+ \`\`\`
91
+
92
+ ## Index & Stats
93
+
94
+ - \`sqlite_index_stats({ table: "orders" })\` \u2014 stats for explicit indexes
95
+
96
+ ## Views
97
+
98
+ \`\`\`javascript
99
+ sqlite_create_view({
100
+ viewName: "active_orders",
101
+ selectQuery: "SELECT * FROM orders WHERE status = 'active'",
102
+ });
103
+ sqlite_create_view({ viewName: "v", selectQuery: "...", replace: true }); // CREATE OR REPLACE
104
+ sqlite_list_views(); // list all views
105
+ sqlite_drop_view({ viewName: "active_orders" });
106
+ \`\`\`
107
+
108
+ ## Virtual Tables
109
+
110
+ \`\`\`javascript
111
+ sqlite_list_virtual_tables(); // list FTS5, R-Tree, CSV tables
112
+ sqlite_virtual_table_info({ tableName: "articles_fts" }); // module and column info
113
+ sqlite_drop_virtual_table({ tableName: "old_fts", ifExists: true });
114
+ \`\`\`
115
+
116
+ ## Generate Series (pure JS)
117
+
118
+ \`\`\`javascript
119
+ sqlite_generate_series({ start: 1, stop: 100, step: 5 }); // returns array of values
120
+ // \u26A0\uFE0F Creates a REGULAR table (not virtual) \u2014 use sqlite_drop_table to remove
121
+ sqlite_create_series_table({ tableName: "numbers", start: 1, stop: 1000 });
122
+ \`\`\`
123
+
124
+ ## R-Tree (Native only)
125
+
126
+ \`\`\`javascript
127
+ sqlite_create_rtree_table({ tableName: "locations_idx", dimensions: 2 }); // 2D: minX, maxX, minY, maxY
128
+ // Returns graceful error with wasmLimitation: true in WASM
129
+ \`\`\`
130
+
131
+ ## CSV Virtual Tables (Native only)
132
+
133
+ \u26A0\uFE0F Requires ABSOLUTE file paths
134
+
135
+ \`\`\`javascript
136
+ sqlite_analyze_csv_schema({ filePath: "/absolute/path/to/data.csv" }); // analyze CSV structure
137
+ sqlite_create_csv_table({
138
+ tableName: "csv_data",
139
+ filePath: "/absolute/path/to/data.csv",
140
+ });
141
+ \`\`\`
142
+
143
+ ## Business Insights
144
+
145
+ \`\`\`javascript
146
+ sqlite_append_insight({ insight: "Q4 revenue increased 23% YoY" }); // add to memo://insights
147
+ \`\`\``],
148
+ ["core", `# db-mcp Help \u2014 Core Operations (21 tools)
149
+
150
+ ## Basic Queries
151
+
152
+ - \`sqlite_read_query({ query: "SELECT * FROM users LIMIT 10" })\` \u2014 execute SELECT, PRAGMA, EXPLAIN, or WITH statements
153
+ - \`sqlite_write_query({ query: "INSERT INTO users (name) VALUES ('Alice')" })\` \u2014 execute INSERT, UPDATE, DELETE, REPLACE, or trigger DDL (CREATE/DROP TRIGGER)
154
+
155
+ ## Tables & Schema
156
+
157
+ - \`sqlite_list_tables({ excludeSystemTables?: boolean })\` \u2014 list all tables in the database (system tables excluded by default)
158
+ - \`sqlite_describe_table({ table: "users" })\` \u2014 get detailed schema, columns, and foreign keys for a specific table. Detects generated columns (VIRTUAL/STORED) with expression.
159
+ - \`sqlite_create_table({ table: "users", columns: [{ name: "id", type: "INTEGER PRIMARY KEY" }, { name: "email", type: "TEXT UNIQUE" }], foreignKeys: [{ column: "role_id", targetTable: "roles" }], checkConstraints: ["price > 0"] })\` \u2014 create a new table with optional table-level constraints. Use \`strict: true\` for STRICT mode (SQLite 3.37+) to enforce column type checking.
160
+ - \`sqlite_drop_table({ table: "users", ifExists?: true })\` \u2014 drop an existing table
161
+ - \`sqlite_alter_table({ table: "users", operation: "add_column", column: "age", type: "INTEGER", nullable: true })\` \u2014 add, rename, or drop columns, or rename a table. Operations: \`add_column\`, \`rename_column\`, \`drop_column\`, \`rename_table\`.
162
+ - \`sqlite_list_triggers({ table?: "users" })\` \u2014 list database triggers, optionally filtered by table
163
+ - \`sqlite_create_trigger({ name: "trg_updated", table: "users", timing: "AFTER", event: "UPDATE", body: "UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id" })\` \u2014 create a trigger with BEFORE/AFTER/INSTEAD OF timing, optional column-specific UPDATE triggers and WHEN conditions
164
+ - \`sqlite_drop_trigger({ name: "trg_updated" })\` \u2014 drop a database trigger
165
+ - \`sqlite_list_constraints({ table: "users" })\` \u2014 list primary key, foreign key, unique, and check constraints for a table
166
+
167
+ ## Indexes
168
+
169
+ - \`sqlite_get_indexes({ table?: "users", excludeSystemIndexes?: true })\` \u2014 list indexes (optionally filtered by table)
170
+ - \`sqlite_create_index({ indexName: "idx_users_email", table: "users", columns: ["email"], unique?: true })\` \u2014 create a new index
171
+ - \`sqlite_drop_index({ indexName: "idx_users_email", ifExists?: true })\` \u2014 drop an existing index
172
+
173
+ ## Convenience Tools (High-Level Data Operations)
174
+
175
+ - \`sqlite_upsert({ table: "users", data: { id: 1, name: "Alice" }, conflictColumns: ["id"], updateColumns: ["name"], returning: true })\` \u2014 insert or update a row using \`ON CONFLICT\` (or \`REPLACE\` fallback). Supports \`returning: true\` or array of columns.
176
+ - \`sqlite_batch_insert({ table: "users", rows: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }], returning: true })\` \u2014 insert multiple rows in a single batch. Supports \`returning: true\` or array of columns.
177
+ - \`sqlite_count({ table: "users", where?: "status = 'active'" })\` \u2014 count rows in a table (faster than a full query)
178
+ - \`sqlite_exists({ table: "users", where: "email = 'test@example.com'" })\` \u2014 check if a row exists (stops at first match)
179
+ - \`sqlite_truncate({ table: "users" })\` \u2014 quickly delete all rows from a table (executes \`DELETE FROM table\`)
180
+ - \`sqlite_date_add({ table: "users", column: "created_at", amount: 7, unit: "days", whereClause: "id = 1" })\` \u2014 add or subtract time intervals from a date column. By default returns only the computed column; use \`selectColumns\` to return additional context.
181
+ - \`sqlite_date_diff({ table: "users", column1: "ended_at", column2: "started_at", unit: "days", whereClause: "id = 1" })\` \u2014 calculate the difference between two date columns. By default returns only the computed column; use \`selectColumns\` to return additional context.`],
182
+ ["geo", `# db-mcp Help \u2014 Geospatial Operations (11N/4W: 4 basic + 7 SpatiaLite [NATIVE ONLY])
183
+
184
+ ## Basic Geo (always available \u2014 Haversine formula)
185
+
186
+ \`\`\`javascript
187
+ sqlite_geo_distance({
188
+ lat1: 40.7128,
189
+ lon1: -74.006,
190
+ lat2: 34.0522,
191
+ lon2: -118.2437,
192
+ }); // returns km
193
+ sqlite_geo_bounding_box({
194
+ table: "stores",
195
+ latColumn: "lat",
196
+ lonColumn: "lon",
197
+ minLat: 40,
198
+ maxLat: 41,
199
+ minLon: -75,
200
+ maxLon: -73,
201
+ });
202
+ sqlite_geo_nearby({
203
+ table: "stores",
204
+ latColumn: "lat",
205
+ lonColumn: "lon",
206
+ centerLat: 40.7,
207
+ centerLon: -74,
208
+ radius: 10,
209
+ unit: "km",
210
+ });
211
+ sqlite_geo_cluster({
212
+ table: "customers",
213
+ latColumn: "lat",
214
+ lonColumn: "lon",
215
+ gridSize: 0.1,
216
+ });
217
+ \`\`\`
218
+
219
+ ## SpatiaLite (7 tools, Native only)
220
+
221
+ \`\`\`javascript
222
+ // Load extension first
223
+ sqlite_spatialite_load(); // \u26A0\uFE0F Required before using other spatial tools
224
+
225
+ // Create spatial table with geometry column
226
+ sqlite_spatialite_create_table({
227
+ tableName: "places",
228
+ geometryColumn: "geom",
229
+ geometryType: "POINT",
230
+ srid: 4326,
231
+ });
232
+
233
+ // Import data (WKT or GeoJSON)
234
+ sqlite_spatialite_import({
235
+ tableName: "places",
236
+ format: "wkt",
237
+ data: "POINT(-73.99 40.75)",
238
+ additionalData: { name: "NYC" },
239
+ });
240
+ sqlite_spatialite_import({
241
+ tableName: "places",
242
+ format: "geojson",
243
+ data: '{"type":"Point","coordinates":[-73.99,40.75]}',
244
+ });
245
+
246
+ // Spatial queries (SELECT only)
247
+ sqlite_spatialite_query({
248
+ query: "SELECT name, AsText(geom) FROM places WHERE ST_Within(geom, ...)",
249
+ });
250
+
251
+ // Spatial analysis
252
+ // analysisType: "spatial_extent" | "point_in_polygon" | "nearest_neighbor" | "distance_matrix"
253
+ // \u26A0\uFE0F nearest_neighbor/distance_matrix return CARTESIAN distance (degrees), not geodetic (km/miles)
254
+ // For same source/target table, use excludeSelf: true to avoid self-matches
255
+ sqlite_spatialite_analyze({
256
+ analysisType: "spatial_extent",
257
+ sourceTable: "places",
258
+ geometryColumn: "geom",
259
+ });
260
+ sqlite_spatialite_analyze({
261
+ analysisType: "nearest_neighbor",
262
+ sourceTable: "pts",
263
+ targetTable: "pts",
264
+ excludeSelf: true,
265
+ });
266
+
267
+ // Geometry transforms
268
+ // buffer: 'distance' = radius; simplify: 'distance' = tolerance (0.0001 for lat/lon)
269
+ // \u26A0\uFE0F Buffer auto-simplifies output by default (tolerance=0.0001). Use simplifyTolerance: 0 to disable
270
+ sqlite_spatialite_transform({
271
+ operation: "buffer",
272
+ geometry1: "POINT(-73.99 40.75)",
273
+ distance: 0.01,
274
+ });
275
+ sqlite_spatialite_transform({
276
+ operation: "simplify",
277
+ geometry1: "...",
278
+ distance: 0.001,
279
+ });
280
+
281
+ // Spatial index (R-Tree)
282
+ sqlite_spatialite_index({
283
+ tableName: "places",
284
+ geometryColumn: "geom",
285
+ action: "create",
286
+ }); // create, drop, or check
287
+ \`\`\``],
288
+ ["gotchas", `# db-mcp Help \u2014 Gotchas & Code Mode
289
+
290
+ ## \u26A0\uFE0F Critical Gotchas
291
+
292
+ 1. **sqlite_write_query**: DML only (INSERT/UPDATE/DELETE/REPLACE) \u2014 use \`sqlite_read_query\` for SELECT, and dedicated DDL tools like \`sqlite_create_table\`, \`sqlite_create_trigger\`, and \`sqlite_drop_trigger\` for schema modifications.
293
+ 2. **Regex patterns**: Double-escape backslashes (\`\\\\\\\\\`) when passing through JSON/MCP
294
+ 3. **FTS5 virtual tables**: \`*_fts\` and shadow tables \`*_fts_*\` are hidden from \`sqlite_list_tables\` for cleaner output
295
+ 4. **FTS5 boolean logic**: Uses AND by default \u2014 \`"machine learning"\` = rows with BOTH words. Use OR explicitly: \`"machine OR learning"\`
296
+ 5. **json_each row multiplication**: Expands arrays to rows \u2014 use \`limit\` param for large arrays
297
+ 6. **json_group_object without groupByColumn**: Each row creates a key-value pair; duplicate keys result if key values aren't unique
298
+ 7. **allowExpressions**: For column extraction ONLY (e.g., \`json_extract\`), NOT aggregate functions \u2014 use \`aggregateFunction\` param instead
299
+ 8. **sqlite_json_normalize_column**: Defaults to \`preserve\` (maintains original format); use \`outputFormat: 'text'\` to force text
300
+ 9. **Fuzzy matching tokenization**: Matches WORD TOKENS by default \u2014 \`"laptop"\` matches \`"Laptop Pro 15"\` (distance 0 on first token). Use \`tokenize: false\` for full-string matching
301
+ 10. **SpatiaLite distances**: \`nearest_neighbor\`/\`distance_matrix\` return CARTESIAN distance (degrees), not geodetic (km/miles)
302
+ 11. **SpatiaLite buffer**: Auto-simplifies output by default (tolerance=0.0001). Use \`simplifyTolerance: 0\` to disable
303
+ 12. **sqlite_stats_top_n**: Returns all columns by default which creates large payloads for wide tables \u2014 always pass \`selectColumns\` to control output size
304
+ 13. **CSV virtual tables**: Require ABSOLUTE file paths
305
+ 14. **sqlite_create_series_table**: Creates a REGULAR table (not virtual) \u2014 use \`sqlite_drop_table\` to remove
306
+ 15. **sqlite_dbstat**: \`summarize\` only works in native; WASM returns counts only
307
+ 16. **PRAGMA compile options**: WASM may show FTS3, not FTS5
308
+ 17. **Vector tool schemas**: Vector tools use distinct schemas for specific operations. E.g., \`sqlite.vector.dimensions\` requires \`vectorColumn\`. Additionally, \`sqlite.vector.get\` wraps metadata inside a \`metadata\` object (e.g., \`metadata.content\`), and \`sqlite.vector.stats\` returns \`sampleSize\` and \`magnitudeStats\` (not \`count\` and \`stats\`).
309
+ 18. **FTS5 trigger cleanup**: Dropping an FTS5 table with \`sqlite_drop_table\` automatically finds and removes the associated \`_ai\`, \`_ad\`, and \`_au\` sync triggers from the source table.
310
+ 19. **sqlite_batch_insert**: All rows must have the same keys \u2014 inconsistent column sets across rows will cause errors or unexpected NULLs
311
+ 20. **sqlite_schema_diff**: \`baseline\` and \`target\` accept either the string \`"current"\` (queries live DB) or an inline snapshot object from a prior \`sqlite_schema_snapshot\` call. At least one side must be \`"current"\` unless doing an offline comparison
312
+ 21. **sqlite_upsert**: Always specify \`conflictColumns\` \u2014 without it, falls back to \`REPLACE\` which deletes and re-inserts the row, potentially losing columns not included in \`data\`
313
+
314
+ ## WASM vs Native
315
+
316
+ | Feature | Native | WASM | Fallback |
317
+ | ------------------------------------------------- | --------------------- | ----------- | ---------------- |
318
+ | FTS5 full-text search | \u2705 | \u274C | None |
319
+ | Transactions (8 tools) | \u2705 | \u274C | None |
320
+ | Window functions (6 tools in stats group) | \u2705 | \u274C | None |
321
+ | SpatiaLite GIS (7 tools; 4 basic geo always work) | \u2705 | \u274C | None |
322
+ | Backup/Restore/Dump/VacuumInto/Verify (5 tools) | \u2705 | \u274C | Graceful error |
323
+ | R-Tree spatial indexing | \u2705 | \u274C | Graceful error |
324
+ | CSV virtual tables | \u2705 | \u274C | Graceful error |
325
+ | generate_series | JS fallback | JS fallback | \u2014 |
326
+ | dbstat | \u2705 native (per-table) | \u274C | JS (counts only) |
327
+ | soundex() | \u2705 native | \u274C | JS |
328
+
329
+ ## Code Mode (Built-in)
330
+
331
+ | Tool | Description |
332
+ | --------------------- | ----------------------------------------------------------------- |
333
+ | \`sqlite_execute_code\` | Execute JavaScript in a sandboxed environment with \`sqlite.*\` API |
334
+
335
+ **Usage**: \`sqlite_execute_code({ code: "const tables = await sqlite.core.listTables(); return tables;" })\`
336
+ **Discover**: \`sqlite.help()\` for all groups, \`sqlite.<group>.help()\` for methods.
337
+ **Progress**: Use \`await sqlite.reportProgress(current, total, "Message")\` for custom long-running tasks.
338
+ **Groups**: \`sqlite.core\`, \`sqlite.json\`, \`sqlite.text\`, \`sqlite.stats\`, \`sqlite.vector\`, \`sqlite.admin\`, \`sqlite.transactions\` (Native-only), \`sqlite.geo\`, \`sqlite.introspection\`, \`sqlite.migration\`
339
+
340
+ > **Note**: Code Mode dynamically filters capabilities. In WASM environments, \`sqlite.help()\` will omit unsupported groups (e.g., \`transactions\`) and tools (e.g., FTS5) to accurately reflect the active runtime.
341
+
342
+ ## Code Mode API Mapping
343
+
344
+ \`sqlite_group_action\` \u2192 \`sqlite.group.action()\` (group prefixes dropped: \`sqlite_json_insert\` \u2192 \`sqlite.json.insert()\`)
345
+ **Exception**: \`stats\` and \`migration\` keep their prefix: \`sqlite_stats_basic\` \u2192 \`sqlite.stats.statsBasic()\`, \`sqlite_migration_apply\` \u2192 \`sqlite.migration.migrationApply()\`
346
+
347
+ **Positional args work**: \`sqlite.core.readQuery("SELECT...")\`, \`sqlite.json.insert("docs", "data", {...})\`
348
+
349
+ **Discovery**: \`sqlite.help()\` returns all groups and methods. \`sqlite.core.help()\`, \`sqlite.json.help()\` for group-specific methods.`],
350
+ ["introspection", `# db-mcp Help \u2014 Schema Introspection (10 tools)
351
+
352
+ All introspection tools are **read-only** \u2014 they query PRAGMAs and sqlite_master, never modify data.
353
+
354
+ ## Graph Analysis (3 tools)
355
+
356
+ \`\`\`javascript
357
+ // Build FK dependency graph \u2014 nodesOnly: true for lightweight response (nodes without edges)
358
+ sqlite_dependency_graph({ includeRowCounts: true, excludeSystemTables: true });
359
+ sqlite_dependency_graph({ nodesOnly: true }); // lighter payload
360
+
361
+ // Safe DDL execution order \u2014 "create" = parents first (default), "drop" = children first
362
+ sqlite_topological_sort({ direction: "create", excludeSystemTables: true });
363
+ sqlite_topological_sort({ direction: "drop" }); // safe DROP order
364
+
365
+ // Simulate DELETE/DROP/TRUNCATE impact \u2014 shows affected tables, cascade paths, severity scoring
366
+ sqlite_cascade_simulator({ table: "users", operation: "DELETE" });
367
+ sqlite_cascade_simulator({ table: "users", operation: "DROP", compact: true }); // compact omits path arrays
368
+ \`\`\`
369
+
370
+ ## Schema Analysis (4 tools)
371
+
372
+ \`\`\`javascript
373
+ // Full schema in one call \u2014 compact: true omits column details, sections limits scope
374
+ sqlite_schema_snapshot({
375
+ sections: ["tables", "indexes"],
376
+ excludeSystemTables: true,
377
+ });
378
+ sqlite_schema_snapshot({ compact: true }); // lighter payload
379
+
380
+ // Compare a saved snapshot against the current live schema \u2014 structured drift analysis
381
+ sqlite_schema_diff({
382
+ baseline: previousSnapshot, // from an earlier sqlite_schema_snapshot call
383
+ target: "current",
384
+ });
385
+ // Compare specific sections only
386
+ sqlite_schema_diff({
387
+ baseline: "current",
388
+ target: modifiedSnapshot,
389
+ sections: ["tables", "indexes"],
390
+ });
391
+ // Both sides inline (offline diff, no DB queries)
392
+ sqlite_schema_diff({ baseline: snapshotA, target: snapshotB });
393
+
394
+ // Find constraint health issues \u2014 missing PKs, nullable FKs, unindexed FKs
395
+ sqlite_constraint_analysis({ excludeSystemTables: true });
396
+ sqlite_constraint_analysis({
397
+ table: "orders",
398
+ checks: ["missing_pk", "unindexed_fk"],
399
+ }); // limit scope
400
+
401
+ // Analyze DDL for SQLite-specific risks \u2014 does NOT execute the statements
402
+ sqlite_migration_risks({
403
+ statements: [
404
+ "ALTER TABLE users ADD COLUMN email TEXT",
405
+ "DROP TABLE old_logs",
406
+ ],
407
+ });
408
+ \`\`\`
409
+
410
+ ## Diagnostics (3 tools)
411
+
412
+ \`\`\`javascript
413
+ // Fragmentation, per-table size breakdown, optimization recommendations
414
+ sqlite_storage_analysis({
415
+ includeTableDetails: true,
416
+ excludeSystemTables: true,
417
+ });
418
+ sqlite_storage_analysis({ limit: 10 }); // top 10 tables only
419
+
420
+ // Audit index effectiveness \u2014 find redundant, missing FK, unindexed large tables
421
+ sqlite_index_audit({ excludeSystemTables: true });
422
+ sqlite_index_audit({ table: "orders", minSeverity: "warning" }); // reduce payload
423
+
424
+ // EXPLAIN QUERY PLAN with scan-type classification and optimization suggestions (SELECT/WITH only)
425
+ sqlite_query_plan({ sql: "SELECT * FROM orders WHERE status = 'active'" });
426
+ \`\`\`
427
+
428
+ ## \u26A0\uFE0F Gotchas
429
+
430
+ - \`excludeSystemTables\` defaults to \`true\` \u2014 SpatiaLite system tables are hidden for cleaner output. Pass \`false\` to include them
431
+ - \`sqlite_migration_risks\` analyzes DDL text statically \u2014 it does NOT execute the statements`],
432
+ ["json", `# db-mcp Help \u2014 JSON Operations (25 tools)
433
+
434
+ ## Collection & CRUD
435
+
436
+ - \`sqlite_create_json_collection({ tableName, indexes: [{ path: "$.type" }, { path: "$.author" }] })\` \u2014 creates table with JSON indexes
437
+ - \`sqlite_json_insert({ table, column, data: { type: "article", title: "Hello", tags: ["news"] } })\` \u2014 insert JSON document
438
+ - \`sqlite_json_select({ table, column, paths? })\` \u2014 select rows, optionally extract specific JSON paths
439
+ - \`sqlite_json_update({ table, column, path, value, whereClause })\` \u2014 update value at JSON path
440
+ - \`sqlite_json_query({ table, column, filterPaths: { "$.type": "article" }, selectPaths: ["$.title"] })\` \u2014 query with path-based filters and projections
441
+
442
+ ## Path Operations
443
+
444
+ - \`sqlite_json_extract({ table, column, path: "$.title" })\` \u2014 extract value at path. Returns null if path doesn't exist
445
+ - \`sqlite_json_set({ table, column, path: "$.views", value: 100, whereClause: "id = 1" })\` \u2014 set value at path (creates if missing)
446
+ - \`sqlite_json_merge({ table, column, mergeData: { featured: true }, whereClause: "id = 1" })\` \u2014 merge object into existing JSON
447
+ - \`sqlite_json_remove({ table, column, path, whereClause })\` \u2014 remove key at path
448
+ - \`sqlite_json_validate_path({ path: "$.store.books[0].title" })\` \u2014 validate JSON path syntax without executing a query
449
+ - \`sqlite_json_type({ table, column, path })\` \u2014 get JSON type (null, true, false, integer, real, text, array, object)
450
+
451
+ ## Array Operations
452
+
453
+ \u26A0\uFE0F \`json_each\` multiplies output rows \u2014 use \`limit\` param for large arrays
454
+
455
+ - \`sqlite_json_array_append({ table, column, path: "$.tags", value: "featured", whereClause: "id = 1" })\` \u2014 append to array
456
+ - \`sqlite_json_array_length({ table, column, path })\` \u2014 get array length
457
+ - \`sqlite_json_each({ table, column, path: "$.tags", limit: 50 })\` \u2014 expand array to rows
458
+
459
+ ## Aggregation & Analysis
460
+
461
+ \`\`\`javascript
462
+ // Regular tables: use column names directly
463
+ sqlite_json_group_array({
464
+ table: "events",
465
+ valueColumn: "user_id",
466
+ groupByColumn: "event_type",
467
+ });
468
+
469
+ // JSON collections: use allowExpressions with json_extract for both columns
470
+ // \u26A0\uFE0F allowExpressions is for column extraction ONLY, NOT aggregate functions
471
+ // \u26A0\uFE0F Without groupByColumn, each row creates a key-value pair; duplicate keys if values aren't unique
472
+ sqlite_json_group_array({
473
+ table: "docs",
474
+ valueColumn: "json_extract(data, '$.author')",
475
+ groupByColumn: "json_extract(data, '$.type')",
476
+ allowExpressions: true,
477
+ });
478
+
479
+ // For aggregate values (COUNT, SUM, AVG), use aggregateFunction parameter instead
480
+ sqlite_json_group_object({
481
+ table: "events",
482
+ keyColumn: "event_type",
483
+ aggregateFunction: "COUNT(*)",
484
+ });
485
+ sqlite_json_group_object({
486
+ table: "orders",
487
+ keyColumn: "status",
488
+ aggregateFunction: "SUM(total)",
489
+ });
490
+ \`\`\`
491
+
492
+ - \`sqlite_json_keys({ table, column, path? })\` \u2014 get distinct keys of JSON objects
493
+ - \`sqlite_json_pretty({ json: "{\\"name\\": \\"Alice\\"}" })\` \u2014 format JSON string with indentation for readability
494
+ - \`sqlite_json_valid({ json: "{\\"name\\": \\"Alice\\"}" })\` \u2014 check if a string is valid JSON
495
+ - \`sqlite_json_analyze_schema({ table, column })\` \u2014 infer schema types
496
+ - \`sqlite_json_diff({ table, column, path1, path2 })\` \u2014 compare two JSON paths within the same row
497
+
498
+ ## Security
499
+
500
+ - \`sqlite_json_security_scan({ table, column, sampleSize?, whereClause? })\` \u2014 scan JSON column for sensitive keys (password, token, ssn, etc.), SQL injection patterns, and XSS patterns. Returns \`riskLevel\` (low/medium/high) and issue details. Use larger \`sampleSize\` for thorough scans
501
+
502
+ ## JSONB Optimization (SQLite 3.45+)
503
+
504
+ - \`sqlite_json_storage_info({ table, column })\` \u2014 check text vs JSONB format
505
+ - \`sqlite_jsonb_convert({ table, column })\` \u2014 convert to JSONB for faster queries
506
+ - \`sqlite_json_normalize_column({ table, column, outputFormat? })\` \u2014 normalize JSON (sort keys, compact). \u26A0\uFE0F Defaults to \`"preserve"\` (maintains original format); use \`outputFormat: "text"\` to force text output`],
507
+ ["migration", `# db-mcp Help \u2014 Migration Tracking (6 tools)
508
+
509
+ \u26A0\uFE0F Must call \`sqlite_migration_init()\` before using any other migration tool \u2014 it creates the tracking table.
510
+
511
+ \`\`\`javascript
512
+ // Initialize tracking table (idempotent \u2014 safe to call multiple times)
513
+ sqlite_migration_init();
514
+
515
+ // Apply migration: executes SQL and records atomically. If SQL fails, no record is created
516
+ // SHA-256 dedup \u2014 submitting the same SQL twice is rejected as a duplicate
517
+ sqlite_migration_apply({
518
+ version: "2024-01-15-add-users",
519
+ description: "Create users table",
520
+ sql: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE)",
521
+ rollbackSql: "DROP TABLE users",
522
+ sourceSystem: "agent",
523
+ appliedBy: "claude",
524
+ });
525
+
526
+ // Record externally-applied migration (does NOT execute the SQL)
527
+ sqlite_migration_record({
528
+ version: "2024-01-10-initial",
529
+ description: "Initial schema (pre-existing)",
530
+ sql: "CREATE TABLE orders (...)",
531
+ sourceSystem: "manual",
532
+ });
533
+
534
+ // Rollback by ID or version \u2014 requires rollbackSql to have been recorded
535
+ sqlite_migration_rollback({ version: "2024-01-15-add-users" });
536
+ sqlite_migration_rollback({ id: 3, dryRun: true }); // preview SQL without executing
537
+
538
+ // Query history with filters and pagination
539
+ sqlite_migration_history({ status: "applied", limit: 10 });
540
+ sqlite_migration_history({ sourceSystem: "agent", compact: true }); // compact omits hash and source system
541
+
542
+ // Summary: latest version, counts by status, unique source systems
543
+ sqlite_migration_status();
544
+ \`\`\`
545
+
546
+ ## \u26A0\uFE0F Gotchas
547
+
548
+ - Rollback requires \`rollbackSql\` to have been provided when the migration was recorded/applied
549
+ - Migration group is **opt-in** \u2014 not included by default. Enable via \`--tool-filter\` flag: use \`dev-schema\` (core+introspection+migration+codemode) or \`full\` (all groups)`],
550
+ ["stats", `# db-mcp Help \u2014 Statistical Analysis (23N/17W: 17 core + 6 window [NATIVE ONLY])
551
+
552
+ ## Core Statistics (always available)
553
+
554
+ \`\`\`javascript
555
+ sqlite_stats_basic({ table: "employees", column: "salary" }); // count, sum, avg, min, max
556
+ sqlite_stats_count({ table: "orders", column: "status", distinct: true }); // count rows, optionally distinct
557
+ sqlite_stats_group_by({ table: "orders", column: "total", groupBy: "status" }); // aggregate by group
558
+ sqlite_stats_percentile({
559
+ table: "sales",
560
+ column: "revenue",
561
+ percentiles: [25, 50, 75, 90],
562
+ });
563
+ sqlite_stats_histogram({ table: "products", column: "price", buckets: 10 });
564
+ sqlite_stats_correlation({ table: "data", column1: "x", column2: "y" }); // Pearson coefficient
565
+ sqlite_stats_distinct({ table: "orders", column: "status" }); // distinct values
566
+ sqlite_stats_sample({ table: "orders", sampleSize: 100 }); // random sample
567
+ sqlite_stats_summary({
568
+ table: "data",
569
+ columns: ["price", "quantity", "discount"],
570
+ }); // multi-column summary
571
+ sqlite_stats_frequency({ table: "orders", column: "status" }); // frequency distribution
572
+ sqlite_stats_regression({ table: "data", xColumn: "year", yColumn: "revenue" }); // linear
573
+ sqlite_stats_regression({
574
+ table: "data",
575
+ xColumn: "year",
576
+ yColumn: "revenue",
577
+ degree: 2,
578
+ }); // quadratic
579
+ sqlite_stats_outliers({ table: "sales", column: "amount", method: "iqr" }); // or "zscore". Use maxOutliers to limit payload
580
+ sqlite_stats_hypothesis({
581
+ table: "samples",
582
+ column: "value",
583
+ testType: "ttest_one",
584
+ expectedMean: 100,
585
+ });
586
+ \`\`\`
587
+
588
+ \u26A0\uFE0F \`sqlite_stats_top_n\` \u2014 always use \`selectColumns\` to avoid returning all columns (large payloads with text fields):
589
+
590
+ \`\`\`javascript
591
+ sqlite_stats_top_n({
592
+ table: "products",
593
+ column: "price",
594
+ n: 10,
595
+ selectColumns: ["id", "name", "price"],
596
+ });
597
+ \`\`\`
598
+
599
+ ## Anomaly Detection (3 tools)
600
+
601
+ \`\`\`javascript
602
+ // Data distribution anomaly detection \u2014 z-score analysis on numeric columns
603
+ sqlite_stats_detect_anomalies({ table: "sales", threshold: 2.0 }); // auto-detects numeric columns
604
+ sqlite_stats_detect_anomalies({
605
+ table: "metrics",
606
+ columns: ["latency_ms", "error_rate"],
607
+ threshold: 3.0,
608
+ limit: 20,
609
+ });
610
+
611
+ // Fragmentation/bloat risk scoring \u2014 PRAGMA + dbstat analysis
612
+ sqlite_stats_detect_bloat(); // riskiest tables (>0 score), default limit 50
613
+ sqlite_stats_detect_bloat({ includeZeroRisk: true, limit: 10 }); // include zero risk, top 10
614
+
615
+ // Schema health risk scoring \u2014 FK indexes, PKs, wide tables
616
+ sqlite_stats_detect_schema_risks(); // riskiest tables (>0 score)
617
+ sqlite_stats_detect_schema_risks({ excludeSystemTables: false }); // include SpatiaLite tables
618
+ \`\`\`
619
+
620
+ ## Window Functions (6 tools, Native only)
621
+
622
+ \`\`\`javascript
623
+ sqlite_window_row_number({
624
+ table: "employees",
625
+ orderBy: "hire_date",
626
+ partitionBy: "department",
627
+ });
628
+ sqlite_window_rank({
629
+ table: "sales",
630
+ orderBy: "revenue DESC",
631
+ partitionBy: "region",
632
+ rankType: "dense_rank",
633
+ });
634
+ sqlite_window_lag_lead({
635
+ table: "sales",
636
+ orderBy: "date",
637
+ column: "revenue",
638
+ partitionBy: "region",
639
+ }); // access previous/next row values
640
+ sqlite_window_running_total({
641
+ table: "transactions",
642
+ column: "amount",
643
+ orderBy: "date",
644
+ });
645
+ sqlite_window_moving_avg({
646
+ table: "stock_prices",
647
+ column: "close_price",
648
+ orderBy: "date",
649
+ windowSize: 7,
650
+ });
651
+ sqlite_window_ntile({
652
+ table: "employees",
653
+ orderBy: "salary DESC",
654
+ buckets: 4,
655
+ partitionBy: "department",
656
+ }); // quartiles
657
+ \`\`\`
658
+
659
+ \u26A0\uFE0F **Payload Sizes**: Window functions retrieve all standard columns by default (up to the \`limit\`). When querying wide tables, this can result in large payloads. It is highly recommended to pass \`selectColumns\` to reduce token usage:
660
+
661
+ \`\`\`javascript
662
+ sqlite_window_moving_avg({
663
+ table: "stock_prices",
664
+ column: "close_price",
665
+ orderBy: "date",
666
+ windowSize: 7,
667
+ selectColumns: ["date"],
668
+ });
669
+ \`\`\``],
670
+ ["text", `# db-mcp Help \u2014 Text Processing & FTS5 (19N/14W: 14 text + 5 FTS5 [NATIVE ONLY])
671
+
672
+ ## Full-Text Search / FTS5 (5 tools, Native only)
673
+
674
+ \`\`\`javascript
675
+ // Create FTS5 table with triggers for auto-sync on future changes
676
+ // (Dropping this FTS table via sqlite_drop_table will automatically clean up these sync triggers)
677
+ sqlite_fts_create({
678
+ tableName: "articles_fts",
679
+ sourceTable: "articles",
680
+ columns: ["title", "content"],
681
+ });
682
+ sqlite_fts_rebuild({ table: "articles_fts" }); // \u26A0\uFE0F Required: populate index with existing data
683
+
684
+ // FTS5 uses AND by default: "machine learning" = rows containing BOTH words
685
+ // Use OR explicitly: "machine OR learning" for rows containing EITHER word
686
+ sqlite_fts_search({
687
+ table: "articles_fts",
688
+ query: "machine learning",
689
+ limit: 10,
690
+ });
691
+ sqlite_fts_match_info({ table: "articles_fts", query: "machine learning" }); // bm25 ranking info
692
+
693
+ // Highlighted snippets from FTS5 search matches (headline/snippet)
694
+ sqlite_fts_headline({ table: "articles_fts", query: "machine learning" });
695
+ // \u2192 results with <b>machine</b> <b>learning</b> highlighted in context
696
+
697
+ // Custom markers and snippet window
698
+ sqlite_fts_headline({
699
+ table: "articles_fts",
700
+ query: "database",
701
+ startSel: "**",
702
+ stopSel: "**",
703
+ snippetWords: 5,
704
+ });
705
+ \`\`\`
706
+
707
+ \u26A0\uFE0F FTS5 virtual tables (\`*_fts\`) and shadow tables (\`*_fts_*\`) are hidden from \`sqlite_list_tables\` for cleaner output
708
+
709
+ ## Text Processing (14 tools)
710
+
711
+ \`\`\`javascript
712
+ // Regex patterns: \u26A0\uFE0F Double-escape backslashes (\\\\) when passing through JSON/MCP
713
+ sqlite_regex_match({
714
+ table: "logs",
715
+ column: "message",
716
+ pattern: "ERROR:\\\\\\\\s+(\\\\\\\\w+)",
717
+ });
718
+ sqlite_regex_extract({
719
+ table: "users",
720
+ column: "email",
721
+ pattern: "@([a-zA-Z0-9.-]+)",
722
+ groupIndex: 1,
723
+ });
724
+
725
+ // Text manipulation
726
+ sqlite_text_split({ table: "users", column: "email", delimiter: "@" }); // split into parts array
727
+ sqlite_text_concat({
728
+ table: "users",
729
+ columns: ["first_name", "last_name"],
730
+ separator: " ",
731
+ });
732
+ sqlite_text_replace({
733
+ table: "docs",
734
+ column: "content",
735
+ searchPattern: "old",
736
+ replaceWith: "new",
737
+ });
738
+ sqlite_text_trim({ table: "users", column: "name" }); // trim whitespace
739
+ sqlite_text_case({ table: "products", column: "name", mode: "upper" }); // or "lower"
740
+ sqlite_text_substring({ table: "codes", column: "value", start: 1, length: 3 }); // extract substring
741
+ sqlite_text_normalize({
742
+ table: "docs",
743
+ column: "content",
744
+ mode: "strip_accents",
745
+ }); // or nfc, nfd, nfkc, nfkd
746
+
747
+ // Validation patterns: email, phone, url, uuid, ipv4, custom
748
+ sqlite_text_validate({ table: "users", column: "email", pattern: "email" });
749
+ sqlite_text_validate({
750
+ table: "data",
751
+ column: "field",
752
+ pattern: "custom",
753
+ customPattern: "^[A-Z]{2}[0-9]{4}$",
754
+ });
755
+
756
+ // Fuzzy matching \u2014 matches WORD TOKENS by default (not entire value)
757
+ // "laptop" matches "Laptop Pro 15" (distance 0 on first token). Use tokenize: false for full-string matching.
758
+ sqlite_fuzzy_match({
759
+ table: "products",
760
+ column: "name",
761
+ search: "laptop",
762
+ maxDistance: 2,
763
+ });
764
+ sqlite_fuzzy_match({
765
+ table: "products",
766
+ column: "name",
767
+ search: "laptob",
768
+ maxDistance: 2,
769
+ tokenize: false,
770
+ }); // full value
771
+
772
+ // Phonetic matching \u2014 finds words that sound alike (matches against any word in value)
773
+ // Use includeRowData: false for lighter payloads when only matching values are needed
774
+ sqlite_phonetic_match({
775
+ table: "products",
776
+ column: "name",
777
+ search: "laptop",
778
+ algorithm: "soundex",
779
+ });
780
+ sqlite_phonetic_match({
781
+ table: "products",
782
+ column: "name",
783
+ search: "laptop",
784
+ includeRowData: false,
785
+ }); // lighter
786
+
787
+ // Advanced search \u2014 combines exact, fuzzy, and phonetic techniques
788
+ // fuzzyThreshold: 0.3-0.4 = loose matching (more results), 0.6-0.8 = strict matching (fewer results)
789
+ sqlite_advanced_search({
790
+ table: "products",
791
+ column: "name",
792
+ searchTerm: "laptop",
793
+ techniques: ["exact", "fuzzy", "phonetic"],
794
+ fuzzyThreshold: 0.4,
795
+ });
796
+
797
+ // Sentiment analysis \u2014 text analysis (can analyze raw text or database columns)
798
+ sqlite_text_sentiment({ text: "This product is amazing and wonderful!" });
799
+ // \u2192 { sentiment: "very_positive", score: 2, confidence: "medium" }
800
+
801
+ sqlite_text_sentiment({ table: "articles", column: "body" }); // analyzes all rows in a column
802
+
803
+ sqlite_text_sentiment({
804
+ text: "Great service but slow delivery",
805
+ returnWords: true,
806
+ });
807
+ // \u2192 { sentiment: "neutral", score: 0, matchedPositive: ["great"], matchedNegative: ["slow"] }
808
+ \`\`\``],
809
+ ["transactions", `# db-mcp Help \u2014 Transactions (8 tools, Native only)
810
+
811
+ ## Atomic Execution (preferred for simple cases)
812
+
813
+ \`\`\`javascript
814
+ sqlite_transaction_execute({
815
+ statements: ["UPDATE a SET x=1", "UPDATE b SET y=2"],
816
+ });
817
+ \`\`\`
818
+
819
+ ## Manual Transaction Control
820
+
821
+ \`\`\`javascript
822
+ sqlite_transaction_begin({ mode: "immediate" }); // or "deferred", "exclusive"
823
+ sqlite_transaction_savepoint({ name: "checkpoint" });
824
+ sqlite_transaction_rollback_to({ name: "checkpoint" });
825
+ sqlite_transaction_release({ name: "checkpoint" });
826
+ sqlite_transaction_commit();
827
+ sqlite_transaction_rollback();
828
+ \`\`\`
829
+
830
+ ## Transaction State (read-only)
831
+
832
+ \`\`\`javascript
833
+ sqlite_transaction_status(); // \u2192 { status: "active" | "none", active: true/false }
834
+ \`\`\`
835
+
836
+ ## \u26A0\uFE0F Gotchas
837
+
838
+ - Transaction tools are **Native only** \u2014 WASM adapter does not support transactions
839
+ - Use \`sqlite_transaction_execute\` for simple multi-statement operations; manual \`begin\`/\`commit\` for complex flows with savepoints
840
+ - \`sqlite_transaction_status\` is read-only and requires only \`read\` scope; all other transaction tools require \`write\` scope`],
841
+ ["vector", `# db-mcp Help \u2014 Vector/Semantic Search (11 tools)
842
+
843
+ \`\`\`javascript
844
+ // Create vector table with metadata columns
845
+ sqlite_vector_create_table({ tableName: "docs", dimensions: 384, additionalColumns: [{ name: "content", type: "TEXT" }] });
846
+
847
+ // Store vectors (single and batch)
848
+ sqlite_vector_store({ table: "docs", idColumn: "id", vectorColumn: "emb", id: 1, vector: [...] });
849
+ sqlite_vector_batch_store({ table: "docs", idColumn: "id", vectorColumn: "emb", items: [{ id: 1, vector: [...] }, { id: 2, vector: [...] }] });
850
+
851
+ // Search vectors (returnColumns omits vector data from results for smaller payloads)
852
+ sqlite_vector_search({ table: "docs", vectorColumn: "emb", queryVector: [...], limit: 10, returnColumns: ["id", "title"] }); // returns { results: [...] } instead of rows
853
+
854
+ // Retrieve and delete vectors
855
+ // Note: sqlite_vector_get returns parsed 'vector' array + additional columns in a 'metadata' object for flexibility
856
+ sqlite_vector_get({ table: "docs", idColumn: "id", vectorColumn: "emb", id: 1 });
857
+ sqlite_vector_delete({ table: "docs", idColumn: "id", ids: [1, 2, 3] });
858
+
859
+ // Vector metadata
860
+ sqlite_vector_count({ table: "docs" }); // or with dimensions filter: { table: "docs", dimensions: 384 }
861
+ sqlite_vector_dimensions({ table: "docs", vectorColumn: "emb" });
862
+ sqlite_vector_stats({ table: "docs", vectorColumn: "emb" }); // returns sampleSize, magnitudeStats (min/max/avg)
863
+
864
+ // Utility tools for preprocessing
865
+ sqlite_vector_normalize({ vector: [3, 4, 0, 0] }); // returns unit vector [0.6, 0.8, 0, 0]
866
+ sqlite_vector_distance({ vector1: [...], vector2: [...], metric: "cosine" }); // returns { value: <number> }
867
+ \`\`\``]
868
+ ]);
869
+ var BUFFER_HIGH_WATER = 50;
870
+ var FLUSH_INTERVAL_MS = 100;
871
+ var DEFAULT_RECENT_COUNT = 50;
872
+ var STDERR_SENTINEL = "stderr";
873
+ var TAIL_READ_BYTES = 65536;
874
+ var AuditLogger = class {
875
+ config;
876
+ buffer = [];
877
+ flushTimer = null;
878
+ activeFlush = null;
879
+ closed = false;
880
+ dirEnsured = false;
881
+ stderrMode;
882
+ constructor(config) {
883
+ this.config = config;
884
+ this.stderrMode = config.logPath.toLowerCase() === STDERR_SENTINEL;
885
+ if (config.enabled) {
886
+ this.flushTimer = setInterval(() => {
887
+ void this.flush();
888
+ }, FLUSH_INTERVAL_MS);
889
+ this.flushTimer.unref();
890
+ }
891
+ }
892
+ /**
893
+ * Eagerly create the log directory and touch the log file on startup.
894
+ * Provides immediate confirmation that audit is configured correctly.
895
+ * Non-throwing — falls back silently if the path is inaccessible.
896
+ */
897
+ async init() {
898
+ if (this.stderrMode || !this.config.enabled) return;
899
+ try {
900
+ await this.ensureDirectory();
901
+ const fh = await open(this.config.logPath, "a");
902
+ await fh.close();
903
+ } catch (err) {
904
+ process.stderr.write(
905
+ `[AUDIT] Failed to initialise log file: ${this.config.logPath}
906
+ `
907
+ );
908
+ throw err;
909
+ }
910
+ }
911
+ /**
912
+ * Append an audit entry to the buffer.
913
+ * Non-blocking — the entry is serialised and queued; the
914
+ * actual file write happens on the next flush cycle.
915
+ */
916
+ log(entry) {
917
+ if (this.closed || !this.config.enabled) return;
918
+ const safeEntry = redactObject(entry, 0, 5);
919
+ this.buffer.push(JSON.stringify(safeEntry));
920
+ if (this.buffer.length >= BUFFER_HIGH_WATER) {
921
+ void this.flush();
922
+ }
923
+ }
924
+ /**
925
+ * Flush the buffer to disk.
926
+ * Safe to call concurrently — serialises via `this.activeFlush` Promise.
927
+ */
928
+ async flush() {
929
+ if (this.activeFlush) {
930
+ await this.activeFlush;
931
+ if (this.buffer.length === 0) return;
932
+ }
933
+ if (this.buffer.length === 0) return;
934
+ const doFlush = async () => {
935
+ await this.rotateIfNeeded();
936
+ const lines = this.buffer;
937
+ this.buffer = [];
938
+ try {
939
+ if (this.stderrMode) {
940
+ process.stderr.write(lines.join("\n") + "\n");
941
+ } else {
942
+ await this.ensureDirectory();
943
+ await appendFile(
944
+ this.config.logPath,
945
+ lines.join("\n") + "\n",
946
+ "utf-8"
947
+ );
948
+ }
949
+ } catch (err) {
950
+ const message = err instanceof Error ? err.message : String(err);
951
+ process.stderr.write(`[AUDIT] Write failed: ${message}
952
+ `);
953
+ this.buffer.unshift(...lines);
954
+ }
955
+ };
956
+ this.activeFlush = doFlush();
957
+ try {
958
+ await this.activeFlush;
959
+ } finally {
960
+ this.activeFlush = null;
961
+ }
962
+ }
963
+ /**
964
+ * Gracefully close the logger — flush remaining entries and stop the timer.
965
+ */
966
+ async close() {
967
+ this.closed = true;
968
+ if (this.flushTimer) {
969
+ clearInterval(this.flushTimer);
970
+ this.flushTimer = null;
971
+ }
972
+ await this.flush();
973
+ }
974
+ /**
975
+ * Read the most recent audit entries from the log file.
976
+ * Uses a streaming tail-read: only the last TAIL_READ_BYTES (64 KB) are
977
+ * read from disk, preventing O(n) memory spikes for large audit logs.
978
+ * Used by the `sqlite://audit` resource.
979
+ *
980
+ * @param count Maximum number of entries to return (default 50)
981
+ */
982
+ async recent(count = DEFAULT_RECENT_COUNT) {
983
+ if (this.stderrMode) return [];
984
+ await this.flush();
985
+ try {
986
+ let fh;
987
+ try {
988
+ fh = await open(this.config.logPath, "r");
989
+ } catch {
990
+ return [];
991
+ }
992
+ try {
993
+ const info = await stat(this.config.logPath);
994
+ const fileSize = info.size;
995
+ if (fileSize === 0) return [];
996
+ const readSize = Math.min(fileSize, TAIL_READ_BYTES);
997
+ const startOffset = fileSize - readSize;
998
+ const buf = Buffer.alloc(readSize);
999
+ await fh.read(buf, 0, readSize, startOffset);
1000
+ const chunk = buf.toString("utf-8");
1001
+ const rawLines = chunk.split("\n").filter(Boolean);
1002
+ const lines = startOffset > 0 ? rawLines.slice(1) : rawLines;
1003
+ const tail = lines.slice(-count);
1004
+ return tail.reduce((acc, line) => {
1005
+ try {
1006
+ acc.push(JSON.parse(line));
1007
+ } catch {
1008
+ }
1009
+ return acc;
1010
+ }, []);
1011
+ } finally {
1012
+ await fh.close();
1013
+ }
1014
+ } catch {
1015
+ return [];
1016
+ }
1017
+ }
1018
+ /**
1019
+ * Ensure the parent directory of the log file exists.
1020
+ */
1021
+ async ensureDirectory() {
1022
+ if (this.dirEnsured) return;
1023
+ try {
1024
+ await mkdir(dirname(this.config.logPath), { recursive: true });
1025
+ this.dirEnsured = true;
1026
+ } catch {
1027
+ this.dirEnsured = true;
1028
+ }
1029
+ }
1030
+ /**
1031
+ * Rotate the log file if it exceeds the configured size limit.
1032
+ * Keeps up to 5 rotated files (`.1` through `.5`); older data is discarded.
1033
+ * Rotation failure is non-fatal — audit must not block tool execution.
1034
+ */
1035
+ async rotateIfNeeded() {
1036
+ if (this.stderrMode || !this.config.maxSizeBytes) return;
1037
+ try {
1038
+ const info = await stat(this.config.logPath).catch(() => null);
1039
+ if (!info || info.size < this.config.maxSizeBytes) return;
1040
+ for (let i = 4; i >= 1; i--) {
1041
+ const oldFile = `${this.config.logPath}.${String(i)}`;
1042
+ const newFile = `${this.config.logPath}.${String(i + 1)}`;
1043
+ await rename(oldFile, newFile).catch(() => null);
1044
+ }
1045
+ const rotatedPath = `${this.config.logPath}.1`;
1046
+ await rename(this.config.logPath, rotatedPath);
1047
+ } catch {
1048
+ }
1049
+ }
1050
+ };
1051
+ var gzipAsync = promisify(gzip);
1052
+ var SNAPSHOT_TOOL_ARGS = {
1053
+ sqlite_drop_table: { targetKey: "table" },
1054
+ sqlite_drop_index: { targetKey: "index" },
1055
+ sqlite_drop_view: { targetKey: "viewName" },
1056
+ sqlite_import_csv: { targetKey: "table" },
1057
+ sqlite_backup: { targetKey: "targetPath" }
1058
+ };
1059
+ var SNAPSHOT_EXT = ".snapshot.json.gz";
1060
+ var SNAPSHOT_EXT_LEGACY = ".snapshot.json";
1061
+ var MAX_SAMPLE_ROWS = 100;
1062
+ var DEFAULT_MAX_DATA_SIZE_BYTES = 50 * 1024 * 1024;
1063
+ var BackupManager = class {
1064
+ config;
1065
+ snapshotDir;
1066
+ dirEnsured = false;
1067
+ pendingWrites = /* @__PURE__ */ new Set();
1068
+ constructor(config, auditLogPath) {
1069
+ this.config = config;
1070
+ const logDir = dirname(auditLogPath);
1071
+ this.snapshotDir = join(logDir, "snapshots");
1072
+ }
1073
+ /**
1074
+ * Check if a tool should receive a pre-mutation snapshot.
1075
+ */
1076
+ shouldSnapshot(toolName) {
1077
+ return this.config.enabled && toolName in SNAPSHOT_TOOL_ARGS;
1078
+ }
1079
+ /**
1080
+ * Create a pre-mutation snapshot of the target object.
1081
+ *
1082
+ * @returns Relative path to the snapshot file, or undefined if skipped/failed
1083
+ */
1084
+ async createSnapshot(toolName, args, requestId, adapter, logAs) {
1085
+ if (!this.shouldSnapshot(toolName)) return void 0;
1086
+ try {
1087
+ const mapping = SNAPSHOT_TOOL_ARGS[toolName];
1088
+ if (!mapping) return void 0;
1089
+ const rawTarget = args[mapping.targetKey];
1090
+ const target = typeof rawTarget === "string" ? rawTarget : "unknown";
1091
+ return await this.captureObjectSnapshot(
1092
+ logAs ?? toolName,
1093
+ target,
1094
+ requestId,
1095
+ adapter
1096
+ );
1097
+ } catch (err) {
1098
+ const message = err instanceof Error ? err.message : String(err);
1099
+ process.stderr.write(
1100
+ `[AUDIT-BACKUP] Snapshot failed for ${toolName}: ${message}
1101
+ `
1102
+ );
1103
+ return void 0;
1104
+ }
1105
+ }
1106
+ /**
1107
+ * List available snapshots with metadata.
1108
+ */
1109
+ async listSnapshots(limit = 10, offset = 0) {
1110
+ try {
1111
+ await this.ensureDirectory();
1112
+ const files = await readdir(this.snapshotDir);
1113
+ const snapshotFiles = files.filter(
1114
+ (f) => f.endsWith(SNAPSHOT_EXT) || f.endsWith(SNAPSHOT_EXT_LEGACY)
1115
+ );
1116
+ snapshotFiles.sort((a, b) => b.localeCompare(a));
1117
+ const total = snapshotFiles.length;
1118
+ const targetFiles = snapshotFiles.slice(offset, offset + limit);
1119
+ const snapshots = [];
1120
+ for (const file of targetFiles) {
1121
+ try {
1122
+ const parsed = await this.readSnapshotFile(file);
1123
+ if (parsed) {
1124
+ snapshots.push({ ...parsed.metadata, filename: file });
1125
+ }
1126
+ } catch {
1127
+ }
1128
+ }
1129
+ return { snapshots, total };
1130
+ } catch {
1131
+ return { snapshots: [], total: 0 };
1132
+ }
1133
+ }
1134
+ /**
1135
+ * Read a specific snapshot by filename.
1136
+ */
1137
+ async getSnapshot(filename) {
1138
+ try {
1139
+ const safe = basename(filename);
1140
+ return await this.readSnapshotFile(safe);
1141
+ } catch {
1142
+ return null;
1143
+ }
1144
+ }
1145
+ /**
1146
+ * Apply retention policy — delete oldest snapshots that exceed limits.
1147
+ */
1148
+ async cleanup() {
1149
+ if (!this.config.enabled) return 0;
1150
+ try {
1151
+ const files = await readdir(this.snapshotDir);
1152
+ const snapshotFiles = files.filter(
1153
+ (f) => f.endsWith(SNAPSHOT_EXT) || f.endsWith(SNAPSHOT_EXT_LEGACY)
1154
+ );
1155
+ if (snapshotFiles.length === 0) return 0;
1156
+ const fileInfos = [];
1157
+ for (const file of snapshotFiles) {
1158
+ const filePath = join(this.snapshotDir, file);
1159
+ try {
1160
+ const stats = await stat(filePath);
1161
+ fileInfos.push({ name: file, mtime: stats.mtime, path: filePath });
1162
+ } catch {
1163
+ }
1164
+ }
1165
+ fileInfos.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
1166
+ let deleted = 0;
1167
+ const now = Date.now();
1168
+ const maxAgeMs = this.config.maxAgeDays * 24 * 60 * 60 * 1e3;
1169
+ for (const info of fileInfos) {
1170
+ const age = now - info.mtime.getTime();
1171
+ const overAge = age > maxAgeMs;
1172
+ const overCount = fileInfos.length - deleted > this.config.maxCount;
1173
+ if (overAge || overCount) {
1174
+ try {
1175
+ await unlink(info.path);
1176
+ deleted++;
1177
+ } catch {
1178
+ }
1179
+ }
1180
+ }
1181
+ if (deleted > 0) {
1182
+ process.stderr.write(
1183
+ `[AUDIT-BACKUP] Cleaned up ${String(deleted)} snapshot(s)
1184
+ `
1185
+ );
1186
+ }
1187
+ return deleted;
1188
+ } catch {
1189
+ return 0;
1190
+ }
1191
+ }
1192
+ /**
1193
+ * Flush all pending async snapshot writes.
1194
+ * Call during graceful shutdown to ensure all snapshots are persisted.
1195
+ */
1196
+ async flush() {
1197
+ if (this.pendingWrites.size > 0) {
1198
+ await Promise.allSettled(this.pendingWrites);
1199
+ }
1200
+ }
1201
+ async getStats() {
1202
+ try {
1203
+ const files = await readdir(this.snapshotDir);
1204
+ const snapshotFiles = files.filter(
1205
+ (f) => f.endsWith(SNAPSHOT_EXT) || f.endsWith(SNAPSHOT_EXT_LEGACY)
1206
+ );
1207
+ let totalSize = 0;
1208
+ let oldestMtime;
1209
+ for (const file of snapshotFiles) {
1210
+ try {
1211
+ const stats = await stat(join(this.snapshotDir, file));
1212
+ totalSize += stats.size;
1213
+ if (!oldestMtime || stats.mtime < oldestMtime) {
1214
+ oldestMtime = stats.mtime;
1215
+ }
1216
+ } catch {
1217
+ }
1218
+ }
1219
+ return {
1220
+ count: snapshotFiles.length,
1221
+ ...oldestMtime && { oldestAge: oldestMtime.toISOString() },
1222
+ totalSizeKB: Math.round(totalSize / 1024)
1223
+ };
1224
+ } catch {
1225
+ return { count: 0, totalSizeKB: 0 };
1226
+ }
1227
+ }
1228
+ // =========================================================================
1229
+ // Private snapshot capture methods
1230
+ // =========================================================================
1231
+ async captureObjectSnapshot(toolName, target, requestId, adapter) {
1232
+ const ddl = await this.buildDdl(target, adapter);
1233
+ const { rowCount } = await this.captureVolumeMetadata(target, adapter);
1234
+ const { data, dataSkipped, dataSkippedReason } = await this.captureTableData(target, adapter);
1235
+ return this.writeSnapshot(toolName, target, requestId, ddl, data, {
1236
+ ...rowCount !== void 0 && { rowCount },
1237
+ ...dataSkipped && { dataSkipped },
1238
+ ...dataSkippedReason !== void 0 && { dataSkippedReason }
1239
+ });
1240
+ }
1241
+ /**
1242
+ * Build a DDL string from sqlite_master for the given object.
1243
+ * Works for tables, views, indexes, and triggers.
1244
+ */
1245
+ async buildDdl(objectName, adapter) {
1246
+ try {
1247
+ const result = await adapter.executeQuery(
1248
+ "SELECT sql FROM sqlite_master WHERE name = ?",
1249
+ [objectName]
1250
+ );
1251
+ if (result.rows && result.rows.length > 0) {
1252
+ const row = result.rows[0];
1253
+ if (row?.["sql"] != null) {
1254
+ const val = row["sql"];
1255
+ return typeof val === "string" ? val : "";
1256
+ }
1257
+ }
1258
+ } catch (e) {
1259
+ const msg = e instanceof Error ? e.message : String(e);
1260
+ process.stderr.write(
1261
+ `[AUDIT-BACKUP] DDL capture failed for ${objectName}: ${msg}
1262
+ `
1263
+ );
1264
+ }
1265
+ return `-- Object "${objectName}" does not exist or cannot be described`;
1266
+ }
1267
+ /**
1268
+ * Capture row count using COUNT(*).
1269
+ * Near-zero cost for small tables; failures are silently ignored (best-effort).
1270
+ */
1271
+ async captureVolumeMetadata(tableName, adapter) {
1272
+ try {
1273
+ const typeResult = await adapter.executeQuery(
1274
+ "SELECT type FROM sqlite_master WHERE name = ?",
1275
+ [tableName]
1276
+ );
1277
+ const objType = typeResult.rows?.[0]?.["type"];
1278
+ if (objType !== "table") return {};
1279
+ const countResult = await adapter.executeQuery(
1280
+ `SELECT COUNT(*) as row_count FROM "${tableName}"`
1281
+ );
1282
+ const rawCount = countResult.rows?.[0]?.["row_count"];
1283
+ if (typeof rawCount === "number") {
1284
+ return { rowCount: rawCount };
1285
+ }
1286
+ } catch {
1287
+ }
1288
+ return {};
1289
+ }
1290
+ /**
1291
+ * Capture row data as INSERT statements, subject to config limits.
1292
+ * Returns empty output when `includeData` is disabled.
1293
+ */
1294
+ async captureTableData(tableName, adapter) {
1295
+ if (!this.config.includeData) {
1296
+ return { dataSkipped: false };
1297
+ }
1298
+ const maxDataSize = this.config.maxDataSizeBytes || DEFAULT_MAX_DATA_SIZE_BYTES;
1299
+ try {
1300
+ const typeResult = await adapter.executeQuery(
1301
+ "SELECT type FROM sqlite_master WHERE name = ?",
1302
+ [tableName]
1303
+ );
1304
+ const objType = typeResult.rows?.[0]?.["type"];
1305
+ if (objType !== "table") return { dataSkipped: false };
1306
+ } catch {
1307
+ return { dataSkipped: false };
1308
+ }
1309
+ try {
1310
+ const pageResult = await adapter.executeQuery(
1311
+ "SELECT (SELECT page_count FROM pragma_page_count()) * (SELECT page_size FROM pragma_page_size()) as total_size"
1312
+ );
1313
+ const rawSize = pageResult.rows?.[0]?.["total_size"];
1314
+ const totalSizeBytes = typeof rawSize === "number" ? rawSize : 0;
1315
+ if (totalSizeBytes > maxDataSize) {
1316
+ const sizeMB = Math.round(totalSizeBytes / (1024 * 1024));
1317
+ const thresholdMB = Math.round(maxDataSize / (1024 * 1024));
1318
+ return {
1319
+ dataSkipped: true,
1320
+ dataSkippedReason: `Database size ~${String(sizeMB)}MB exceeds ${String(thresholdMB)}MB threshold`
1321
+ };
1322
+ }
1323
+ } catch {
1324
+ }
1325
+ try {
1326
+ const result = await adapter.executeQuery(
1327
+ `SELECT * FROM "${tableName}" LIMIT ${String(MAX_SAMPLE_ROWS)}`
1328
+ );
1329
+ if (result.rows && result.rows.length > 0) {
1330
+ const firstRow = result.rows[0];
1331
+ if (firstRow) {
1332
+ const cols = Object.keys(firstRow).map((c) => `"${c}"`).join(", ");
1333
+ const data = result.rows.map((row) => {
1334
+ const vals = Object.values(row).map((v) => {
1335
+ if (v === null) return "NULL";
1336
+ if (typeof v === "string")
1337
+ return `'${v.replace(/'/g, "''")}'`;
1338
+ if (typeof v === "number" || typeof v === "boolean")
1339
+ return String(v);
1340
+ return `'${JSON.stringify(v).replace(/'/g, "''")}'`;
1341
+ }).join(", ");
1342
+ return `INSERT INTO "${tableName}" (${cols}) VALUES (${vals});`;
1343
+ }).join("\n");
1344
+ return { data, dataSkipped: false };
1345
+ }
1346
+ }
1347
+ } catch {
1348
+ }
1349
+ return { dataSkipped: false };
1350
+ }
1351
+ async writeSnapshot(tool, target, requestId, ddl, data, volumeMeta) {
1352
+ await this.ensureDirectory();
1353
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1354
+ const safeTarget = target.replace(/[^a-zA-Z0-9_-]/g, "_");
1355
+ const safeTool = tool.replace(/[^a-zA-Z0-9_-]/g, "_");
1356
+ const filename = `${timestamp.replace(/[:.]/g, "-")}_${safeTool}_${safeTarget}${SNAPSHOT_EXT}`;
1357
+ const content = {
1358
+ metadata: {
1359
+ timestamp,
1360
+ tool,
1361
+ target,
1362
+ type: data ? "ddl+data" : "ddl",
1363
+ requestId,
1364
+ sizeBytes: 0,
1365
+ // Updated after serialization
1366
+ ...volumeMeta?.rowCount !== void 0 && {
1367
+ rowCount: volumeMeta.rowCount
1368
+ },
1369
+ ...volumeMeta?.dataSkipped && { dataSkipped: true },
1370
+ ...volumeMeta?.dataSkippedReason && {
1371
+ dataSkippedReason: volumeMeta.dataSkippedReason
1372
+ }
1373
+ },
1374
+ ddl,
1375
+ data
1376
+ };
1377
+ const json = JSON.stringify(content, null, 2);
1378
+ const sizeBytes = Buffer.byteLength(json, "utf-8");
1379
+ const finalJson = json.replace(
1380
+ '"sizeBytes": 0',
1381
+ `"sizeBytes": ${String(sizeBytes)}`
1382
+ );
1383
+ const compressed = await gzipAsync(Buffer.from(finalJson, "utf-8"));
1384
+ const filePath = join(this.snapshotDir, filename);
1385
+ const writePromise = mkdtemp(`${this.snapshotDir}${sep}.tmp-`).then(async (tempDir) => {
1386
+ const tmpPath = join(tempDir, filename);
1387
+ await writeFile(tmpPath, compressed, { flag: "wx" });
1388
+ await rename(tmpPath, filePath);
1389
+ await rmdir(tempDir).catch(() => null);
1390
+ }).catch((err) => {
1391
+ const msg = err instanceof Error ? err.message : String(err);
1392
+ process.stderr.write(
1393
+ `[AUDIT-BACKUP] Async write failed for ${filename}: ${msg}
1394
+ `
1395
+ );
1396
+ });
1397
+ this.pendingWrites.add(writePromise);
1398
+ void writePromise.finally(() => {
1399
+ this.pendingWrites.delete(writePromise);
1400
+ });
1401
+ return filename;
1402
+ }
1403
+ /**
1404
+ * Read and decompress a snapshot file (supports both gzip and legacy JSON).
1405
+ */
1406
+ async readSnapshotFile(filename) {
1407
+ const filePath = join(this.snapshotDir, filename);
1408
+ const raw = await readFile(filePath);
1409
+ if (raw[0] === 31 && raw[1] === 139) {
1410
+ const decompressed = gunzipSync(raw);
1411
+ return JSON.parse(decompressed.toString("utf-8"));
1412
+ }
1413
+ return JSON.parse(raw.toString("utf-8"));
1414
+ }
1415
+ async ensureDirectory() {
1416
+ if (this.dirEnsured) return;
1417
+ try {
1418
+ await mkdir(this.snapshotDir, { recursive: true });
1419
+ this.dirEnsured = true;
1420
+ } catch {
1421
+ this.dirEnsured = true;
1422
+ }
1423
+ }
1424
+ };
1425
+ var SENSITIVE_KEY_PATTERN = /^(password|passwd|token|secret|authorization|api_?key|credential|private_?key|access_?token|refresh_?token)$/i;
1426
+ var SENSITIVE_VALUE_PATTERN = /(?:sk-|Bearer |token\s*[:=]\s*|password\s*[:=]\s*|secret\s*[:=]\s*|apikey\s*[:=]\s*|api_key\s*[:=]\s*|AWS_SECRET_ACCESS_KEY\s*[:=]\s*|GITHUB_TOKEN\s*[:=]\s*|ghp_|gho_|ghu_|ghs_|xoxb-|xoxp-|xoxs-|AZURE_[A-Z_]*\s*[:=]\s*|DATABASE_URL\s*[:=]\s*|AKIA|sk-ant-api[a-zA-Z0-9_-]+|sk_live_[a-zA-Z0-9_]+|rk_live_[a-zA-Z0-9_]+|SG\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+|npm_[a-zA-Z0-9]{30,}|dpl_[a-zA-Z0-9]{30,}|hvs\.[a-zA-Z0-9_-]+)[^\s'",;)}\]]{4,}/gi;
1427
+ function redactSensitiveKeys(value, depth = 0) {
1428
+ const MAX_DEPTH = 5;
1429
+ if (depth > MAX_DEPTH || value === null || value === void 0) {
1430
+ return value;
1431
+ }
1432
+ if (Array.isArray(value)) {
1433
+ return value.map((item) => redactSensitiveKeys(item, depth + 1));
1434
+ }
1435
+ if (typeof value === "string") {
1436
+ const isSql = /^\s*(?:SELECT|INSERT|UPDATE|DELETE|WITH|CREATE|ALTER|DROP|PRAGMA)\b/i.test(
1437
+ value
1438
+ );
1439
+ const scrubbed = isSql ? value.replace(/'(?:''|[^'])*'/g, "'***'") : value;
1440
+ return scrubbed.replace(SENSITIVE_VALUE_PATTERN, "[REDACTED]");
1441
+ }
1442
+ if (typeof value === "object") {
1443
+ const result = {};
1444
+ for (const [key, val] of Object.entries(value)) {
1445
+ if (SENSITIVE_KEY_PATTERN.test(key)) {
1446
+ result[key] = "[REDACTED]";
1447
+ } else if (typeof val === "object" && val !== null) {
1448
+ result[key] = redactSensitiveKeys(val, depth + 1);
1449
+ } else if (typeof val === "string") {
1450
+ const isSql = key.toLowerCase() === "sql" || key.toLowerCase() === "query" || /^\s*(?:SELECT|INSERT|UPDATE|DELETE|WITH|CREATE|ALTER|DROP|PRAGMA)\b/i.test(
1451
+ val
1452
+ );
1453
+ const scrubbed = isSql ? val.replace(/'(?:''|[^'])*'/g, "'***'") : val;
1454
+ result[key] = scrubbed.replace(SENSITIVE_VALUE_PATTERN, "[REDACTED]");
1455
+ } else {
1456
+ result[key] = val;
1457
+ }
1458
+ }
1459
+ return result;
1460
+ }
1461
+ return value;
1462
+ }
1463
+ var ALWAYS_AUDITED_SCOPES = /* @__PURE__ */ new Set(["write", "admin"]);
1464
+ function scopeToCategory(scope) {
1465
+ if (scope === "admin") return "admin";
1466
+ if (scope === "read") return "read";
1467
+ return "write";
1468
+ }
1469
+ function createAuditInterceptor(auditLogger, backupManager) {
1470
+ const auditReads = auditLogger.config.auditReads;
1471
+ let currentQueryAdapter;
1472
+ return {
1473
+ setQueryAdapter(adapter) {
1474
+ currentQueryAdapter = adapter;
1475
+ },
1476
+ async around(toolName, args, requestId, fn, options) {
1477
+ const scope = getRequiredScope(toolName);
1478
+ if (!ALWAYS_AUDITED_SCOPES.has(scope) && !auditReads) {
1479
+ return fn();
1480
+ }
1481
+ const isReadScope = scope === "read";
1482
+ const authCtx = getAuthContext();
1483
+ const start = performance.now();
1484
+ let success = true;
1485
+ let error;
1486
+ let backupRef;
1487
+ let tokenEstimate;
1488
+ if (backupManager !== void 0 && currentQueryAdapter !== void 0 && backupManager.shouldSnapshot(toolName)) {
1489
+ try {
1490
+ backupRef = await backupManager.createSnapshot(
1491
+ toolName,
1492
+ args ?? {},
1493
+ requestId,
1494
+ currentQueryAdapter,
1495
+ options?.logAs
1496
+ );
1497
+ } catch {
1498
+ }
1499
+ }
1500
+ try {
1501
+ const result = await fn();
1502
+ if (typeof result === "object" && result !== null) {
1503
+ try {
1504
+ const json = JSON.stringify({
1505
+ ...result,
1506
+ _meta: { tokenEstimate: 0 }
1507
+ });
1508
+ tokenEstimate = Math.ceil(Buffer.byteLength(json, "utf8") / 4);
1509
+ } catch {
1510
+ }
1511
+ } else if (typeof result === "string") {
1512
+ tokenEstimate = Math.ceil(Buffer.byteLength(result, "utf8") / 4);
1513
+ }
1514
+ return result;
1515
+ } catch (err) {
1516
+ success = false;
1517
+ error = sanitizeErrorMessage(
1518
+ err instanceof Error ? err.message : String(err)
1519
+ );
1520
+ const errorResult = {
1521
+ success: false,
1522
+ error,
1523
+ code: "INTERNAL_ERROR",
1524
+ category: "internal",
1525
+ recoverable: false
1526
+ };
1527
+ const enriched = JSON.stringify({
1528
+ ...errorResult,
1529
+ _meta: { tokenEstimate: 0 }
1530
+ });
1531
+ tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
1532
+ throw err;
1533
+ } finally {
1534
+ const durationMs = Math.round(performance.now() - start);
1535
+ if (isReadScope) {
1536
+ auditLogger.log({
1537
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1538
+ requestId,
1539
+ tool: options?.logAs ?? toolName,
1540
+ category: "read",
1541
+ scope,
1542
+ durationMs,
1543
+ success,
1544
+ error,
1545
+ tokenEstimate
1546
+ });
1547
+ } else {
1548
+ auditLogger.log({
1549
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1550
+ requestId,
1551
+ tool: options?.logAs ?? toolName,
1552
+ category: scopeToCategory(scope),
1553
+ scope,
1554
+ user: authCtx?.claims?.sub ?? null,
1555
+ scopes: authCtx?.scopes ?? [],
1556
+ durationMs,
1557
+ success,
1558
+ error,
1559
+ args: auditLogger.config.redact ? void 0 : redactSensitiveKeys(args),
1560
+ backup: backupRef,
1561
+ tokenEstimate
1562
+ });
1563
+ }
1564
+ }
1565
+ }
1566
+ };
1567
+ }
1568
+
1569
+ // src/server/registration/built-in-tools.ts
1570
+ function registerBuiltInTools(server, adaptersMap, config, toolFilter) {
1571
+ const serverInfoOpts = {
1572
+ title: "Server Info",
1573
+ description: "Get information about the db-mcp server and registered adapters",
1574
+ icons: SERVER_ICONS,
1575
+ annotations: READ_ONLY
1576
+ };
1577
+ server.registerTool("server_info", serverInfoOpts, () => {
1578
+ const authCtx = getAuthContext();
1579
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "server_info")) {
1580
+ throw new InsufficientScopeError(["read"], authCtx.scopes);
1581
+ }
1582
+ const adapterInfo = [];
1583
+ for (const [id, adapter] of adaptersMap) {
1584
+ adapterInfo.push({
1585
+ id,
1586
+ ...adapter.getInfo()
1587
+ });
1588
+ }
1589
+ return {
1590
+ content: [
1591
+ {
1592
+ type: "text",
1593
+ text: JSON.stringify(
1594
+ {
1595
+ name: config.name,
1596
+ version: config.version,
1597
+ transport: config.transport,
1598
+ adapters: adapterInfo,
1599
+ toolFilter: {
1600
+ raw: toolFilter.raw,
1601
+ enabledGroups: [...toolFilter.enabledGroups]
1602
+ }
1603
+ },
1604
+ null,
1605
+ 2
1606
+ )
1607
+ }
1608
+ ]
1609
+ };
1610
+ });
1611
+ const healthOpts = {
1612
+ title: "Server Health",
1613
+ description: "Check health status of all database connections",
1614
+ icons: SERVER_ICONS,
1615
+ annotations: READ_ONLY
1616
+ };
1617
+ server.registerTool("server_health", healthOpts, async () => {
1618
+ const authCtx = getAuthContext();
1619
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "server_health")) {
1620
+ throw new InsufficientScopeError(["read"], authCtx.scopes);
1621
+ }
1622
+ const health = {
1623
+ server: "healthy",
1624
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1625
+ adapters: {}
1626
+ };
1627
+ for (const [id, adapter] of adaptersMap) {
1628
+ try {
1629
+ const adapterHealth = await adapter.getHealth();
1630
+ health["adapters"][id] = adapterHealth;
1631
+ } catch (error) {
1632
+ let errorMsg = error instanceof Error ? error.message : "Unknown error";
1633
+ errorMsg = errorMsg.replace(/(:[^:@/]+@)/g, ":***@");
1634
+ health["adapters"][id] = {
1635
+ connected: false,
1636
+ error: errorMsg
1637
+ };
1638
+ }
1639
+ }
1640
+ return {
1641
+ content: [
1642
+ {
1643
+ type: "text",
1644
+ text: JSON.stringify(health, null, 2)
1645
+ }
1646
+ ]
1647
+ };
1648
+ });
1649
+ const listAdaptersOpts = {
1650
+ title: "List Adapters",
1651
+ description: "List all registered database adapters",
1652
+ icons: SERVER_ICONS,
1653
+ annotations: READ_ONLY
1654
+ };
1655
+ server.registerTool("list_adapters", listAdaptersOpts, () => {
1656
+ const authCtx = getAuthContext();
1657
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "list_adapters")) {
1658
+ throw new InsufficientScopeError(["read"], authCtx.scopes);
1659
+ }
1660
+ const adapters = [];
1661
+ for (const [, adapter] of adaptersMap) {
1662
+ adapters.push({
1663
+ id: `${adapter.type}:default`,
1664
+ type: adapter.type,
1665
+ name: adapter.name,
1666
+ version: adapter.version,
1667
+ connected: adapter.isConnected()
1668
+ });
1669
+ }
1670
+ return {
1671
+ content: [
1672
+ {
1673
+ type: "text",
1674
+ text: JSON.stringify(adapters, null, 2)
1675
+ }
1676
+ ]
1677
+ };
1678
+ });
1679
+ registerToolScopes(
1680
+ /* @__PURE__ */ new Map([
1681
+ ["server_info", ["read"]],
1682
+ ["server_health", ["read"]],
1683
+ ["list_adapters", ["read"]]
1684
+ ])
1685
+ );
1686
+ }
1687
+
1688
+ // src/server/registration/help-resources.ts
1689
+ function registerHelpResources(server, toolFilter) {
1690
+ const gotchasContent = HELP_CONTENT.get("gotchas");
1691
+ if (gotchasContent) {
1692
+ server.registerResource(
1693
+ "sqlite_help",
1694
+ "sqlite://help",
1695
+ {
1696
+ description: "Critical gotchas, WASM vs Native comparison, and Code Mode API reference",
1697
+ mimeType: "text/markdown",
1698
+ annotations: ASSISTANT_FOCUSED
1699
+ },
1700
+ () => ({
1701
+ contents: [
1702
+ {
1703
+ uri: "sqlite://help",
1704
+ mimeType: "text/markdown",
1705
+ text: gotchasContent
1706
+ }
1707
+ ]
1708
+ })
1709
+ );
1710
+ }
1711
+ const groupHelpKeys = [
1712
+ { group: "core", key: "core" },
1713
+ { group: "json", key: "json" },
1714
+ { group: "text", key: "text" },
1715
+ { group: "stats", key: "stats" },
1716
+ { group: "vector", key: "vector" },
1717
+ { group: "geo", key: "geo" },
1718
+ { group: "admin", key: "admin" },
1719
+ { group: "transactions", key: "transactions" },
1720
+ { group: "introspection", key: "introspection" },
1721
+ { group: "migration", key: "migration" }
1722
+ ];
1723
+ for (const { group, key } of groupHelpKeys) {
1724
+ const isCodemodeOnly = toolFilter.enabledGroups.size === 1 && toolFilter.enabledGroups.has("codemode");
1725
+ if (!toolFilter.enabledGroups.has(group) && !isCodemodeOnly) {
1726
+ continue;
1727
+ }
1728
+ const content = HELP_CONTENT.get(key);
1729
+ if (!content) continue;
1730
+ server.registerResource(
1731
+ `sqlite_help_${key}`,
1732
+ `sqlite://help/${key}`,
1733
+ {
1734
+ description: `Tool reference for the ${group} tool group`,
1735
+ mimeType: "text/markdown",
1736
+ annotations: ASSISTANT_FOCUSED
1737
+ },
1738
+ () => ({
1739
+ contents: [
1740
+ {
1741
+ uri: `sqlite://help/${key}`,
1742
+ mimeType: "text/markdown",
1743
+ text: content
1744
+ }
1745
+ ]
1746
+ })
1747
+ );
1748
+ }
1749
+ const registeredHelp = ["sqlite://help"];
1750
+ for (const { group, key } of groupHelpKeys) {
1751
+ if (toolFilter.enabledGroups.has(group)) {
1752
+ registeredHelp.push(`sqlite://help/${key}`);
1753
+ }
1754
+ }
1755
+ logger.info(`Help resources: ${registeredHelp.join(", ")}`, {
1756
+ module: "SERVER"
1757
+ });
1758
+ }
1759
+ function redactSqlLiterals(text) {
1760
+ return text.replace(/'(?:''|[^'])*'/g, "'***'");
1761
+ }
1762
+ function validateDdl(sql) {
1763
+ const cleanSql = sql.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "");
1764
+ const upperSql = cleanSql.toUpperCase();
1765
+ if (upperSql.includes("ATTACH ") || upperSql.includes("DETACH ") || upperSql.includes("PRAGMA ") || upperSql.includes("LOAD_EXTENSION(")) {
1766
+ throw new Error(
1767
+ `DDL validation failed: unauthorized command or function call`
1768
+ );
1769
+ }
1770
+ if (upperSql.includes(" ON MAIN.") || upperSql.includes(" ON TEMP.")) {
1771
+ throw new Error(
1772
+ `DDL validation failed: trigger attempts to target a specific database`
1773
+ );
1774
+ }
1775
+ }
1776
+ function registerAuditResource(server, auditLogger, backupManager) {
1777
+ if (!auditLogger) return;
1778
+ server.registerResource(
1779
+ "sqlite_audit",
1780
+ "sqlite://audit",
1781
+ {
1782
+ description: "Recent audit log entries and backup statistics. Shows the last 50 tool invocations with timing, outcomes, and token estimates.",
1783
+ mimeType: "application/json"
1784
+ },
1785
+ async () => {
1786
+ const recent = await auditLogger.recent(50);
1787
+ const backupStats = backupManager ? await backupManager.getStats() : void 0;
1788
+ const payload = {
1789
+ entries: recent,
1790
+ stats: {
1791
+ totalEntries: recent.length,
1792
+ ...backupStats && { backups: backupStats }
1793
+ }
1794
+ };
1795
+ const payloadStr = JSON.stringify(payload, null, 2);
1796
+ return {
1797
+ contents: [
1798
+ {
1799
+ uri: "sqlite://audit",
1800
+ mimeType: "application/json",
1801
+ text: redactSqlLiterals(payloadStr)
1802
+ }
1803
+ ]
1804
+ };
1805
+ }
1806
+ );
1807
+ }
1808
+ function registerAuditBackupTools(server, backupManager, adaptersMap) {
1809
+ if (!backupManager) return;
1810
+ server.registerTool(
1811
+ "sqlite_audit_list_backups",
1812
+ {
1813
+ title: "List Audit Backups",
1814
+ description: "List pre-mutation DDL snapshots captured before destructive operations. Returns metadata for each snapshot including timestamp, tool, target, and size.",
1815
+ inputSchema: AuditListBackupsSchema,
1816
+ outputSchema: AuditListBackupsOutputSchema,
1817
+ annotations: {
1818
+ readOnlyHint: true,
1819
+ destructiveHint: false,
1820
+ idempotentHint: true,
1821
+ openWorldHint: false
1822
+ // Requires admin scope
1823
+ }
1824
+ },
1825
+ async (args) => {
1826
+ const authCtx = getAuthContext();
1827
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_list_backups")) {
1828
+ throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
1829
+ }
1830
+ let parsed;
1831
+ try {
1832
+ parsed = AuditListBackupsSchema.parse(args ?? {});
1833
+ } catch (error) {
1834
+ const structured = formatHandlerError(error);
1835
+ return {
1836
+ content: [
1837
+ {
1838
+ type: "text",
1839
+ text: JSON.stringify(structured, null, 2)
1840
+ }
1841
+ ],
1842
+ isError: true,
1843
+ structuredContent: structured
1844
+ };
1845
+ }
1846
+ const limit = parsed.limit ?? 10;
1847
+ const offset = parsed.offset ?? 0;
1848
+ const { snapshots, total } = await backupManager.listSnapshots(
1849
+ limit,
1850
+ offset
1851
+ );
1852
+ const result = {
1853
+ success: true,
1854
+ snapshots,
1855
+ count: snapshots.length,
1856
+ totalCount: total
1857
+ };
1858
+ const tokenEstimate = Math.ceil(
1859
+ Buffer.byteLength(JSON.stringify(result), "utf8") / 4
1860
+ );
1861
+ return {
1862
+ content: [
1863
+ {
1864
+ type: "text",
1865
+ text: JSON.stringify(
1866
+ { ...result, _meta: { tokenEstimate } },
1867
+ null,
1868
+ 2
1869
+ )
1870
+ }
1871
+ ],
1872
+ structuredContent: result
1873
+ };
1874
+ }
1875
+ );
1876
+ server.registerTool(
1877
+ "sqlite_audit_get_backup",
1878
+ {
1879
+ title: "Get Audit Backup",
1880
+ description: "Retrieve a specific pre-mutation DDL snapshot by filename. Returns the full snapshot content including DDL and optional data.",
1881
+ inputSchema: z.object({
1882
+ filename: z.string().default("").describe("Snapshot filename from sqlite_audit_list_backups results")
1883
+ }),
1884
+ outputSchema: AuditGetBackupOutputSchema,
1885
+ annotations: {
1886
+ readOnlyHint: true,
1887
+ destructiveHint: false,
1888
+ idempotentHint: true,
1889
+ openWorldHint: false
1890
+ // Requires admin scope
1891
+ }
1892
+ },
1893
+ async (args) => {
1894
+ const authCtx = getAuthContext();
1895
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_get_backup")) {
1896
+ throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
1897
+ }
1898
+ let filename;
1899
+ try {
1900
+ const parsed = z.object({ filename: z.string().min(1, "filename is required") }).parse(args);
1901
+ filename = parsed.filename;
1902
+ } catch (error) {
1903
+ const structured = formatHandlerError(error);
1904
+ return {
1905
+ content: [
1906
+ {
1907
+ type: "text",
1908
+ text: JSON.stringify(structured, null, 2)
1909
+ }
1910
+ ],
1911
+ isError: true,
1912
+ structuredContent: structured
1913
+ };
1914
+ }
1915
+ const snapshot = await backupManager.getSnapshot(filename);
1916
+ if (!snapshot) {
1917
+ const errRes = {
1918
+ success: false,
1919
+ error: `Snapshot not found: ${filename}`
1920
+ };
1921
+ return {
1922
+ content: [
1923
+ {
1924
+ type: "text",
1925
+ text: JSON.stringify(errRes, null, 2)
1926
+ }
1927
+ ],
1928
+ isError: true,
1929
+ structuredContent: errRes
1930
+ };
1931
+ }
1932
+ const result = { success: true, ...snapshot };
1933
+ const snapshotStr = JSON.stringify(result, null, 2);
1934
+ return {
1935
+ content: [
1936
+ {
1937
+ type: "text",
1938
+ text: redactSqlLiterals(snapshotStr)
1939
+ }
1940
+ ],
1941
+ structuredContent: result
1942
+ };
1943
+ }
1944
+ );
1945
+ server.registerTool(
1946
+ "sqlite_audit_cleanup",
1947
+ {
1948
+ title: "Cleanup Audit Backups",
1949
+ description: "Apply retention policy to audit backup snapshots. Deletes snapshots exceeding age or count limits.",
1950
+ outputSchema: AuditCleanupOutputSchema,
1951
+ annotations: {
1952
+ readOnlyHint: false,
1953
+ destructiveHint: true,
1954
+ idempotentHint: true,
1955
+ openWorldHint: false
1956
+ }
1957
+ },
1958
+ async () => {
1959
+ const authCtx = getAuthContext();
1960
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_cleanup")) {
1961
+ throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
1962
+ }
1963
+ const deleted = await backupManager.cleanup();
1964
+ const result = {
1965
+ success: true,
1966
+ deletedCount: deleted,
1967
+ message: deleted > 0 ? `Cleaned up ${String(deleted)} snapshot(s)` : "No snapshots to clean up"
1968
+ };
1969
+ return {
1970
+ content: [
1971
+ {
1972
+ type: "text",
1973
+ text: JSON.stringify(result, null, 2)
1974
+ }
1975
+ ],
1976
+ structuredContent: result
1977
+ };
1978
+ }
1979
+ );
1980
+ server.registerTool(
1981
+ "sqlite_audit_diff_backup",
1982
+ {
1983
+ title: "Diff Audit Backup",
1984
+ description: "Compare a pre-mutation DDL snapshot against the live database schema. Shows objects that have been added, removed, or modified since the snapshot was taken.",
1985
+ inputSchema: z.object({
1986
+ filename: z.string().default("").describe(
1987
+ "Snapshot filename to compare against the live database schema"
1988
+ )
1989
+ }),
1990
+ outputSchema: AuditDiffBackupOutputSchema,
1991
+ annotations: {
1992
+ readOnlyHint: true,
1993
+ destructiveHint: false,
1994
+ idempotentHint: true,
1995
+ openWorldHint: false
1996
+ // Requires admin scope
1997
+ }
1998
+ },
1999
+ async (args) => {
2000
+ const authCtx = getAuthContext();
2001
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_diff_backup")) {
2002
+ throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
2003
+ }
2004
+ let filename;
2005
+ try {
2006
+ const parsed = z.object({ filename: z.string().min(1, "filename is required") }).parse(args);
2007
+ filename = parsed.filename;
2008
+ } catch (error) {
2009
+ const structured = formatHandlerError(error);
2010
+ return {
2011
+ content: [
2012
+ {
2013
+ type: "text",
2014
+ text: JSON.stringify(structured, null, 2)
2015
+ }
2016
+ ],
2017
+ isError: true,
2018
+ structuredContent: structured
2019
+ };
2020
+ }
2021
+ const snapshot = await backupManager.getSnapshot(filename);
2022
+ if (!snapshot) {
2023
+ const errRes = {
2024
+ success: false,
2025
+ error: `Snapshot not found: ${filename}`
2026
+ };
2027
+ return {
2028
+ content: [
2029
+ {
2030
+ type: "text",
2031
+ text: JSON.stringify(errRes, null, 2)
2032
+ }
2033
+ ],
2034
+ isError: true,
2035
+ structuredContent: errRes
2036
+ };
2037
+ }
2038
+ const adapter = [...adaptersMap.values()][0];
2039
+ if (!adapter) {
2040
+ const errRes = {
2041
+ success: false,
2042
+ error: "No connected adapter available"
2043
+ };
2044
+ return {
2045
+ content: [
2046
+ {
2047
+ type: "text",
2048
+ text: JSON.stringify(errRes, null, 2)
2049
+ }
2050
+ ],
2051
+ isError: true,
2052
+ structuredContent: errRes
2053
+ };
2054
+ }
2055
+ try {
2056
+ const liveResult = await adapter.executeReadQuery(
2057
+ "SELECT name, type, sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY type, name"
2058
+ );
2059
+ const liveObjects = /* @__PURE__ */ new Map();
2060
+ for (const row of liveResult.rows ?? []) {
2061
+ liveObjects.set(row["name"], {
2062
+ type: row["type"],
2063
+ sql: row["sql"]
2064
+ });
2065
+ }
2066
+ const snapshotDdl = snapshot.ddl ?? "";
2067
+ const snapshotObjects = /* @__PURE__ */ new Map();
2068
+ const statements = snapshotDdl.split(";").map((s) => s.trim()).filter(Boolean);
2069
+ for (const stmt of statements) {
2070
+ const match = /CREATE\s+(?:VIRTUAL\s+)?(?:TEMP(?:ORARY)?\s+)?(TABLE|INDEX|VIEW|TRIGGER)\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|(\S+))/i.exec(
2071
+ stmt
2072
+ );
2073
+ if (match) {
2074
+ const type = (match[1] ?? "").toLowerCase();
2075
+ const name = match[2] ?? match[3] ?? "";
2076
+ snapshotObjects.set(name, { type, sql: stmt });
2077
+ }
2078
+ }
2079
+ const diffs = [];
2080
+ const snapshotTarget = snapshot.metadata?.target;
2081
+ for (const [name, live] of liveObjects) {
2082
+ if (name === snapshotTarget || snapshotObjects.has(name)) {
2083
+ if (!snapshotObjects.has(name)) {
2084
+ diffs.push({
2085
+ object: name,
2086
+ type: live.type,
2087
+ status: "added",
2088
+ liveDefinition: live.sql
2089
+ });
2090
+ }
2091
+ }
2092
+ }
2093
+ for (const [name, snap] of snapshotObjects) {
2094
+ if (!liveObjects.has(name)) {
2095
+ diffs.push({
2096
+ object: name,
2097
+ type: snap.type,
2098
+ status: "removed",
2099
+ snapshotDefinition: snap.sql
2100
+ });
2101
+ }
2102
+ }
2103
+ for (const [name, snap] of snapshotObjects) {
2104
+ const live = liveObjects.get(name);
2105
+ if (live && live.sql !== snap.sql) {
2106
+ diffs.push({
2107
+ object: name,
2108
+ type: live.type,
2109
+ status: "modified",
2110
+ liveDefinition: live.sql,
2111
+ snapshotDefinition: snap.sql
2112
+ });
2113
+ }
2114
+ }
2115
+ const result = {
2116
+ success: true,
2117
+ diffs,
2118
+ snapshotTimestamp: snapshot.metadata?.timestamp ?? "",
2119
+ snapshotTarget: snapshot.metadata?.target ?? ""
2120
+ };
2121
+ return {
2122
+ content: [
2123
+ {
2124
+ type: "text",
2125
+ text: JSON.stringify(result, null, 2)
2126
+ }
2127
+ ],
2128
+ structuredContent: result
2129
+ };
2130
+ } catch (error) {
2131
+ const errRes = {
2132
+ success: false,
2133
+ error: error instanceof Error ? error.message : String(error)
2134
+ };
2135
+ return {
2136
+ content: [
2137
+ {
2138
+ type: "text",
2139
+ text: JSON.stringify(errRes, null, 2)
2140
+ }
2141
+ ],
2142
+ isError: true,
2143
+ structuredContent: errRes
2144
+ };
2145
+ }
2146
+ }
2147
+ );
2148
+ server.registerTool(
2149
+ "sqlite_audit_restore_backup",
2150
+ {
2151
+ title: "Restore Audit Backup",
2152
+ description: "Restore a pre-mutation DDL snapshot by executing its DDL statements against the live database. Use dryRun=true to preview the DDL without applying changes.",
2153
+ inputSchema: z.object({
2154
+ filename: z.string().default("").describe("Snapshot filename to restore from"),
2155
+ dryRun: z.unknown().optional().describe("If true, returns the DDL without executing it (boolean)")
2156
+ }),
2157
+ outputSchema: AuditRestoreBackupOutputSchema,
2158
+ annotations: {
2159
+ readOnlyHint: false,
2160
+ destructiveHint: true,
2161
+ idempotentHint: false,
2162
+ openWorldHint: false
2163
+ }
2164
+ },
2165
+ async (args) => {
2166
+ const authCtx = getAuthContext();
2167
+ if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_restore_backup")) {
2168
+ throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
2169
+ }
2170
+ let filename;
2171
+ let dryRun;
2172
+ try {
2173
+ const parsed = z.object({
2174
+ filename: z.string().min(1, "filename is required"),
2175
+ dryRun: z.boolean().optional().default(false)
2176
+ }).parse(args);
2177
+ filename = parsed.filename;
2178
+ dryRun = parsed.dryRun;
2179
+ } catch (error) {
2180
+ const structured = formatHandlerError(error);
2181
+ return {
2182
+ content: [
2183
+ {
2184
+ type: "text",
2185
+ text: JSON.stringify(structured, null, 2)
2186
+ }
2187
+ ],
2188
+ isError: true,
2189
+ structuredContent: structured
2190
+ };
2191
+ }
2192
+ const snapshot = await backupManager.getSnapshot(filename);
2193
+ if (!snapshot) {
2194
+ const errRes = {
2195
+ success: false,
2196
+ error: `Snapshot not found: ${filename}`
2197
+ };
2198
+ return {
2199
+ content: [
2200
+ {
2201
+ type: "text",
2202
+ text: JSON.stringify(errRes, null, 2)
2203
+ }
2204
+ ],
2205
+ isError: true,
2206
+ structuredContent: errRes
2207
+ };
2208
+ }
2209
+ const ddl = snapshot.ddl ?? "";
2210
+ if (!ddl.trim()) {
2211
+ const errRes = {
2212
+ success: false,
2213
+ error: "Snapshot contains no DDL statements"
2214
+ };
2215
+ return {
2216
+ content: [
2217
+ {
2218
+ type: "text",
2219
+ text: JSON.stringify(errRes, null, 2)
2220
+ }
2221
+ ],
2222
+ isError: true,
2223
+ structuredContent: errRes
2224
+ };
2225
+ }
2226
+ const adapter = [...adaptersMap.values()][0];
2227
+ if (!adapter) {
2228
+ const errRes = {
2229
+ success: false,
2230
+ error: "No connected adapter available"
2231
+ };
2232
+ return {
2233
+ content: [
2234
+ {
2235
+ type: "text",
2236
+ text: JSON.stringify(errRes, null, 2)
2237
+ }
2238
+ ],
2239
+ isError: true,
2240
+ structuredContent: errRes
2241
+ };
2242
+ }
2243
+ if (dryRun) {
2244
+ const result = {
2245
+ success: true,
2246
+ message: "Dry run: no changes applied",
2247
+ dryRun: true,
2248
+ ddl: redactSqlLiterals(ddl)
2249
+ };
2250
+ return {
2251
+ content: [
2252
+ {
2253
+ type: "text",
2254
+ text: JSON.stringify(result, null, 2)
2255
+ }
2256
+ ],
2257
+ structuredContent: result
2258
+ };
2259
+ }
2260
+ try {
2261
+ const statements = ddl.split(";").map((s) => s.trim()).filter((s) => {
2262
+ const cleanSql = s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "").trim();
2263
+ return cleanSql.length > 0;
2264
+ });
2265
+ if (statements.length === 0) {
2266
+ const result2 = {
2267
+ success: true,
2268
+ message: "No executable statements found in snapshot (only comments)",
2269
+ changesApplied: 0
2270
+ };
2271
+ return {
2272
+ content: [
2273
+ {
2274
+ type: "text",
2275
+ text: JSON.stringify(result2, null, 2)
2276
+ }
2277
+ ],
2278
+ structuredContent: result2
2279
+ };
2280
+ }
2281
+ await adapter.executeQuery("BEGIN TRANSACTION;");
2282
+ try {
2283
+ for (const stmt of statements) {
2284
+ validateDdl(stmt);
2285
+ await adapter.executeQuery(`${stmt};`);
2286
+ }
2287
+ await adapter.executeQuery("COMMIT;");
2288
+ } catch (e) {
2289
+ await adapter.executeQuery("ROLLBACK;");
2290
+ throw e;
2291
+ }
2292
+ const result = {
2293
+ success: true,
2294
+ message: `Successfully executed ${statements.length} statement(s) from snapshot`,
2295
+ changesApplied: statements.length
2296
+ };
2297
+ return {
2298
+ content: [
2299
+ {
2300
+ type: "text",
2301
+ text: JSON.stringify(result, null, 2)
2302
+ }
2303
+ ],
2304
+ structuredContent: result
2305
+ };
2306
+ } catch (error) {
2307
+ const errRes = {
2308
+ success: false,
2309
+ error: error instanceof Error ? error.message : String(error)
2310
+ };
2311
+ return {
2312
+ content: [
2313
+ {
2314
+ type: "text",
2315
+ text: JSON.stringify(errRes, null, 2)
2316
+ }
2317
+ ],
2318
+ isError: true,
2319
+ structuredContent: errRes
2320
+ };
2321
+ }
2322
+ }
2323
+ );
2324
+ registerToolScopes(
2325
+ /* @__PURE__ */ new Map([
2326
+ ["sqlite_audit_list_backups", ["admin", "full"]],
2327
+ ["sqlite_audit_get_backup", ["admin", "full"]],
2328
+ ["sqlite_audit_cleanup", ["admin", "full"]],
2329
+ ["sqlite_audit_diff_backup", ["admin", "full"]],
2330
+ ["sqlite_audit_restore_backup", ["admin", "full"]]
2331
+ ])
2332
+ );
2333
+ logger.info(
2334
+ "Registered audit backup tools: sqlite_audit_list_backups, sqlite_audit_get_backup, sqlite_audit_cleanup, sqlite_audit_diff_backup, sqlite_audit_restore_backup",
2335
+ { module: "AUDIT" }
2336
+ );
2337
+ }
2338
+
2339
+ // src/server/mcp-server.ts
2340
+ var proto = McpServer.prototype;
2341
+ if (typeof proto["createToolError"] === "function") {
2342
+ const originalCreateToolError = proto["createToolError"];
2343
+ proto["createToolError"] = function(errorMessage) {
2344
+ const result = originalCreateToolError.call(
2345
+ this,
2346
+ errorMessage
2347
+ );
2348
+ if (result.content?.[0]?.type === "text") {
2349
+ const rawError = result.content[0].text;
2350
+ if (rawError.includes("Input validation error")) {
2351
+ const cleanError = rawError.replace(
2352
+ /^MCP error -32602: Input validation error: /,
2353
+ "Validation error: "
2354
+ );
2355
+ const structured = {
2356
+ success: false,
2357
+ error: cleanError,
2358
+ code: "VALIDATION_ERROR",
2359
+ category: "validation" /* VALIDATION */
2360
+ };
2361
+ result.content[0].text = JSON.stringify(structured, null, 2);
2362
+ result.isError = true;
2363
+ }
2364
+ }
2365
+ return result;
2366
+ };
2367
+ }
2368
+ var DbMcpServer = class {
2369
+ server;
2370
+ adapters = /* @__PURE__ */ new Map();
2371
+ toolFilter;
2372
+ config;
2373
+ auditLogger = null;
2374
+ backupManager = null;
2375
+ auditInterceptor = null;
2376
+ auditInitPromise = null;
2377
+ constructor(config) {
2378
+ this.config = config;
2379
+ this.toolFilter = config.toolFilter ? parseToolFilter(config.toolFilter) : getToolFilterFromEnv();
2380
+ this.server = new McpServer(
2381
+ {
2382
+ name: config.name,
2383
+ version: config.version
2384
+ },
2385
+ {
2386
+ capabilities: {
2387
+ logging: {}
2388
+ },
2389
+ instructions: INSTRUCTIONS
2390
+ }
2391
+ );
2392
+ logger.info(getFilterSummary(this.toolFilter), { module: "FILTER" });
2393
+ logger.info("MCP Server initialized", {
2394
+ module: "SERVER",
2395
+ name: config.name,
2396
+ version: config.version,
2397
+ toolFilter: config.toolFilter ?? "none",
2398
+ capabilities: ["logging"]
2399
+ });
2400
+ registerBuiltInTools(
2401
+ this.server,
2402
+ this.adapters,
2403
+ this.config,
2404
+ this.toolFilter
2405
+ );
2406
+ registerHelpResources(this.server, this.toolFilter);
2407
+ const internalMcp = this.server;
2408
+ const handlers = internalMcp.server._requestHandlers;
2409
+ if (!handlers?.has("tools/list")) {
2410
+ throw new DbMcpError(
2411
+ "Security: SDK _requestHandlers monkey-patch failed. Scope filtering is disabled.",
2412
+ "SERVER_START_FAILED",
2413
+ "internal" /* INTERNAL */
2414
+ );
2415
+ }
2416
+ const originalListToolsHandler = handlers.get("tools/list");
2417
+ if (originalListToolsHandler) {
2418
+ handlers.set("tools/list", async (request, extra) => {
2419
+ const result = await originalListToolsHandler(request, extra);
2420
+ const authCtx = getAuthContext();
2421
+ if (authCtx && Array.isArray(result.tools)) {
2422
+ result.tools = result.tools.filter(
2423
+ (t) => scopesGrantToolAccess(authCtx.scopes, t.name)
2424
+ );
2425
+ }
2426
+ return result;
2427
+ });
2428
+ }
2429
+ if (config.audit?.enabled) {
2430
+ this.auditInitPromise = this.initializeAudit(config);
2431
+ }
2432
+ }
2433
+ /**
2434
+ * Register a database adapter
2435
+ */
2436
+ async registerAdapter(adapter, config) {
2437
+ const adapterId = `${adapter.type}:${config.connectionString ?? "default"}`;
2438
+ if (this.auditInitPromise) {
2439
+ await this.auditInitPromise;
2440
+ }
2441
+ await adapter.connect(config);
2442
+ this.adapters.set(adapterId, adapter);
2443
+ if (this.auditInterceptor && "setAuditInterceptor" in adapter) {
2444
+ adapter.setAuditInterceptor(this.auditInterceptor);
2445
+ this.auditInterceptor.setQueryAdapter({
2446
+ executeQuery: (sql, params) => adapter.executeReadQuery(sql, params)
2447
+ });
2448
+ }
2449
+ if (this.backupManager && "setBackupManager" in adapter) {
2450
+ adapter.setBackupManager(this.backupManager);
2451
+ }
2452
+ adapter.registerTools(this.server, this.toolFilter);
2453
+ adapter.registerResources(this.server);
2454
+ adapter.registerPrompts(this.server);
2455
+ const toolDefs = adapter.getToolDefinitions();
2456
+ const scopesMap = /* @__PURE__ */ new Map();
2457
+ for (const tool of toolDefs) {
2458
+ if (tool.requiredScopes) {
2459
+ scopesMap.set(tool.name, tool.requiredScopes);
2460
+ scopesMap.set(`sqlite_${tool.name}`, tool.requiredScopes);
2461
+ }
2462
+ }
2463
+ registerToolScopes(scopesMap);
2464
+ logger.info(`Registered adapter: ${adapter.name} (${adapterId})`, {
2465
+ module: "SERVER"
2466
+ });
2467
+ }
2468
+ /**
2469
+ * Get an adapter by ID
2470
+ */
2471
+ getAdapter(adapterId) {
2472
+ return this.adapters.get(adapterId);
2473
+ }
2474
+ /**
2475
+ * Get all registered adapters
2476
+ */
2477
+ getAdapters() {
2478
+ return this.adapters;
2479
+ }
2480
+ /**
2481
+ * Start the server with the configured transport
2482
+ */
2483
+ async start() {
2484
+ switch (this.config.transport) {
2485
+ case "stdio":
2486
+ await this.startStdio();
2487
+ break;
2488
+ case "http":
2489
+ await this.startHttp();
2490
+ break;
2491
+ default:
2492
+ throw new DbMcpError(
2493
+ `Unsupported transport: ${this.config.transport}`,
2494
+ "SERVER_START_FAILED",
2495
+ "config" /* CONFIGURATION */
2496
+ );
2497
+ }
2498
+ }
2499
+ /**
2500
+ * Start server with stdio transport
2501
+ */
2502
+ async startStdio() {
2503
+ const transport = new StdioServerTransport();
2504
+ await this.server.connect(transport);
2505
+ logger.info(`db-mcp server started (stdio transport)`, {
2506
+ module: "SERVER"
2507
+ });
2508
+ logger.warning(
2509
+ "SECURITY WARNING: Running in STDIO mode grants blanket 'admin' access to all tools. This implies full trust in the local client/agent. If this server is exposed over SSH or used by an untrusted client, consider using HTTP transport with OAuth (--oauth-enabled) for granular scope enforcement.",
2510
+ { module: "SERVER", code: "STDIO_ADMIN_RISK" }
2511
+ );
2512
+ }
2513
+ /**
2514
+ * Start server with HTTP transport (Streamable HTTP with SSE support)
2515
+ */
2516
+ async startHttp() {
2517
+ const { HttpTransport } = await import('./http-HWTUVFIA.js');
2518
+ const oauthConfig = {
2519
+ enabled: this.config.oauth?.enabled ?? false,
2520
+ authorizationServerUrl: this.config.oauth?.authorizationServerUrl ?? "",
2521
+ audience: this.config.oauth?.audience ?? this.config.name
2522
+ };
2523
+ if (this.config.oauth?.issuer !== void 0) {
2524
+ oauthConfig.issuer = this.config.oauth.issuer;
2525
+ }
2526
+ if (this.config.oauth?.jwksUri !== void 0) {
2527
+ oauthConfig.jwksUri = this.config.oauth.jwksUri;
2528
+ }
2529
+ if (this.config.oauth?.clockTolerance !== void 0) {
2530
+ oauthConfig.clockTolerance = this.config.oauth.clockTolerance;
2531
+ }
2532
+ if (this.config.oauth?.publicPaths !== void 0) {
2533
+ oauthConfig.publicPaths = this.config.oauth.publicPaths;
2534
+ }
2535
+ const transport = new HttpTransport({
2536
+ port: this.config.port ?? 3e3,
2537
+ ...this.config.host !== void 0 && { host: this.config.host },
2538
+ ...this.config.authToken !== void 0 && {
2539
+ authToken: this.config.authToken
2540
+ },
2541
+ ...this.config.enableHSTS !== void 0 && {
2542
+ enableHSTS: this.config.enableHSTS
2543
+ },
2544
+ oauth: oauthConfig,
2545
+ stateless: this.config.statelessHttp ?? false,
2546
+ noAuthEnforcement: this.config.noAuthEnforcement ?? false
2547
+ });
2548
+ await transport.initialize(this.server);
2549
+ await transport.start();
2550
+ const mode = this.config.statelessHttp ? "stateless" : "stateful";
2551
+ const host = this.config.host ?? "127.0.0.1";
2552
+ const port = String(this.config.port ?? 3e3);
2553
+ logger.info(
2554
+ `db-mcp server started (HTTP transport on ${host}:${port}, ${mode} mode)`,
2555
+ { module: "TRANSPORT", mode }
2556
+ );
2557
+ }
2558
+ /**
2559
+ * Gracefully shut down the server
2560
+ */
2561
+ async shutdown() {
2562
+ logger.info("Shutting down db-mcp server...", { module: "SERVER" });
2563
+ for (const [id, adapter] of this.adapters) {
2564
+ try {
2565
+ await adapter.disconnect();
2566
+ logger.info(`Disconnected adapter: ${id}`, { module: "SERVER" });
2567
+ } catch (error) {
2568
+ logger.error(`Error disconnecting adapter ${id}`, {
2569
+ module: "SERVER",
2570
+ error: error instanceof Error ? error : void 0
2571
+ });
2572
+ }
2573
+ }
2574
+ if (this.auditLogger) {
2575
+ await this.auditLogger.close();
2576
+ logger.info("Audit logger closed", { module: "AUDIT" });
2577
+ }
2578
+ if (this.backupManager) {
2579
+ await this.backupManager.flush();
2580
+ logger.info("Backup manager flushed", { module: "AUDIT" });
2581
+ }
2582
+ await this.server.close();
2583
+ logger.info("Server shutdown complete", { module: "SERVER" });
2584
+ }
2585
+ /**
2586
+ * Initialize the audit subsystem: logger, interceptor, backup manager,
2587
+ * sqlite://audit resource, and audit backup tools.
2588
+ */
2589
+ async initializeAudit(config) {
2590
+ const auditConfig = config.audit;
2591
+ if (!auditConfig?.enabled) return;
2592
+ this.auditLogger = new AuditLogger(auditConfig);
2593
+ await this.auditLogger.init();
2594
+ if (auditConfig.backup?.enabled) {
2595
+ this.backupManager = new BackupManager(
2596
+ auditConfig.backup,
2597
+ auditConfig.logPath
2598
+ );
2599
+ }
2600
+ this.auditInterceptor = createAuditInterceptor(
2601
+ this.auditLogger,
2602
+ this.backupManager ?? void 0
2603
+ );
2604
+ registerAuditResource(this.server, this.auditLogger, this.backupManager);
2605
+ if (this.backupManager && this.toolFilter.enabledGroups.has("admin")) {
2606
+ registerAuditBackupTools(this.server, this.backupManager, this.adapters);
2607
+ }
2608
+ logger.info(
2609
+ `Audit logging enabled (${auditConfig.logPath}, redact=${String(auditConfig.redact)}, reads=${String(auditConfig.auditReads)}, backup=${String(!!auditConfig.backup?.enabled)})`,
2610
+ { module: "AUDIT" }
2611
+ );
2612
+ }
2613
+ };
2614
+ function createServer(config) {
2615
+ return new DbMcpServer(config);
2616
+ }
2617
+ var DEFAULT_CONFIG = {
2618
+ name: NAME,
2619
+ version: VERSION,
2620
+ transport: "stdio",
2621
+ databases: []
2622
+ };
2623
+
2624
+ export { DEFAULT_CONFIG, DbMcpServer, createServer };