db-mcp 1.0.2 → 1.1.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 (241) hide show
  1. package/.gitattributes +2 -0
  2. package/LICENSE +1 -1
  3. package/README.md +244 -518
  4. package/dist/chunk-4IA3DB5C.js +135 -0
  5. package/dist/chunk-AOUL5SHS.js +930 -0
  6. package/dist/chunk-DWJXQEFA.js +13759 -0
  7. package/dist/chunk-DZQLDEQS.js +879 -0
  8. package/dist/chunk-S5IDDSSB.js +74 -0
  9. package/dist/chunk-Z2GFQU3G.js +363 -0
  10. package/dist/cli.d.ts +0 -7
  11. package/dist/cli.js +227 -183
  12. package/dist/http-VSB7DBJR.js +1541 -0
  13. package/dist/index.d.ts +842 -10
  14. package/dist/index.js +5 -17
  15. package/dist/sqlite-VHBA4ABV.js +600 -0
  16. package/dist/sqlite-native-D2LH5ZT7.js +2498 -0
  17. package/dist/worker-script.d.ts +2 -0
  18. package/dist/worker-script.js +126 -0
  19. package/package.json +26 -16
  20. package/playwright.config.ts +101 -0
  21. package/scripts/generate-server-instructions.ts +111 -0
  22. package/server.json +5 -6
  23. package/test-server/README.md +118 -0
  24. package/test-server/code-map.md +409 -0
  25. package/test-server/fixtures/sample.csv +6 -0
  26. package/test-server/reset-database.ps1 +373 -0
  27. package/test-server/sample.csv +11 -0
  28. package/test-server/test-agent-experience.md +243 -0
  29. package/test-server/test-database.sql +388 -0
  30. package/test-server/test-group-tools.md +861 -0
  31. package/test-server/test-help-resources.mjs +238 -0
  32. package/test-server/test-preflight.md +53 -0
  33. package/test-server/test-prompts.md +354 -0
  34. package/test-server/test-resources.md +245 -0
  35. package/test-server/test-tool-annotations.mjs +157 -0
  36. package/test-server/test-tools-advanced-1.md +517 -0
  37. package/test-server/test-tools-advanced-2.md +487 -0
  38. package/test-server/test-tools-codemode.md +629 -0
  39. package/test-server/test-tools.md +176 -0
  40. package/test-server/tool-reference.md +236 -0
  41. package/tsconfig.test.json +9 -0
  42. package/dist/adapters/DatabaseAdapter.d.ts +0 -141
  43. package/dist/adapters/DatabaseAdapter.d.ts.map +0 -1
  44. package/dist/adapters/DatabaseAdapter.js +0 -131
  45. package/dist/adapters/DatabaseAdapter.js.map +0 -1
  46. package/dist/adapters/sqlite/SchemaManager.d.ts +0 -58
  47. package/dist/adapters/sqlite/SchemaManager.d.ts.map +0 -1
  48. package/dist/adapters/sqlite/SchemaManager.js +0 -187
  49. package/dist/adapters/sqlite/SchemaManager.js.map +0 -1
  50. package/dist/adapters/sqlite/SqliteAdapter.d.ts +0 -161
  51. package/dist/adapters/sqlite/SqliteAdapter.d.ts.map +0 -1
  52. package/dist/adapters/sqlite/SqliteAdapter.js +0 -741
  53. package/dist/adapters/sqlite/SqliteAdapter.js.map +0 -1
  54. package/dist/adapters/sqlite/index.d.ts +0 -9
  55. package/dist/adapters/sqlite/index.d.ts.map +0 -1
  56. package/dist/adapters/sqlite/index.js +0 -8
  57. package/dist/adapters/sqlite/index.js.map +0 -1
  58. package/dist/adapters/sqlite/json-utils.d.ts +0 -100
  59. package/dist/adapters/sqlite/json-utils.d.ts.map +0 -1
  60. package/dist/adapters/sqlite/json-utils.js +0 -274
  61. package/dist/adapters/sqlite/json-utils.js.map +0 -1
  62. package/dist/adapters/sqlite/output-schemas.d.ts +0 -1187
  63. package/dist/adapters/sqlite/output-schemas.d.ts.map +0 -1
  64. package/dist/adapters/sqlite/output-schemas.js +0 -1337
  65. package/dist/adapters/sqlite/output-schemas.js.map +0 -1
  66. package/dist/adapters/sqlite/prompts.d.ts +0 -13
  67. package/dist/adapters/sqlite/prompts.d.ts.map +0 -1
  68. package/dist/adapters/sqlite/prompts.js +0 -605
  69. package/dist/adapters/sqlite/prompts.js.map +0 -1
  70. package/dist/adapters/sqlite/resources.d.ts +0 -13
  71. package/dist/adapters/sqlite/resources.d.ts.map +0 -1
  72. package/dist/adapters/sqlite/resources.js +0 -251
  73. package/dist/adapters/sqlite/resources.js.map +0 -1
  74. package/dist/adapters/sqlite/tools/admin.d.ts +0 -14
  75. package/dist/adapters/sqlite/tools/admin.d.ts.map +0 -1
  76. package/dist/adapters/sqlite/tools/admin.js +0 -788
  77. package/dist/adapters/sqlite/tools/admin.js.map +0 -1
  78. package/dist/adapters/sqlite/tools/core.d.ts +0 -25
  79. package/dist/adapters/sqlite/tools/core.d.ts.map +0 -1
  80. package/dist/adapters/sqlite/tools/core.js +0 -359
  81. package/dist/adapters/sqlite/tools/core.js.map +0 -1
  82. package/dist/adapters/sqlite/tools/fts.d.ts +0 -13
  83. package/dist/adapters/sqlite/tools/fts.d.ts.map +0 -1
  84. package/dist/adapters/sqlite/tools/fts.js +0 -347
  85. package/dist/adapters/sqlite/tools/fts.js.map +0 -1
  86. package/dist/adapters/sqlite/tools/geo.d.ts +0 -14
  87. package/dist/adapters/sqlite/tools/geo.d.ts.map +0 -1
  88. package/dist/adapters/sqlite/tools/geo.js +0 -252
  89. package/dist/adapters/sqlite/tools/geo.js.map +0 -1
  90. package/dist/adapters/sqlite/tools/index.d.ts +0 -30
  91. package/dist/adapters/sqlite/tools/index.d.ts.map +0 -1
  92. package/dist/adapters/sqlite/tools/index.js +0 -61
  93. package/dist/adapters/sqlite/tools/index.js.map +0 -1
  94. package/dist/adapters/sqlite/tools/json-helpers.d.ts +0 -14
  95. package/dist/adapters/sqlite/tools/json-helpers.d.ts.map +0 -1
  96. package/dist/adapters/sqlite/tools/json-helpers.js +0 -477
  97. package/dist/adapters/sqlite/tools/json-helpers.js.map +0 -1
  98. package/dist/adapters/sqlite/tools/json-operations.d.ts +0 -14
  99. package/dist/adapters/sqlite/tools/json-operations.d.ts.map +0 -1
  100. package/dist/adapters/sqlite/tools/json-operations.js +0 -839
  101. package/dist/adapters/sqlite/tools/json-operations.js.map +0 -1
  102. package/dist/adapters/sqlite/tools/stats.d.ts +0 -15
  103. package/dist/adapters/sqlite/tools/stats.d.ts.map +0 -1
  104. package/dist/adapters/sqlite/tools/stats.js +0 -1219
  105. package/dist/adapters/sqlite/tools/stats.js.map +0 -1
  106. package/dist/adapters/sqlite/tools/text.d.ts +0 -14
  107. package/dist/adapters/sqlite/tools/text.d.ts.map +0 -1
  108. package/dist/adapters/sqlite/tools/text.js +0 -1141
  109. package/dist/adapters/sqlite/tools/text.js.map +0 -1
  110. package/dist/adapters/sqlite/tools/vector.d.ts +0 -14
  111. package/dist/adapters/sqlite/tools/vector.d.ts.map +0 -1
  112. package/dist/adapters/sqlite/tools/vector.js +0 -613
  113. package/dist/adapters/sqlite/tools/vector.js.map +0 -1
  114. package/dist/adapters/sqlite/tools/virtual.d.ts +0 -13
  115. package/dist/adapters/sqlite/tools/virtual.d.ts.map +0 -1
  116. package/dist/adapters/sqlite/tools/virtual.js +0 -930
  117. package/dist/adapters/sqlite/tools/virtual.js.map +0 -1
  118. package/dist/adapters/sqlite/types.d.ts +0 -207
  119. package/dist/adapters/sqlite/types.d.ts.map +0 -1
  120. package/dist/adapters/sqlite/types.js +0 -186
  121. package/dist/adapters/sqlite/types.js.map +0 -1
  122. package/dist/adapters/sqlite-native/NativeSqliteAdapter.d.ts +0 -163
  123. package/dist/adapters/sqlite-native/NativeSqliteAdapter.d.ts.map +0 -1
  124. package/dist/adapters/sqlite-native/NativeSqliteAdapter.js +0 -748
  125. package/dist/adapters/sqlite-native/NativeSqliteAdapter.js.map +0 -1
  126. package/dist/adapters/sqlite-native/index.d.ts +0 -11
  127. package/dist/adapters/sqlite-native/index.d.ts.map +0 -1
  128. package/dist/adapters/sqlite-native/index.js +0 -11
  129. package/dist/adapters/sqlite-native/index.js.map +0 -1
  130. package/dist/adapters/sqlite-native/tools/spatialite.d.ts +0 -19
  131. package/dist/adapters/sqlite-native/tools/spatialite.d.ts.map +0 -1
  132. package/dist/adapters/sqlite-native/tools/spatialite.js +0 -628
  133. package/dist/adapters/sqlite-native/tools/spatialite.js.map +0 -1
  134. package/dist/adapters/sqlite-native/tools/transactions.d.ts +0 -12
  135. package/dist/adapters/sqlite-native/tools/transactions.d.ts.map +0 -1
  136. package/dist/adapters/sqlite-native/tools/transactions.js +0 -255
  137. package/dist/adapters/sqlite-native/tools/transactions.js.map +0 -1
  138. package/dist/adapters/sqlite-native/tools/window.d.ts +0 -12
  139. package/dist/adapters/sqlite-native/tools/window.d.ts.map +0 -1
  140. package/dist/adapters/sqlite-native/tools/window.js +0 -370
  141. package/dist/adapters/sqlite-native/tools/window.js.map +0 -1
  142. package/dist/auth/AuthorizationServerDiscovery.d.ts +0 -90
  143. package/dist/auth/AuthorizationServerDiscovery.d.ts.map +0 -1
  144. package/dist/auth/AuthorizationServerDiscovery.js +0 -204
  145. package/dist/auth/AuthorizationServerDiscovery.js.map +0 -1
  146. package/dist/auth/OAuthResourceServer.d.ts +0 -65
  147. package/dist/auth/OAuthResourceServer.d.ts.map +0 -1
  148. package/dist/auth/OAuthResourceServer.js +0 -121
  149. package/dist/auth/OAuthResourceServer.js.map +0 -1
  150. package/dist/auth/TokenValidator.d.ts +0 -60
  151. package/dist/auth/TokenValidator.d.ts.map +0 -1
  152. package/dist/auth/TokenValidator.js +0 -235
  153. package/dist/auth/TokenValidator.js.map +0 -1
  154. package/dist/auth/errors.d.ts +0 -74
  155. package/dist/auth/errors.d.ts.map +0 -1
  156. package/dist/auth/errors.js +0 -133
  157. package/dist/auth/errors.js.map +0 -1
  158. package/dist/auth/index.d.ts +0 -13
  159. package/dist/auth/index.d.ts.map +0 -1
  160. package/dist/auth/index.js +0 -15
  161. package/dist/auth/index.js.map +0 -1
  162. package/dist/auth/middleware.d.ts +0 -81
  163. package/dist/auth/middleware.d.ts.map +0 -1
  164. package/dist/auth/middleware.js +0 -291
  165. package/dist/auth/middleware.js.map +0 -1
  166. package/dist/auth/scopes.d.ts +0 -136
  167. package/dist/auth/scopes.d.ts.map +0 -1
  168. package/dist/auth/scopes.js +0 -349
  169. package/dist/auth/scopes.js.map +0 -1
  170. package/dist/auth/types.d.ts +0 -257
  171. package/dist/auth/types.d.ts.map +0 -1
  172. package/dist/auth/types.js +0 -8
  173. package/dist/auth/types.js.map +0 -1
  174. package/dist/cli.d.ts.map +0 -1
  175. package/dist/cli.js.map +0 -1
  176. package/dist/constants/ServerInstructions.d.ts +0 -45
  177. package/dist/constants/ServerInstructions.d.ts.map +0 -1
  178. package/dist/constants/ServerInstructions.js +0 -356
  179. package/dist/constants/ServerInstructions.js.map +0 -1
  180. package/dist/filtering/ToolConstants.d.ts +0 -34
  181. package/dist/filtering/ToolConstants.d.ts.map +0 -1
  182. package/dist/filtering/ToolConstants.js +0 -174
  183. package/dist/filtering/ToolConstants.js.map +0 -1
  184. package/dist/filtering/ToolFilter.d.ts +0 -82
  185. package/dist/filtering/ToolFilter.d.ts.map +0 -1
  186. package/dist/filtering/ToolFilter.js +0 -296
  187. package/dist/filtering/ToolFilter.js.map +0 -1
  188. package/dist/index.d.ts.map +0 -1
  189. package/dist/index.js.map +0 -1
  190. package/dist/server/McpServer.d.ts +0 -61
  191. package/dist/server/McpServer.d.ts.map +0 -1
  192. package/dist/server/McpServer.js +0 -270
  193. package/dist/server/McpServer.js.map +0 -1
  194. package/dist/transports/http.d.ts +0 -134
  195. package/dist/transports/http.d.ts.map +0 -1
  196. package/dist/transports/http.js +0 -516
  197. package/dist/transports/http.js.map +0 -1
  198. package/dist/transports/index.d.ts +0 -5
  199. package/dist/transports/index.d.ts.map +0 -1
  200. package/dist/transports/index.js +0 -5
  201. package/dist/transports/index.js.map +0 -1
  202. package/dist/types/index.d.ts +0 -380
  203. package/dist/types/index.d.ts.map +0 -1
  204. package/dist/types/index.js +0 -68
  205. package/dist/types/index.js.map +0 -1
  206. package/dist/utils/annotations.d.ts +0 -44
  207. package/dist/utils/annotations.d.ts.map +0 -1
  208. package/dist/utils/annotations.js +0 -77
  209. package/dist/utils/annotations.js.map +0 -1
  210. package/dist/utils/errors.d.ts +0 -155
  211. package/dist/utils/errors.d.ts.map +0 -1
  212. package/dist/utils/errors.js +0 -329
  213. package/dist/utils/errors.js.map +0 -1
  214. package/dist/utils/identifiers.d.ts +0 -121
  215. package/dist/utils/identifiers.d.ts.map +0 -1
  216. package/dist/utils/identifiers.js +0 -319
  217. package/dist/utils/identifiers.js.map +0 -1
  218. package/dist/utils/index.d.ts +0 -7
  219. package/dist/utils/index.d.ts.map +0 -1
  220. package/dist/utils/index.js +0 -7
  221. package/dist/utils/index.js.map +0 -1
  222. package/dist/utils/insightsManager.d.ts +0 -39
  223. package/dist/utils/insightsManager.d.ts.map +0 -1
  224. package/dist/utils/insightsManager.js +0 -63
  225. package/dist/utils/insightsManager.js.map +0 -1
  226. package/dist/utils/logger.d.ts +0 -189
  227. package/dist/utils/logger.d.ts.map +0 -1
  228. package/dist/utils/logger.js +0 -394
  229. package/dist/utils/logger.js.map +0 -1
  230. package/dist/utils/progress-utils.d.ts +0 -54
  231. package/dist/utils/progress-utils.d.ts.map +0 -1
  232. package/dist/utils/progress-utils.js +0 -74
  233. package/dist/utils/progress-utils.js.map +0 -1
  234. package/dist/utils/resourceAnnotations.d.ts +0 -36
  235. package/dist/utils/resourceAnnotations.d.ts.map +0 -1
  236. package/dist/utils/resourceAnnotations.js +0 -57
  237. package/dist/utils/resourceAnnotations.js.map +0 -1
  238. package/dist/utils/where-clause.d.ts +0 -41
  239. package/dist/utils/where-clause.d.ts.map +0 -1
  240. package/dist/utils/where-clause.js +0 -116
  241. package/dist/utils/where-clause.js.map +0 -1
@@ -0,0 +1,861 @@
1
+ ## core Group-Specific Testing (Native and WASM are identical)
2
+
3
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
4
+
5
+ ### Built-in Tools (3)
6
+
7
+ 1. server_info
8
+ 2. server_health
9
+ 3. list_adapters
10
+
11
+ ---
12
+
13
+ ### core Group Tools (10)
14
+
15
+ 4. sqlite_read_query
16
+ 5. sqlite_write_query
17
+ 6. sqlite_create_table
18
+ 7. sqlite_list_tables
19
+ 8. sqlite_describe_table
20
+ 9. sqlite_drop_table
21
+ 10. sqlite_get_indexes
22
+ 11. sqlite_create_index
23
+ 12. sqlite_drop_index
24
+ 13. sqlite_execute_code
25
+
26
+ **Checklist:**
27
+
28
+ 1. `server_info` → verify server name, version, adapter info present
29
+ 2. `server_health` → verify healthy status
30
+ 3. `list_adapters` → verify at least one adapter listed
31
+ 4. `sqlite_read_query({query: "SELECT COUNT(*) AS n FROM test_products"})` → `{rows: [{n: 16}]}`
32
+ 5. `sqlite_read_query({query: "SELECT name, price FROM test_products WHERE price > 500"})` → 1 result: `Laptop Pro 15` (1299.99)
33
+ 6. `sqlite_read_query({query: "SELECT COUNT(*) AS n FROM test_orders WHERE status = 'completed'"})` → `{rows: [{n: 8}]}`
34
+ 7. `sqlite_list_tables` → verify `test_products`, `test_orders`, `test_jsonb_docs`, `test_articles`, `test_users`, `test_measurements`, `test_embeddings`, `test_locations`, `test_categories`, `test_events` all present
35
+ 8. `sqlite_describe_table({table: "test_products"})` → verify columns include `id` (INTEGER), `name` (TEXT), `price` (REAL), `category` (TEXT)
36
+ 9. `sqlite_get_indexes({table: "test_orders"})` → verify `idx_orders_status` and `idx_orders_date` present
37
+ 10. `sqlite_create_table({table: "temp_core_test", columns: [{name: "id", type: "INTEGER", primaryKey: true}, {name: "name", type: "TEXT"}, {name: "value", type: "REAL"}]})` → success
38
+ 11. `sqlite_write_query({query: "INSERT INTO temp_core_test (id, name, value) VALUES (1, 'alpha', 10.5), (2, 'beta', 20.0)"})` → `{rowsAffected: 2}`
39
+ 12. `sqlite_read_query({query: "SELECT * FROM temp_core_test"})` → 2 rows
40
+ 13. `sqlite_create_index({table: "temp_core_test", columns: ["name"], indexName: "idx_temp_core_name"})` → success
41
+ 14. `sqlite_drop_table({table: "temp_core_test"})` → success
42
+
43
+ **Code mode testing:**
44
+
45
+ 15. `sqlite_execute_code({code: "const tables = await sqlite.core.listTables(); return tables;"})` → returns list of tables including `test_products`, `test_orders`, etc.
46
+ 16. `sqlite_execute_code({code: "const result = await sqlite.core.readQuery('SELECT COUNT(*) AS n FROM test_products'); return result;", readonly: true})` → `{rows: [{n: 16}]}` (verify readonly mode works)
47
+ 17. `sqlite_execute_code({code: "const result = await sqlite.core.writeQuery('INSERT INTO test_products VALUES (999, \"x\", \"x\", 0, \"x\", \"x\")'); return result;", readonly: true})` → `result` contains `{success: false, code: "CODEMODE_READONLY_VIOLATION"}` (code mode returns errors as values, not thrown exceptions)
48
+
49
+ **Error path testing:**
50
+
51
+ 🔴 18. `sqlite_describe_table({table: "nonexistent_table_xyz"})` → structured error response, NOT a raw MCP exception
52
+ 🔴 19. `sqlite_read_query({query: "SELECT * FROM nonexistent_table_xyz"})` → structured error mentioning table name
53
+ 🔴 20. `sqlite_get_indexes({table: "nonexistent_table_xyz"})` → report behavior (structured error or empty result)
54
+ 🔴 21. `sqlite_drop_table({table: "nonexistent_table_xyz"})` → structured error or `{existed: false}` style response
55
+
56
+ **Zod validation sweep** — call each tool with `{}` (empty params). Every response must be a handler error (`{success: false, error: "Validation error: ..."}`) — NOT a raw MCP error frame:
57
+
58
+ 🔴 22. `sqlite_read_query({})` → handler error
59
+ 🔴 23. `sqlite_write_query({})` → handler error
60
+ 🔴 24. `sqlite_create_table({})` → handler error
61
+ 🔴 25. `sqlite_describe_table({})` → handler error
62
+ 🔴 26. `sqlite_drop_table({})` → handler error
63
+ 🔴 27. `sqlite_get_indexes({})` → handler error
64
+ 🔴 28. `sqlite_create_index({})` → handler error
65
+ 🔴 29. `sqlite_drop_index({})` → handler error
66
+ 🔴 30. `sqlite_execute_code({})` → handler error (has required `code` param)
67
+
68
+ ---
69
+
70
+ ## json Group-Specific Testing (Native and WASM are identical)
71
+
72
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
73
+
74
+ ### Built-in Tools (3)
75
+
76
+ 1. server_info
77
+ 2. server_health
78
+ 3. list_adapters
79
+
80
+ ### json Group Tools (24)
81
+
82
+ 4. sqlite_json_valid
83
+ 5. sqlite_json_extract
84
+ 6. sqlite_json_set
85
+ 7. sqlite_json_remove
86
+ 8. sqlite_json_type
87
+ 9. sqlite_json_array_length
88
+ 10. sqlite_json_array_append
89
+ 11. sqlite_json_keys
90
+ 12. sqlite_json_each
91
+ 13. sqlite_json_group_array
92
+ 14. sqlite_json_group_object
93
+ 15. sqlite_json_pretty
94
+ 16. sqlite_jsonb_convert
95
+ 17. sqlite_json_storage_info
96
+ 18. sqlite_json_normalize_column
97
+ 19. sqlite_json_insert
98
+ 20. sqlite_json_update
99
+ 21. sqlite_json_select
100
+ 22. sqlite_json_query
101
+ 23. sqlite_json_validate_path
102
+ 24. sqlite_json_merge
103
+ 25. sqlite_json_analyze_schema
104
+ 26. sqlite_create_json_collection
105
+ 27. sqlite_execute_code
106
+
107
+ **Test data reference (test_jsonb_docs):**
108
+
109
+ | id | doc.type | doc.author | doc.views | metadata.source | tags |
110
+ | --- | -------- | ---------- | --------- | --------------- | ------------------------------------ |
111
+ | 1 | article | Alice | 1250 | blog | ["database","tutorial","beginner"] |
112
+ | 2 | article | Bob | 890 | docs | ["json","advanced","sqlite"] |
113
+ | 3 | video | Carol | 5400 | youtube | ["mcp","protocol","ai"] |
114
+ | 4 | article | David | 670 | wiki | ["fts5","search","indexing"] |
115
+ | 5 | podcast | Eve | — | spotify | ["performance","tips","podcast"] |
116
+ | 6 | article | Frank | 2100 | medium | ["vector","embeddings","similarity"] |
117
+
118
+ Row 4 has nested path: `doc → nested → level1 → level2 = "deep value"`
119
+
120
+ **Checklist:**
121
+
122
+ 1. `sqlite_json_extract({table: "test_jsonb_docs", column: "doc", path: "$.author", whereClause: "id = 1"})` → result contains `"Alice"`
123
+ 2. `sqlite_json_extract({table: "test_jsonb_docs", column: "doc", path: "$.nested.level1.level2", whereClause: "id = 4"})` → result contains `"deep value"`
124
+ 3. `sqlite_json_keys({table: "test_jsonb_docs", column: "doc", whereClause: "id = 1"})` → keys include `type`, `title`, `author`, `views`, `rating`
125
+ 4. `sqlite_json_type({table: "test_jsonb_docs", column: "tags", whereClause: "id = 1"})` → `"array"`
126
+ 5. `sqlite_json_type({table: "test_jsonb_docs", column: "doc", whereClause: "id = 1"})` → `"object"`
127
+ 6. `sqlite_json_array_length({table: "test_jsonb_docs", column: "tags", whereClause: "id = 1"})` → `3`
128
+ 7. `sqlite_json_valid({json: "{\"type\":\"article\",\"title\":\"Getting Started with SQLite\",\"author\":\"Alice\",\"views\":1250,\"rating\":4.5}"})` → `{valid: true}`
129
+ 8. `sqlite_json_validate_path({path: "$.author"})` → valid
130
+ 9. `sqlite_json_pretty({json: "{\"type\":\"article\",\"author\":\"Alice\",\"views\":1250}"})` → formatted JSON with indentation
131
+ 10. `sqlite_json_each({table: "test_jsonb_docs", column: "tags", whereClause: "id = 1"})` → 3 expanded rows: `database`, `tutorial`, `beginner`
132
+ 11. `sqlite_json_analyze_schema({table: "test_jsonb_docs", column: "doc"})` → inferred schema with `type`, `author`, etc.
133
+ 12. `sqlite_json_merge({table: "test_jsonb_docs", column: "doc", mergeData: {"featured": true}, whereClause: "id = 999"})` → `{rowsAffected: 0}` (no matching rows, non-destructive test)
134
+ 13. `sqlite_json_select({table: "test_jsonb_docs", column: "doc", paths: ["$.author", "$.views"]})` → rows with author and views columns
135
+ 14. `sqlite_json_query({table: "test_jsonb_docs", column: "doc", filterPaths: {"$.type": "article"}})` → 4 rows (rows 1,2,4,6)
136
+ 15. `sqlite_json_storage_info({table: "test_jsonb_docs", column: "doc"})` → storage analysis
137
+ 16. `sqlite_json_group_array({table: "test_jsonb_docs", valueColumn: "json_extract(doc, '$.author')", allowExpressions: true})` → array of all authors `["Alice","Bob","Carol","David","Eve","Frank"]`
138
+ 17. `sqlite_json_group_object({table: "test_jsonb_docs", keyColumn: "json_extract(doc, '$.author')", valueColumn: "json_extract(doc, '$.views')", allowExpressions: true})` → object mapping authors to view counts
139
+ 18. `sqlite_jsonb_convert({table: "test_jsonb_docs", column: "doc", whereClause: "id = 1"})` → JSONB binary conversion result
140
+ 19. `sqlite_json_normalize_column({table: "test_jsonb_docs", column: "doc"})` → normalization report for the doc column
141
+
142
+ **Write operations (use temp tables):**
143
+
144
+ 20. `sqlite_create_json_collection({tableName: "temp_json_test"})` → creates table with JSON columns
145
+ 21. `sqlite_json_set` on `temp_json_test` → modify a JSON value
146
+ 22. `sqlite_json_update` on `temp_json_test` → update an existing key's value (distinct from json_set: update requires key to exist)
147
+ 23. `sqlite_json_insert` on `temp_json_test` → insert new key
148
+ 24. `sqlite_json_remove` on `temp_json_test` → remove a key
149
+ 25. `sqlite_json_array_append` on `temp_json_test` → add to array
150
+ 26. Cleanup: drop `temp_json_test`
151
+
152
+ **Code mode testing:**
153
+
154
+ 27. `sqlite_execute_code({code: "const result = await sqlite.json.extract({table: 'test_jsonb_docs', column: 'doc', path: '$.author', whereClause: 'id = 1'}); return result;"})` → result contains `"Alice"`
155
+ 28. `sqlite_execute_code({code: "const keys = await sqlite.json.keys({table: 'test_jsonb_docs', column: 'doc', whereClause: 'id = 1'}); return keys;"})` → keys include `type`, `title`, `author`
156
+
157
+ **Error path testing:**
158
+
159
+ 🔴 29. `sqlite_json_extract({table: "nonexistent_table_xyz", column: "doc", path: "$.x"})` → structured error
160
+ 🔴 30. `sqlite_json_extract({table: "test_jsonb_docs", column: "nonexistent_col", path: "$.x"})` → report behavior
161
+ 🔴 31. `sqlite_json_validate_path({path: "invalid path !@#"})` → report behavior
162
+
163
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
164
+
165
+ 🔴 32. `sqlite_json_valid({})` → handler error
166
+ 🔴 33. `sqlite_json_extract({})` → handler error
167
+ 🔴 34. `sqlite_json_set({})` → handler error
168
+ 🔴 35. `sqlite_json_remove({})` → handler error
169
+ 🔴 36. `sqlite_json_type({})` → handler error
170
+ 🔴 37. `sqlite_json_array_length({})` → handler error
171
+ 🔴 38. `sqlite_json_array_append({})` → handler error
172
+ 🔴 39. `sqlite_json_keys({})` → handler error
173
+ 🔴 40. `sqlite_json_each({})` → handler error
174
+ 🔴 41. `sqlite_json_group_array({})` → handler error
175
+ 🔴 42. `sqlite_json_group_object({})` → handler error
176
+ 🔴 43. `sqlite_json_pretty({})` → handler error
177
+ 🔴 44. `sqlite_jsonb_convert({})` → handler error
178
+ 🔴 45. `sqlite_json_storage_info({})` → handler error
179
+ 🔴 46. `sqlite_json_normalize_column({})` → handler error
180
+ 🔴 47. `sqlite_json_insert({})` → handler error
181
+ 🔴 48. `sqlite_json_update({})` → handler error
182
+ 🔴 49. `sqlite_json_select({})` → handler error
183
+ 🔴 50. `sqlite_json_query({})` → handler error
184
+ 🔴 51. `sqlite_json_validate_path({})` → handler error
185
+ 🔴 52. `sqlite_json_merge({})` → handler error
186
+ 🔴 53. `sqlite_json_analyze_schema({})` → handler error
187
+ 🔴 54. `sqlite_create_json_collection({})` → handler error
188
+
189
+ ---
190
+
191
+ ## text Group-Specific Testing (Native- 18 Tools)
192
+
193
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
194
+
195
+ > **FTS Testing Notes:** After `sqlite_fts_create`, always call `sqlite_fts_rebuild` before searching. `test_articles` searchable terms: `SQLite`, `database`, `JSON`, `FTS`, `vector`, `API`, `search`, `MCP`.
196
+
197
+ ### Built-in Tools (3)
198
+
199
+ 1. server_info
200
+ 2. server_health
201
+ 3. list_adapters
202
+
203
+ ### text Group Tools
204
+
205
+ 4. sqlite_regex_extract
206
+ 5. sqlite_regex_match
207
+ 6. sqlite_text_split
208
+ 7. sqlite_text_concat
209
+ 8. sqlite_text_replace
210
+ 9. sqlite_text_trim
211
+ 10. sqlite_text_case
212
+ 11. sqlite_text_substring
213
+ 12. sqlite_fuzzy_match
214
+ 13. sqlite_phonetic_match
215
+ 14. sqlite_text_normalize
216
+ 15. sqlite_text_validate
217
+ 16. sqlite_advanced_search
218
+ 17. sqlite_fts_create `[NATIVE ONLY]`
219
+ 18. sqlite_fts_search `[NATIVE ONLY]`
220
+ 19. sqlite_fts_rebuild `[NATIVE ONLY]`
221
+ 20. sqlite_fts_match_info `[NATIVE ONLY]`
222
+ 21. sqlite_execute_code
223
+
224
+ ### text Group Tools (WASM- 14 Tools)
225
+
226
+ Same as Native minus the 4 FTS5 tools (items 17-20). WASM mode excludes FTS5 tools entirely.
227
+
228
+ **Test data reference:**
229
+
230
+ - `test_articles` (8 rows): FTS5 searchable terms: `SQLite`, `database`, `JSON`, `FTS`, `vector`, `API`, `search`, `MCP`
231
+ - `test_users` (9 rows): Emails include `@example.com`, `@company.org`, `@gmail.com`, etc. One user (`testuser`) has `test.user@gmail.com`. Phone formats: `+1-555-0101`, `+44-20-7123-4567`, `+82-2-1234-5678`
232
+ - `test_products` row 16: `name = 'Café Décor Light'` — has accented characters for `strip_accents` testing
233
+
234
+ **Checklist:**
235
+
236
+ 1. `sqlite_regex_match({table: "test_users", column: "email", pattern: "@gmail\\.com$"})` → at least 1 result (`test.user@gmail.com`)
237
+ 2. `sqlite_regex_extract({table: "test_users", column: "email", pattern: "@([^.]+)\\.", groupIndex: 1})` → extract domain parts (example, company, startup, etc.)
238
+ 3. `sqlite_fuzzy_match({table: "test_products", column: "name", search: "Laptp", maxDistance: 3})` → results include `Laptop Pro 15`
239
+ 4. `sqlite_phonetic_match({table: "test_products", column: "name", search: "Labtop"})` → should find `Laptop Pro 15` via Soundex (both produce L131)
240
+ 5. `sqlite_text_validate({table: "test_users", column: "email", pattern: "email"})` → all 9 rows should be valid emails
241
+ 6. `sqlite_text_validate({table: "test_users", column: "phone", pattern: "phone"})` → report valid/invalid counts (one user has NULL phone)
242
+ 7. `sqlite_text_case({table: "test_users", column: "username", mode: "upper"})` → all usernames uppercased
243
+ 8. `sqlite_text_normalize({table: "test_products", column: "name", mode: "strip_accents"})` → `Café Décor Light` becomes `Cafe Decor Light`
244
+ 9. `sqlite_text_split({table: "test_users", column: "email", delimiter: "@"})` → each email split into local + domain parts
245
+ 10. `sqlite_text_concat({table: "test_users", columns: ["username", "email"], separator: " - "})` → concatenated strings
246
+ 11. `sqlite_text_replace({table: "test_users", column: "email", searchPattern: "@example.com", replaceWith: "@test.org", whereClause: "email LIKE '%@example.com'"})` → 1 row affected (write operation — revert with `searchPattern: "@test.org", replaceWith: "@example.com", whereClause: "email LIKE '%@test.org'"` afterward)
247
+ 12. `sqlite_text_trim({table: "test_users", column: "bio"})` → trimmed bios
248
+ 13. `sqlite_text_substring({table: "test_users", column: "username", start: 1, length: 4})` → first 4 chars of each username
249
+ 14. `sqlite_advanced_search({table: "test_products", column: "name", searchTerm: "keyboard", techniques: ["exact", "fuzzy", "phonetic"]})` → should find `Mechanical Keyboard`
250
+
251
+ **FTS5 tools `[NATIVE ONLY]`:**
252
+
253
+ 15. `sqlite_fts_create({sourceTable: "test_users", columns: ["username", "bio"], ftsTable: "temp_users_fts"})` → FTS5 virtual table created
254
+ 16. `sqlite_fts_rebuild({table: "temp_users_fts"})` → rebuild index before searching
255
+ 17. `sqlite_fts_search({table: "temp_users_fts", query: "test*"})` → verify results from test_users data (prefix query needed since no standalone "test" token exists)
256
+ 18. Cleanup: `sqlite_drop_table({table: "temp_users_fts"})` (drop the temp FTS table)
257
+ 19. `sqlite_fts_search({table: "test_articles_fts", query: "SQLite"})` → at least 1 result (article 1: "Introduction to SQLite")
258
+ 20. `sqlite_fts_search({table: "test_articles_fts", query: "MCP protocol"})` → matches article 3: "The Model Context Protocol Explained"
259
+ 21. `sqlite_fts_search({table: "test_articles_fts", query: "nonexistent_term_xyz"})` → 0 results
260
+ 22. `sqlite_fts_match_info({table: "test_articles_fts", query: "database"})` → match info with scoring data
261
+ 23. `sqlite_fts_rebuild({table: "test_articles_fts"})` → success
262
+
263
+ **Code mode testing:**
264
+
265
+ 24. `sqlite_execute_code({code: "const result = await sqlite.text.fuzzyMatch({table: 'test_products', column: 'name', search: 'Laptp', maxDistance: 3}); return result;"})` → results include `Laptop Pro 15`
266
+ 25. `sqlite_execute_code({code: "const result = await sqlite.text.regexMatch({table: 'test_users', column: 'email', pattern: '@gmail\\\\.com$'}); return result;"})` → at least 1 result
267
+
268
+ **Error path testing:**
269
+
270
+ 🔴 26. `sqlite_regex_match({table: "nonexistent_table_xyz", column: "x", pattern: "."})` → structured error
271
+ 🔴 27. `sqlite_fuzzy_match({table: "test_users", column: "nonexistent_col", search: "test"})` → structured error with code `COLUMN_NOT_FOUND`
272
+ 🔴 28. `sqlite_fts_search({table: "nonexistent_fts_xyz", query: "test"})` `[NATIVE ONLY]` → structured error
273
+
274
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
275
+
276
+ 🔴 29. `sqlite_regex_extract({})` → handler error
277
+ 🔴 30. `sqlite_regex_match({})` → handler error
278
+ 🔴 31. `sqlite_text_split({})` → handler error
279
+ 🔴 32. `sqlite_text_concat({})` → handler error
280
+ 🔴 33. `sqlite_text_replace({})` → handler error
281
+ 🔴 34. `sqlite_text_trim({})` → handler error
282
+ 🔴 35. `sqlite_text_case({})` → handler error
283
+ 🔴 36. `sqlite_text_substring({})` → handler error
284
+ 🔴 37. `sqlite_fuzzy_match({})` → handler error
285
+ 🔴 38. `sqlite_phonetic_match({})` → handler error
286
+ 🔴 39. `sqlite_text_normalize({})` → handler error
287
+ 🔴 40. `sqlite_text_validate({})` → handler error
288
+ 🔴 41. `sqlite_advanced_search({})` → handler error
289
+ 🔴 42. `sqlite_fts_create({})` `[NATIVE ONLY]` → handler error
290
+ 🔴 43. `sqlite_fts_search({})` `[NATIVE ONLY]` → handler error
291
+ 🔴 44. `sqlite_fts_rebuild({})` `[NATIVE ONLY]` → handler error
292
+ 🔴 45. `sqlite_fts_match_info({})` `[NATIVE ONLY]` → handler error
293
+
294
+ ---
295
+
296
+ ## stats Group-Specific Testing
297
+
298
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
299
+
300
+ ### Built-in Tools (3)
301
+
302
+ 1. server_info
303
+ 2. server_health
304
+ 3. list_adapters
305
+
306
+ ### stats Group Tools — (Native- 20 Tools)
307
+
308
+ 4. sqlite_stats_basic
309
+ 5. sqlite_stats_count
310
+ 6. sqlite_stats_group_by
311
+ 7. sqlite_stats_histogram
312
+ 8. sqlite_stats_percentile
313
+ 9. sqlite_stats_correlation
314
+ 10. sqlite_stats_top_n
315
+ 11. sqlite_stats_distinct
316
+ 12. sqlite_stats_summary
317
+ 13. sqlite_stats_frequency
318
+ 14. sqlite_stats_outliers
319
+ 15. sqlite_stats_regression
320
+ 16. sqlite_stats_hypothesis
321
+ 17. sqlite_window_row_number `[NATIVE ONLY]`
322
+ 18. sqlite_window_rank `[NATIVE ONLY]`
323
+ 19. sqlite_window_lag_lead `[NATIVE ONLY]`
324
+ 20. sqlite_window_running_total `[NATIVE ONLY]`
325
+ 21. sqlite_window_moving_avg `[NATIVE ONLY]`
326
+ 22. sqlite_window_ntile `[NATIVE ONLY]`
327
+ 23. sqlite_execute_code
328
+
329
+ ### stats Group Tools (WASM- 14 Tools)
330
+
331
+ Same as Native minus the 6 window function tools (items 17-22).
332
+
333
+ **Test data:** `test_measurements` (200 rows, sensor_id 1-5, columns: temperature, humidity, pressure, measured_at). `test_products` (16 rows, price column). `test_events` (100 rows, event_type column: page_view, click, purchase, login, search).
334
+
335
+ **Checklist:**
336
+
337
+ 1. `sqlite_stats_basic({table: "test_measurements", column: "temperature"})` → verify `count: 200`, `min`, `max`, `avg` present
338
+ 2. `sqlite_stats_count({table: "test_products"})` → `{count: 16}`
339
+ 3. `sqlite_stats_count({table: "test_products", column: "category", distinct: true})` → distinct category count (electronics, accessories, office = 3)
340
+ 4. `sqlite_stats_group_by({table: "test_measurements", groupByColumn: "sensor_id", valueColumn: "temperature", stat: "avg"})` → 5 groups (sensor_id 1-5) with average temperatures
341
+ 5. `sqlite_stats_histogram({table: "test_measurements", column: "temperature", buckets: 5})` → 5 buckets
342
+ 6. `sqlite_stats_percentile({table: "test_measurements", column: "temperature", percentiles: [25, 50, 75, 90]})` → 4 percentile values
343
+ 7. `sqlite_stats_correlation({table: "test_measurements", column1: "temperature", column2: "humidity"})` → correlation value between -1 and 1
344
+ 8. `sqlite_stats_top_n({table: "test_products", column: "price", n: 3, orderDirection: "desc"})` → top 3 most expensive products (Laptop Pro 15 at 1299.99 should be #1)
345
+ 9. `sqlite_stats_distinct({table: "test_locations", column: "city"})` → distinct city count (New York, Paris, London, Tokyo, Sydney, San Francisco = 6)
346
+ 10. `sqlite_stats_summary({table: "test_measurements", columns: ["temperature", "humidity", "pressure"]})` → summaries array with 3 entries
347
+ 11. `sqlite_stats_frequency({table: "test_events", column: "event_type"})` → distribution of page_view, click, purchase, login, search (each ~20)
348
+ 12. `sqlite_stats_outliers({table: "test_measurements", column: "temperature"})` → outlier detection result
349
+ 13. `sqlite_stats_regression({table: "test_measurements", xColumn: "temperature", yColumn: "humidity", degree: 1})` → regression coefficients
350
+ 14. `sqlite_stats_hypothesis({table: "test_measurements", column: "temperature", testType: "ttest_one", expectedMean: 25})` → verify `statistic` and `pValue` present
351
+
352
+ **Window functions `[NATIVE ONLY]`:**
353
+
354
+ 15. `sqlite_window_row_number({table: "test_products", orderBy: "price DESC"})` → products ranked by price
355
+ 16. `sqlite_window_rank({table: "test_products", orderBy: "price DESC"})` → rank with ties
356
+ 17. `sqlite_window_running_total({table: "test_orders", valueColumn: "total_price", orderBy: "order_date"})` → cumulative totals
357
+ 18. `sqlite_window_moving_avg({table: "test_measurements", valueColumn: "temperature", windowSize: 5, orderBy: "measured_at"})` → moving averages
358
+ 19. `sqlite_window_lag_lead({table: "test_orders", column: "total_price", direction: "lag", orderBy: "order_date"})` → lag/lead values
359
+ 20. `sqlite_window_ntile({table: "test_products", buckets: 4, orderBy: "price"})` → quartile assignments
360
+
361
+ **Code mode testing:**
362
+
363
+ 21. `sqlite_execute_code({code: "const result = await sqlite.stats.statsBasic({table: 'test_measurements', column: 'temperature'}); return result;"})` → verify `count: 200`, `min`, `max`, `avg` present
364
+ 22. `sqlite_execute_code({code: "const result = await sqlite.stats.statsPercentile({table: 'test_measurements', column: 'temperature', percentiles: [50]}); return result;"})` → median value
365
+
366
+ **Error path testing:**
367
+
368
+ 🔴 23. `sqlite_stats_basic({table: "nonexistent_table_xyz", column: "x"})` → structured error
369
+ 🔴 24. `sqlite_stats_basic({table: "test_products", column: "nonexistent_col"})` → report behavior
370
+ 🔴 25. `sqlite_stats_correlation({table: "test_products", column1: "name", column2: "description"})` → error about non-numeric columns
371
+
372
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
373
+
374
+ 🔴 26. `sqlite_stats_basic({})` → handler error
375
+ 🔴 27. `sqlite_stats_count({})` → handler error
376
+ 🔴 28. `sqlite_stats_group_by({})` → handler error
377
+ 🔴 29. `sqlite_stats_histogram({})` → handler error
378
+ 🔴 30. `sqlite_stats_percentile({})` → handler error
379
+ 🔴 31. `sqlite_stats_correlation({})` → handler error
380
+ 🔴 32. `sqlite_stats_top_n({})` → handler error
381
+ 🔴 33. `sqlite_stats_distinct({})` → handler error
382
+ 🔴 34. `sqlite_stats_summary({})` → handler error
383
+ 🔴 35. `sqlite_stats_frequency({})` → handler error
384
+ 🔴 36. `sqlite_stats_outliers({})` → handler error
385
+ 🔴 37. `sqlite_stats_regression({})` → handler error
386
+ 🔴 38. `sqlite_stats_hypothesis({})` → handler error
387
+ 🔴 39. `sqlite_window_row_number({})` `[NATIVE ONLY]` → handler error
388
+ 🔴 40. `sqlite_window_rank({})` `[NATIVE ONLY]` → handler error
389
+ 🔴 41. `sqlite_window_lag_lead({})` `[NATIVE ONLY]` → handler error
390
+ 🔴 42. `sqlite_window_running_total({})` `[NATIVE ONLY]` → handler error
391
+ 🔴 43. `sqlite_window_moving_avg({})` `[NATIVE ONLY]` → handler error
392
+ 🔴 44. `sqlite_window_ntile({})` `[NATIVE ONLY]` → handler error
393
+
394
+ ---
395
+
396
+ ## vector Group-Specific Testing (Native and WASM are identical)
397
+
398
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
399
+
400
+ > **Note:** Vector tools use pure JS computations (cosine, euclidean, dot product) with JSON-stored vectors — no native SQLite extension required. All 12 tools work identically in both WASM and Native modes.
401
+
402
+ ### Built-in Tools (3)
403
+
404
+ 1. server_info
405
+ 2. server_health
406
+ 3. list_adapters
407
+
408
+ ### vector Group Tools (12 Tools)
409
+
410
+ 4. sqlite_vector_create_table
411
+ 5. sqlite_vector_store
412
+ 6. sqlite_vector_batch_store
413
+ 7. sqlite_vector_search
414
+ 8. sqlite_vector_get
415
+ 9. sqlite_vector_delete
416
+ 10. sqlite_vector_count
417
+ 11. sqlite_vector_stats
418
+ 12. sqlite_vector_dimensions
419
+ 13. sqlite_vector_normalize
420
+ 14. sqlite_vector_distance
421
+ 15. sqlite_execute_code
422
+
423
+ **Test data:** `test_embeddings` (20 rows, 8-dim vectors, categories: tech, database, food, fitness, travel). Row 1: content="Machine learning fundamentals", embedding=[0.12, 0.45, -0.23, 0.78, 0.34, -0.56, 0.89, 0.01].
424
+
425
+ **Checklist:**
426
+
427
+ 1. `sqlite_vector_count({table: "test_embeddings"})` → `{count: 20}`
428
+ 2. `sqlite_vector_dimensions({table: "test_embeddings"})` → `{dimensions: 8}`
429
+ 3. `sqlite_vector_get({table: "test_embeddings", idColumn: "id", vectorColumn: "embedding", id: 1})` → verify content="Machine learning fundamentals", category="tech", embedding has 8 dimensions
430
+ 4. `sqlite_vector_search({table: "test_embeddings", vectorColumn: "embedding", queryVector: [0.12, 0.45, -0.23, 0.78, 0.34, -0.56, 0.89, 0.01], metric: "cosine", limit: 3})` → top result should be row 1 (exact match, \_similarity ≈ 1)
431
+ 5. `sqlite_vector_search({table: "test_embeddings", vectorColumn: "embedding", queryVector: [0.12, 0.45, -0.23, 0.78, 0.34, -0.56, 0.89, 0.01], metric: "cosine", limit: 3, whereClause: "category = 'database'"})` → only database category results
432
+ 6. `sqlite_vector_distance({vector1: [1, 0, 0], vector2: [0, 1, 0], metric: "cosine"})` → distance ≈ 1.0 (orthogonal vectors)
433
+ 7. `sqlite_vector_distance({vector1: [3, 4], vector2: [0, 0], metric: "euclidean"})` → distance = 5.0
434
+ 8. `sqlite_vector_normalize({vector: [3, 4]})` → `{normalized: [0.6, 0.8], originalMagnitude: 5}`
435
+ 9. `sqlite_vector_stats({table: "test_embeddings", vectorColumn: "embedding"})` → verify min/max/avg magnitude
436
+
437
+ **Write operations (use temp tables):**
438
+
439
+ 10. `sqlite_vector_create_table({tableName: "temp_vector_test", dimensions: 8, additionalColumns: [{name: "content", type: "TEXT"}, {name: "category", type: "TEXT"}]})` → success
440
+ 11. `sqlite_vector_store({table: "temp_vector_test", idColumn: "id", vectorColumn: "vector", id: 1, vector: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]})` → success (note: additional columns like content/category cannot be set via this tool)
441
+ 12. `sqlite_vector_batch_store({table: "temp_vector_test", idColumn: "id", vectorColumn: "vector", items: [{id: 2, vector: [0.11, 0.22, 0.33, 0.44, 0.55, 0.66, 0.77, 0.88]}, {id: 3, vector: [0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2]}]})` → `{stored: 2}`
442
+ 13. `sqlite_vector_count({table: "temp_vector_test"})` → `{count: 3}`
443
+ 14. `sqlite_vector_delete({table: "temp_vector_test", idColumn: "id", ids: [1]})` → success
444
+ 15. `sqlite_vector_count({table: "temp_vector_test"})` → `{count: 2}`
445
+ 16. Cleanup: `sqlite_drop_table({table: "temp_vector_test"})` (via core tools if needed)
446
+
447
+ **Code mode testing:**
448
+
449
+ 17. `sqlite_execute_code({code: "const result = await sqlite.vector.count({table: 'test_embeddings'}); return result;"})` → `{count: 20}`
450
+ 18. `sqlite_execute_code({code: "const result = await sqlite.vector.distance({vector1: [3, 4], vector2: [0, 0], metric: 'euclidean'}); return result;"})` → distance = 5.0
451
+
452
+ **Error path testing:**
453
+
454
+ 🔴 19. `sqlite_vector_search({table: "nonexistent_table_xyz", vectorColumn: "embedding", queryVector: [1,2,3], metric: "cosine"})` → structured error
455
+ 🔴 20. `sqlite_vector_distance({vector1: [1, 2, 3], vector2: [1, 2], metric: "cosine"})` → error about dimension mismatch
456
+
457
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
458
+
459
+ 🔴 21. `sqlite_vector_create_table({})` → handler error
460
+ 🔴 22. `sqlite_vector_store({})` → handler error
461
+ 🔴 23. `sqlite_vector_batch_store({})` → handler error
462
+ 🔴 24. `sqlite_vector_search({})` → handler error
463
+ 🔴 25. `sqlite_vector_get({})` → handler error
464
+ 🔴 26. `sqlite_vector_delete({})` → handler error
465
+ 🔴 27. `sqlite_vector_count({})` → handler error
466
+ 🔴 28. `sqlite_vector_stats({})` → handler error
467
+ 🔴 29. `sqlite_vector_dimensions({})` → handler error
468
+ 🔴 30. `sqlite_vector_normalize({})` → handler error
469
+ 🔴 31. `sqlite_vector_distance({})` → handler error
470
+
471
+ ---
472
+
473
+ ## admin Group-Specific Testing
474
+
475
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
476
+
477
+ > **CSV Testing Note:** Use `test-server/sample.csv` (columns: id, name, category, price, quantity, created_at) with **absolute paths** for CSV tool testing — relative paths resolve from IDE CWD.
478
+
479
+ ### Built-in Tools (3)
480
+
481
+ 1. server_info
482
+ 2. server_health
483
+ 3. list_adapters
484
+
485
+ ### admin Group Tools (Native- 34 Tools)
486
+
487
+ 4. sqlite_generate_series
488
+ 5. sqlite_create_view
489
+ 6. sqlite_list_views
490
+ 7. sqlite_drop_view
491
+ 8. sqlite_dbstat
492
+ 9. sqlite_vacuum
493
+ 10. sqlite_list_virtual_tables
494
+ 11. sqlite_virtual_table_info
495
+ 12. sqlite_drop_virtual_table
496
+ 13. sqlite_create_csv_table
497
+ 14. sqlite_analyze_csv_schema
498
+ 15. sqlite_create_rtree_table
499
+ 16. sqlite_create_series_table
500
+ 17. sqlite_backup
501
+ 18. sqlite_analyze
502
+ 19. sqlite_integrity_check
503
+ 20. sqlite_optimize
504
+ 21. sqlite_restore
505
+ 22. sqlite_verify_backup
506
+ 23. sqlite_index_stats
507
+ 24. sqlite_pragma_compile_options
508
+ 25. sqlite_pragma_database_list
509
+ 26. sqlite_pragma_optimize
510
+ 27. sqlite_pragma_settings
511
+ 28. sqlite_pragma_table_info
512
+ 29. sqlite_append_insight
513
+ 30. sqlite_transaction_begin
514
+ 31. sqlite_transaction_commit
515
+ 32. sqlite_transaction_rollback
516
+ 33. sqlite_transaction_savepoint
517
+ 34. sqlite_transaction_release
518
+ 35. sqlite_transaction_rollback_to
519
+ 36. sqlite_transaction_execute
520
+ 37. sqlite_execute_code
521
+
522
+ ---
523
+
524
+ ### admin Group Tools (WASM- 27 Tools)
525
+
526
+ Same as Native minus the 7 transaction management tools (items 30-36).
527
+
528
+ **Checklist — Pragma & Inspection:**
529
+
530
+ 1. `sqlite_pragma_database_list` → verify database path matches `test.db`
531
+ 2. `sqlite_pragma_compile_options` → verify list of compile options returned
532
+ 3. `sqlite_pragma_compile_options({filter: "FTS"})` → filtered subset containing FTS-related options (`ENABLE_FTS3`, `ENABLE_FTS4`, `ENABLE_FTS5`)
533
+ 4. `sqlite_pragma_settings({pragma: "journal_mode"})` → `{value: "wal"}`
534
+ 5. `sqlite_pragma_table_info({table: "test_products"})` → verify columns: id, name, description, price, category, created_at
535
+ 6. `sqlite_index_stats` → verify index statistics for test database
536
+ 7. `sqlite_integrity_check` → `ok` result
537
+ 8. `sqlite_analyze` → success
538
+ 9. `sqlite_dbstat({summarize: true})` → per-table storage metrics
539
+
540
+ **Checklist — View Management:**
541
+
542
+ 10. `sqlite_create_view({viewName: "temp_view_orders", selectQuery: "SELECT product_id, COUNT(*) as order_count, SUM(total_price) as revenue FROM test_orders GROUP BY product_id"})` → success
543
+ 11. `sqlite_list_views` → verify `temp_view_orders` present
544
+ 12. `sqlite_drop_view({viewName: "temp_view_orders"})` → success
545
+
546
+ **Checklist — Virtual Tables:**
547
+
548
+ 13. `sqlite_list_virtual_tables` → verify `test_articles_fts` present (Native) or report behavior (WASM)
549
+ 14. `sqlite_virtual_table_info({tableName: "test_articles_fts"})` → verify module and column info (Native)
550
+ 15. `sqlite_generate_series({start: 1, stop: 5, step: 1})` → 5 values
551
+ 16. `sqlite_create_rtree_table({tableName: "temp_rtree_test", dimensions: 2})` → R-Tree virtual table created with 2D bounding box columns
552
+ 17. `sqlite_create_series_table({tableName: "temp_series_test", start: 1, stop: 10})` → regular table created with 10 rows (not a virtual table — see gotcha #15)
553
+ 18. Cleanup: `sqlite_drop_virtual_table({tableName: "temp_rtree_test"})` and `sqlite_drop_table({table: "temp_series_test"})` (series is a regular table — use `sqlite_drop_table`)
554
+
555
+ **Checklist — Backup/Restore:**
556
+
557
+ 19. `sqlite_backup({targetPath: "<absolute-path>/test-server/test-backup.db"})` → success with backup file info (⚠️ use absolute path — relative paths resolve from IDE CWD)
558
+ 20. `sqlite_verify_backup({backupPath: "<absolute-path>/test-server/test-backup.db"})` → integrity verified
559
+ 21. `sqlite_restore({sourcePath: "<absolute-path>/test-server/test-backup.db"})` → restore from backup, verify success
560
+ 22. Cleanup: note backup file location for manual removal if desired
561
+
562
+ **Checklist — Optimization:**
563
+
564
+ 23. `sqlite_vacuum` → success
565
+ 24. `sqlite_optimize` → success with optimization details
566
+ 25. `sqlite_pragma_optimize` → success (note: distinct from `sqlite_optimize` — this runs `PRAGMA optimize`)
567
+
568
+ **Checklist — Transaction Management `[NATIVE ONLY]`:**
569
+
570
+ 26. `sqlite_transaction_begin` → capture transaction ID
571
+ 27. `sqlite_transaction_rollback` → rollback entire transaction, verify success
572
+ 28. `sqlite_transaction_begin` → start a new transaction for savepoint tests
573
+ 29. `sqlite_transaction_savepoint({name: "sp1"})` → success
574
+ 30. `sqlite_transaction_rollback_to({name: "sp1"})` → success
575
+ 31. `sqlite_transaction_release({name: "sp1"})` → success (released savepoints cannot be rolled back to)
576
+ 32. `sqlite_transaction_commit` → success
577
+ 33. `sqlite_transaction_execute({statements: ["SELECT 1 AS test", "SELECT 2 AS test2"]})` → success with 2 statements executed
578
+
579
+ **Checklist — CSV:**
580
+
581
+ 34. `sqlite_analyze_csv_schema({filePath: "<absolute-path>/test-server/sample.csv"})` → inferred column types (⚠️ CSV requires absolute paths — see gotcha #14)
582
+ 35. `sqlite_create_csv_table({tableName: "temp_csv_test", filePath: "<absolute-path>/test-server/sample.csv"})` → virtual table created
583
+ 36. Cleanup: `sqlite_drop_virtual_table({tableName: "temp_csv_test"})`
584
+
585
+ **Checklist — Insights:**
586
+
587
+ 37. `sqlite_append_insight({insight: "Test insight for verification"})` → success
588
+
589
+ **Code mode testing:**
590
+
591
+ 38. `sqlite_execute_code({code: "const result = await sqlite.admin.integrityCheck(); return result;"})` → `ok` result
592
+ 39. `sqlite_execute_code({code: "const result = await sqlite.admin.pragmaSettings({pragma: 'journal_mode'}); return result;"})` → `{pragma: "journal_mode", value: "wal"}`
593
+
594
+ **Error path testing:**
595
+
596
+ 🔴 40. `sqlite_pragma_table_info({table: "nonexistent_table_xyz"})` → report behavior
597
+ 🔴 41. `sqlite_virtual_table_info({tableName: "nonexistent_table_xyz"})` → structured error
598
+ 🔴 42. `sqlite_verify_backup({backupPath: "nonexistent_file.db"})` → structured error
599
+ 🔴 43. `sqlite_transaction_execute({statements: ["INSERT INTO nonexistent_table VALUES (1)"]})` `[NATIVE ONLY]` → structured error with rollback info
600
+
601
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
602
+
603
+ 🔴 44. `sqlite_backup({})` → handler error
604
+ 🔴 45. `sqlite_restore({})` → handler error
605
+ 🔴 46. `sqlite_verify_backup({})` → handler error
606
+ 🔴 47. `sqlite_pragma_table_info({})` → handler error
607
+ 🔴 48. `sqlite_pragma_settings({})` → handler error (has required `pragma` param)
608
+ 🔴 49. `sqlite_append_insight({})` → handler error
609
+ 🔴 50. `sqlite_create_view({})` → handler error
610
+ 🔴 51. `sqlite_drop_view({})` → handler error
611
+ 🔴 52. `sqlite_virtual_table_info({})` → handler error
612
+ 🔴 53. `sqlite_drop_virtual_table({})` → handler error
613
+ 🔴 54. `sqlite_create_csv_table({})` → handler error
614
+ 🔴 55. `sqlite_analyze_csv_schema({})` → handler error
615
+ 🔴 56. `sqlite_create_rtree_table({})` → handler error
616
+ 🔴 57. `sqlite_create_series_table({})` → handler error
617
+ 🔴 58. `sqlite_generate_series({})` → handler error
618
+ 🔴 59. `sqlite_dbstat({})` → handler error (or success if no required params)
619
+ 🔴 60. `sqlite_transaction_begin({})` `[NATIVE ONLY]` → handler error (or success if no required params)
620
+ 🔴 61. `sqlite_transaction_execute({})` `[NATIVE ONLY]` → handler error
621
+ 🔴 62. `sqlite_transaction_savepoint({})` `[NATIVE ONLY]` → handler error
622
+ 🔴 63. `sqlite_transaction_release({})` `[NATIVE ONLY]` → handler error
623
+ 🔴 64. `sqlite_transaction_rollback_to({})` `[NATIVE ONLY]` → handler error
624
+
625
+ ---
626
+
627
+ ## geo Group-Specific Testing
628
+
629
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
630
+
631
+ ### Built-in Tools (3)
632
+
633
+ 1. server_info
634
+ 2. server_health
635
+ 3. list_adapters
636
+
637
+ ### geo Group Tools (Native- 12 Tools)
638
+
639
+ 4. sqlite_geo_distance
640
+ 5. sqlite_geo_nearby
641
+ 6. sqlite_geo_bounding_box
642
+ 7. sqlite_geo_cluster
643
+ 8. sqlite_spatialite_load `[NATIVE ONLY]`
644
+ 9. sqlite_spatialite_create_table `[NATIVE ONLY]`
645
+ 10. sqlite_spatialite_query `[NATIVE ONLY]`
646
+ 11. sqlite_spatialite_analyze `[NATIVE ONLY]`
647
+ 12. sqlite_spatialite_index `[NATIVE ONLY]`
648
+ 13. sqlite_spatialite_transform `[NATIVE ONLY]`
649
+ 14. sqlite_spatialite_import `[NATIVE ONLY]`
650
+ 15. sqlite_execute_code
651
+
652
+ ### geo Group Tools — WASM (5)
653
+
654
+ Only the Haversine-based tools: items 4-7 and code mode. SpatiaLite tools (items 8-14) are Native only.
655
+
656
+ **Test data:** `test_locations` (15 rows). Key coordinates:
657
+
658
+ | Name | City | Lat | Lng |
659
+ | ------------------ | ------------- | -------- | --------- |
660
+ | Central Park | New York | 40.7829 | -73.9654 |
661
+ | Eiffel Tower | Paris | 48.8584 | 2.2945 |
662
+ | Big Ben | London | 51.5007 | -0.1246 |
663
+ | Tokyo Tower | Tokyo | 35.6586 | 139.7454 |
664
+ | Sydney Opera House | Sydney | -33.8568 | 151.2153 |
665
+ | Golden Gate Bridge | San Francisco | 37.8199 | -122.4783 |
666
+
667
+ **Checklist (Haversine tools — Native & WASM):**
668
+
669
+ 1. `sqlite_geo_distance({lat1: 40.7829, lon1: -73.9654, lat2: 48.8584, lon2: 2.2945})` → NYC to Paris ≈ 5,837 km (verify within ±50 km)
670
+ 2. `sqlite_geo_distance({lat1: 40.7829, lon1: -73.9654, lat2: 37.8199, lon2: -122.4783})` → NYC to SF ≈ 4,130 km
671
+ 3. `sqlite_geo_nearby({table: "test_locations", latColumn: "latitude", lonColumn: "longitude", centerLat: 40.7580, centerLon: -73.9855, radius: 10})` → should find NYC locations (Central Park, Empire State Building, Times Square) — 3 results
672
+ 4. `sqlite_geo_nearby({table: "test_locations", latColumn: "latitude", lonColumn: "longitude", centerLat: 48.8584, centerLon: 2.2945, radius: 10})` → should find Paris locations (Eiffel Tower, Louvre, Notre-Dame) — 3 results
673
+ 5. `sqlite_geo_bounding_box({table: "test_locations", latColumn: "latitude", lonColumn: "longitude", minLat: 35, maxLat: 55, minLon: -130, maxLon: -70})` → US locations (NYC 3 + SF 1 = 4)
674
+ 6. `sqlite_geo_cluster({table: "test_locations", latColumn: "latitude", lonColumn: "longitude", gridSize: 5})` → ~5 clusters grouping by city proximity
675
+
676
+ **SpatiaLite tools `[NATIVE ONLY]`:**
677
+
678
+ 7. `sqlite_spatialite_load` → load SpatiaLite extension, verify version
679
+ 8. `sqlite_spatialite_create_table({tableName: "temp_spatial_test", geometryColumn: "geom", geometryType: "POINT", srid: 4326, additionalColumns: [{name: "name", type: "TEXT"}]})` → success
680
+ 9. `sqlite_spatialite_import({tableName: "temp_spatial_test", format: "wkt", data: "POINT(-73.9654 40.7829)", additionalData: {name: "Test Point"}})` → success
681
+ 10. `sqlite_spatialite_query({query: "SELECT name, AsText(geom) as geom_text FROM temp_spatial_test"})` → WKT geometry returned
682
+ 11. `sqlite_spatialite_transform({operation: "buffer", geometry1: "POINT(-73.9654 40.7829)", distance: 0.01, srid: 4326})` → buffered polygon
683
+ 12. `sqlite_spatialite_index({tableName: "temp_spatial_test", geometryColumn: "geom", action: "create"})` → R-Tree index created
684
+ 13. `sqlite_spatialite_analyze({analysisType: "spatial_extent", sourceTable: "temp_spatial_test", geometryColumn: "geom"})` → spatial extent
685
+ 14. Cleanup: drop `temp_spatial_test`
686
+
687
+ **Code mode testing:**
688
+
689
+ 15. `sqlite_execute_code({code: "const result = await sqlite.geo.distance({lat1: 40.7829, lon1: -73.9654, lat2: 48.8584, lon2: 2.2945}); return result;"})` → NYC to Paris ≈ 5,837 km
690
+ 16. `sqlite_execute_code({code: "const result = await sqlite.geo.nearby({table: 'test_locations', latColumn: 'latitude', lonColumn: 'longitude', centerLat: 40.758, centerLon: -73.9855, radius: 10}); return result;"})` → NYC locations
691
+
692
+ **Error path testing:**
693
+
694
+ 🔴 17. `sqlite_geo_nearby({table: "nonexistent_table_xyz", latColumn: "lat", lonColumn: "lng", centerLat: 0, centerLon: 0, radius: 100})` → structured error
695
+ 🔴 18. `sqlite_geo_distance({lat1: 91, lon1: 0, lat2: 0, lon2: 0})` → must return `{success: false, error: "Invalid lat1: 91. Must be between -90 and 90."}` — structured handler error, NOT a raw MCP error. If this returns a raw MCP `-32602`, it is a Zod `.min()/.max()` refinement leak bug (see `test-tools.md` refinement leak pattern).
696
+
697
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
698
+
699
+ 🔴 19. `sqlite_geo_distance({})` → handler error
700
+ 🔴 20. `sqlite_geo_nearby({})` → handler error
701
+ 🔴 21. `sqlite_geo_bounding_box({})` → handler error
702
+ 🔴 22. `sqlite_geo_cluster({})` → handler error
703
+ 🔴 23. `sqlite_spatialite_create_table({})` `[NATIVE ONLY]` → handler error
704
+ 🔴 24. `sqlite_spatialite_query({})` `[NATIVE ONLY]` → handler error
705
+ 🔴 25. `sqlite_spatialite_analyze({})` `[NATIVE ONLY]` → handler error
706
+ 🔴 26. `sqlite_spatialite_index({})` `[NATIVE ONLY]` → handler error
707
+ 🔴 27. `sqlite_spatialite_transform({})` `[NATIVE ONLY]` → handler error
708
+ 🔴 28. `sqlite_spatialite_import({})` `[NATIVE ONLY]` → handler error
709
+
710
+ ---
711
+
712
+ ## introspection Group-Specific Testing (Native and WASM are identical)
713
+
714
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
715
+
716
+ > **Note:** All introspection tools are **read-only**. The test database has one FK relationship (`test_orders.product_id → test_products.id`) and a deliberately redundant index (`idx_orders_status` is a prefix of `idx_orders_status_date`) for audit testing.
717
+
718
+ ### Built-in Tools (3)
719
+
720
+ 1. server_info
721
+ 2. server_health
722
+ 3. list_adapters
723
+
724
+ ### introspection Group Tools (10)
725
+
726
+ 4. sqlite_dependency_graph
727
+ 5. sqlite_topological_sort
728
+ 6. sqlite_cascade_simulator
729
+ 7. sqlite_schema_snapshot
730
+ 8. sqlite_constraint_analysis
731
+ 9. sqlite_migration_risks
732
+ 10. sqlite_storage_analysis
733
+ 11. sqlite_index_audit
734
+ 12. sqlite_query_plan
735
+ 13. sqlite_execute_code
736
+
737
+ **Checklist — Graph Analysis:**
738
+
739
+ 1. `sqlite_dependency_graph({})` → nodes ≥ 2, edges includes `test_orders → test_products` (FK); stats.totalRelationships ≥ 1
740
+ 2. `sqlite_topological_sort({})` → order array with `test_products` before `test_orders` (FK dependency); hasCycles = false
741
+ 3. `sqlite_cascade_simulator({table: "test_products"})` → affectedTables includes `test_orders` (FK dependent)
742
+ 4. `sqlite_cascade_simulator({table: "test_measurements"})` → affectedTables is empty (no tables reference it via FK)
743
+ 5. `sqlite_cascade_simulator({table: "nonexistent_table_xyz"})` → `{success: false, error: "..."}`
744
+
745
+ **Checklist — Schema Analysis:**
746
+
747
+ 6. `sqlite_schema_snapshot({})` → snapshot.tables ≥ 11 (10 test\_ tables + FTS virtual); stats.indexes ≥ 4; generatedAt present
748
+ 7. `sqlite_constraint_analysis({})` → findings array; summary.totalFindings ≥ 0; summary.byType and bySeverity objects present
749
+ 8. `sqlite_migration_risks({statements: ["DROP TABLE test_products"]})` → risks array non-empty; risk category includes data_loss or destructive
750
+ 9. `sqlite_migration_risks({statements: ["ALTER TABLE test_users ADD COLUMN age INTEGER"]})` → low risk
751
+ 10. `sqlite_migration_risks({statements: ["CREATE TABLE new_table (id INTEGER PRIMARY KEY)", "DROP TABLE test_products"]})` → summary.totalStatements = 2; summary.highestRisk ≥ "high"
752
+
753
+ **Checklist — Diagnostics:**
754
+
755
+ 11. `sqlite_storage_analysis({})` → database.pageSize > 0, database.totalPages > 0, database.totalSizeBytes = pageSize × totalPages; recommendations array present
756
+ 12. `sqlite_storage_analysis({})` → tables array contains "test_measurements" (largest by row count); verify each entry has name, sizeBytes, rowCount
757
+ 13. `sqlite_index_audit({})` → findings array present; summary has redundant, missingFk, total fields
758
+ 14. `sqlite_index_audit({})` → findings includes type="redundant" for `idx_orders_status` (prefix of `idx_orders_status_date`)
759
+ 15. `sqlite_query_plan({sql: "SELECT * FROM test_products WHERE category = 'electronics'"})` → plan array non-empty; analysis.fullScans may or may not include test_products (idx_products_category exists)
760
+ 16. `sqlite_query_plan({sql: "SELECT * FROM test_orders WHERE status = 'completed'"})` → analysis.indexScans present (idx_orders_status exists)
761
+ 17. `sqlite_query_plan({sql: "SELECT * FROM test_products WHERE name = 'Laptop Pro 15'"})` → analysis.fullScans includes test_products (no index on name); suggestions non-empty
762
+ 18. `sqlite_query_plan({sql: "WITH recent AS (SELECT * FROM test_orders ORDER BY order_date DESC LIMIT 5) SELECT * FROM recent"})` → plan contains CTE-related entries
763
+
764
+ **Code mode testing (params only accessible via code mode):**
765
+
766
+ 19. `sqlite_execute_code({code: "const result = await sqlite.introspection.schemaSnapshot({}); return { tableCount: result.snapshot.tables.length, hasStats: !!result.stats };"})` → tableCount ≥ 11, hasStats = true
767
+ 20. `sqlite_execute_code({code: "const result = await sqlite.introspection.queryPlan({sql: 'SELECT * FROM test_products WHERE category = \u0027electronics\u0027'}); return result;"})` → plan array present
768
+ 21. `sqlite_execute_code({code: "const result = await sqlite.introspection.schemaSnapshot({sections: ['tables']}); return { hasTables: !!result.snapshot.tables, hasViews: !!result.snapshot.views, hasIndexes: !!result.snapshot.indexes };"})` → hasTables=true, hasViews=false, hasIndexes=false
769
+ 22. `sqlite_execute_code({code: "const result = await sqlite.introspection.schemaSnapshot({compact: true}); const t = result.snapshot.tables[0]; return { name: t.name, hasColumns: !!t.columns };"})` → hasColumns=false (compact omits columns)
770
+ 23. `sqlite_execute_code({code: "const result = await sqlite.introspection.constraintAnalysis({table: 'test_orders'}); return { count: result.findings.length, allTestOrders: result.findings.every(f => f.table === 'test_orders') };"})` → allTestOrders=true
771
+ 24. `sqlite_execute_code({code: "const result = await sqlite.introspection.constraintAnalysis({checks: ['unindexed_fk']}); return { count: result.findings.length, allUnindexedFk: result.findings.every(f => f.type === 'unindexed_fk') };"})` → allUnindexedFk=true
772
+ 25. `sqlite_execute_code({code: "const result = await sqlite.introspection.storageAnalysis({includeTableDetails: false}); return { hasTables: !!result.tables, hasDatabase: !!result.database };"})` → hasTables=false, hasDatabase=true
773
+ 26. `sqlite_execute_code({code: "const result = await sqlite.introspection.indexAudit({table: 'test_orders'}); return { count: result.findings.length, allTestOrders: result.findings.every(f => f.table === 'test_orders') };"})` → allTestOrders=true
774
+ 27. `sqlite_execute_code({code: "const result = await sqlite.introspection.topologicalSort({direction: 'drop'}); return { direction: result.direction, first: result.order[0], last: result.order[result.order.length - 1] };"})` → direction="drop", test_orders before test_products in order
775
+
776
+ **Error path testing:**
777
+
778
+ 🔴 28. `sqlite_query_plan({sql: "DELETE FROM test_products WHERE id = 1"})` → `{success: false, error: "...only SELECT..."}` (non-SELECT rejected)
779
+ 🔴 29. `sqlite_query_plan({})` → Zod validation error (missing required `sql`). Must be handler error, NOT raw MCP error.
780
+ 🔴 30. `sqlite_cascade_simulator({})` → Zod validation error (missing required `table`)
781
+ 🔴 31. `sqlite_migration_risks({statements: []})` → report behavior for empty array
782
+ 🔴 32. `sqlite_execute_code({code: "return await sqlite.introspection.storageAnalysis({limit: 0});", readonly: true})` → Zod validation error (min: 1) — `limit` param only accessible via code mode
783
+
784
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
785
+
786
+ 🔴 33. `sqlite_dependency_graph({})` → handler error (or success if no required params)
787
+ 🔴 34. `sqlite_topological_sort({})` → handler error (or success if no required params)
788
+ 🔴 35. `sqlite_cascade_simulator({})` → handler error
789
+ 🔴 36. `sqlite_schema_snapshot({})` → handler error (or success if no required params)
790
+ 🔴 37. `sqlite_constraint_analysis({})` → handler error (or success if no required params)
791
+ 🔴 38. `sqlite_migration_risks({})` → handler error
792
+ 🔴 39. `sqlite_storage_analysis({})` → handler error (or success if no required params)
793
+ 🔴 40. `sqlite_index_audit({})` → handler error (or success if no required params)
794
+ 🔴 41. `sqlite_query_plan({})` → handler error
795
+
796
+ ---
797
+
798
+ ## migration Group-Specific Testing (Native and WASM are identical)
799
+
800
+ > **Instructions**: Execute every numbered checklist item with the exact inputs shown. Compare responses against the expected results. Report any deviation. These are the minimum-bar tests that must pass every run — freeform testing comes after.
801
+
802
+ > **Note:** Migration tools write a `_mcp_migrations` tracking table. Use `temp_*` naming for test migrations. Attempt to remove all `temp_*` tables upon completion. If DROP fails due to a database lock, note the leftover tables and move on — they are inert and will be cleaned up on next database regeneration
803
+
804
+ ### Built-in Tools (3)
805
+
806
+ 1. server_info
807
+ 2. server_health
808
+ 3. list_adapters
809
+
810
+ ### migration Group Tools (7)
811
+
812
+ 4. sqlite_migration_init
813
+ 5. sqlite_migration_record
814
+ 6. sqlite_migration_apply
815
+ 7. sqlite_migration_rollback
816
+ 8. sqlite_migration_history
817
+ 9. sqlite_migration_status
818
+ 10. sqlite_execute_code
819
+
820
+ **Checklist — Full Lifecycle:**
821
+
822
+ > **Note**: `sqlite_migration_record` is for **externally-applied** migrations — it records them as `applied` immediately without executing the SQL. `sqlite_migration_apply` **executes SQL AND records** the migration atomically. They are NOT a two-step record→apply workflow.
823
+
824
+ 1. `sqlite_migration_init({})` → success; creates `_mcp_migrations` table
825
+ 2. `sqlite_migration_status({})` → initialized state, zero counts
826
+ 3. `sqlite_migration_apply({version: "001_create_temp_table", migrationSql: "CREATE TABLE temp_migration_test (id INTEGER PRIMARY KEY, value TEXT)", rollbackSql: "DROP TABLE IF EXISTS temp_migration_test", description: "Create temp table for migration testing", sourceSystem: "agent", appliedBy: "antigravity"})` → migration executed and recorded as applied; verify `temp_migration_test` exists via `sqlite_list_tables`
827
+ 4. `sqlite_migration_status({})` → shows 1 applied migration, latestVersion = "001_create_temp_table"
828
+ 5. `sqlite_migration_record({version: "002_external_change", migrationSql: "ALTER TABLE temp_migration_test ADD COLUMN extra TEXT", description: "Record external schema change", sourceSystem: "agent", appliedBy: "antigravity"})` → migration recorded as applied (SQL NOT executed — record-only)
829
+ 6. `sqlite_migration_history({})` → shows both migrations with `applied` status, ordered by id DESC
830
+ 7. `sqlite_migration_rollback({version: "001_create_temp_table"})` → rollback executed; `temp_migration_test` dropped; status = "rolled_back"
831
+ 8. `sqlite_migration_history({})` → shows 001=rolled_back, 002=applied
832
+ 9. `sqlite_migration_status({})` → verify overall state: 1 applied, 1 rolled_back
833
+ 10. `sqlite_migration_apply({version: "003_duplicate_test", migrationSql: "CREATE TABLE temp_migration_test (id INTEGER PRIMARY KEY, value TEXT)"})` → duplicate SQL detection (same hash as 001) → `{success: false, error: "Duplicate migration: ..."}`
834
+
835
+ **Code mode testing:**
836
+
837
+ 11. `sqlite_execute_code({code: "const result = await sqlite.migration.migrationStatus(); return result;"})` → current migration state
838
+ 12. `sqlite_execute_code({code: "const result = await sqlite.migration.migrationHistory(); return result;"})` → migration history list
839
+
840
+ **Error path testing:**
841
+
842
+ 🔴 13. `sqlite_migration_apply({version: "fail_test", migrationSql: "CREATE TABLE nonexistent_xyz.bad (id INT)"})` → structured error (SQL execution fails; recorded as status=failed)
843
+ 🔴 14. `sqlite_migration_record({})` → Zod validation error (missing required params) — must be handler error, NOT raw MCP error
844
+ 🔴 15. `sqlite_migration_rollback({version: "nonexistent_migration_xyz"})` → structured error `{success: false, error: "Migration not found: ..."}`
845
+ 🔴 16. `sqlite_migration_rollback({version: "002_external_change"})` → structured error (no rollback SQL was recorded with this migration)
846
+
847
+ **Zod validation sweep** — call each tool with `{}` (empty params). Must return handler error, NOT raw MCP error:
848
+
849
+ 🔴 17. `sqlite_migration_init({})` → handler error (or success if no required params)
850
+ 🔴 18. `sqlite_migration_record({})` → handler error
851
+ 🔴 19. `sqlite_migration_apply({})` → handler error
852
+ 🔴 20. `sqlite_migration_rollback({})` → handler error
853
+ 🔴 21. `sqlite_migration_history({})` → handler error (or success if no required params)
854
+ 🔴 22. `sqlite_migration_status({})` → handler error (or success if no required params)
855
+
856
+ **Cleanup:**
857
+
858
+ 23. Drop `_mcp_migrations` table: `sqlite_drop_table({table: "_mcp_migrations"})` (writeQuery rejects DROP TABLE)
859
+ 24. Drop `temp_migration_test` if still exists: `sqlite_drop_table({table: "temp_migration_test"})`
860
+ 25. Verify cleanup via `sqlite_list_tables`
861
+ 26. Attempt to remove all `temp_*` tables. If DROP fails due to a database lock, move on — they are inert and will be cleaned up on next database regeneration