db-mcp 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.gitleaks.toml +9 -0
  2. package/.trivyignore +8 -0
  3. package/README.md +193 -120
  4. package/dist/{chunk-TVIZ3XJH.js → chunk-5Y42NPBP.js} +4168 -2411
  5. package/dist/{chunk-AOUL5SHS.js → chunk-645ZEFLA.js} +70 -20
  6. package/dist/chunk-OKOVZ5QE.js +28 -0
  7. package/dist/chunk-SFJQCNG7.js +131 -0
  8. package/dist/{chunk-FW7UCRLN.js → chunk-VIDSICEL.js} +3 -0
  9. package/dist/chunk-WBER5YY4.js +2053 -0
  10. package/dist/{chunk-4IA3DB5C.js → chunk-X3MUUOWM.js} +19 -2
  11. package/dist/{chunk-Z2GFQU3G.js → chunk-Z7C2TM4L.js} +114 -21
  12. package/dist/cli.js +79 -5
  13. package/dist/{http-VSB7DBJR.js → http-6KF4ULDI.js} +199 -77
  14. package/dist/index.d.ts +231 -8
  15. package/dist/index.js +6 -5
  16. package/dist/{sqlite-26V3Y4MK.js → sqlite-U5KSYQXK.js} +62 -138
  17. package/dist/{sqlite-native-5O7FZJGB.js → sqlite-native-JXMCFQBA.js} +575 -113
  18. package/dist/worker-script.js +34 -10
  19. package/logs/.gitkeep +1 -0
  20. package/mcp-config-example.json +83 -0
  21. package/package.json +9 -8
  22. package/playwright.config.ts +1 -1
  23. package/scripts/update-badges.ts +99 -0
  24. package/server.json +7 -5
  25. package/test-server/README.md +20 -23
  26. package/test-server/code-map.md +45 -33
  27. package/test-server/reset-database.ps1 +59 -17
  28. package/test-server/scripts/README.md +27 -0
  29. package/test-server/{test-help-resources.mjs → scripts/test-help-resources.mjs} +12 -5
  30. package/test-server/scripts/test-prompts.mjs +251 -0
  31. package/test-server/{test-tool-annotations.mjs → scripts/test-tool-annotations.mjs} +9 -4
  32. package/test-server/test-advanced/README.md +70 -0
  33. package/test-server/test-advanced/test-codemode-advanced-admin.md +174 -0
  34. package/test-server/test-advanced/test-codemode-advanced-core.md +193 -0
  35. package/test-server/test-advanced/test-codemode-advanced-geo.md +157 -0
  36. package/test-server/test-advanced/test-codemode-advanced-introspection.md +171 -0
  37. package/test-server/test-advanced/test-codemode-advanced-json.md +169 -0
  38. package/test-server/test-advanced/test-codemode-advanced-migration.md +160 -0
  39. package/test-server/test-advanced/test-codemode-advanced-stats.md +185 -0
  40. package/test-server/test-advanced/test-codemode-advanced-text.md +183 -0
  41. package/test-server/test-advanced/test-codemode-advanced-transactions.md +136 -0
  42. package/test-server/test-advanced/test-codemode-advanced-vector.md +141 -0
  43. package/test-server/test-codemode/README.md +121 -0
  44. package/test-server/test-codemode/test-codemode-admin.md +223 -0
  45. package/test-server/test-codemode/test-codemode-core.md +286 -0
  46. package/test-server/test-codemode/test-codemode-geo.md +177 -0
  47. package/test-server/test-codemode/test-codemode-introspection.md +190 -0
  48. package/test-server/test-codemode/test-codemode-json.md +237 -0
  49. package/test-server/test-codemode/test-codemode-migration.md +278 -0
  50. package/test-server/test-codemode/test-codemode-sandbox.md +413 -0
  51. package/test-server/test-codemode/test-codemode-stats.md +232 -0
  52. package/test-server/test-codemode/test-codemode-text.md +237 -0
  53. package/test-server/test-codemode/test-codemode-transactions.md +236 -0
  54. package/test-server/test-codemode/test-codemode-vector.md +244 -0
  55. package/test-server/test-codemode/test-codemode-wasm-degradation.md +394 -0
  56. package/test-server/test-database.sql +37 -1
  57. package/test-server/test-resources.md +43 -16
  58. package/test-server/test-tool-groups/README.md +100 -0
  59. package/test-server/test-tool-groups/test-admin-core.md +165 -0
  60. package/test-server/test-tool-groups/test-admin-extensions.md +133 -0
  61. package/test-server/{test-tools.md → test-tool-groups/test-core-data.md} +103 -17
  62. package/test-server/test-tool-groups/test-core-schema.md +240 -0
  63. package/test-server/test-tool-groups/test-geo-haversine.md +130 -0
  64. package/test-server/test-tool-groups/test-geo-spatialite.md +110 -0
  65. package/test-server/test-tool-groups/test-introspection-diagnostics.md +123 -0
  66. package/test-server/test-tool-groups/test-introspection-schema.md +133 -0
  67. package/test-server/test-tool-groups/test-json-read.md +219 -0
  68. package/test-server/test-tool-groups/test-json-write.md +157 -0
  69. package/test-server/test-tool-groups/test-migration.md +193 -0
  70. package/test-server/test-tool-groups/test-stats-advanced.md +118 -0
  71. package/test-server/test-tool-groups/test-stats-basic.md +156 -0
  72. package/test-server/test-tool-groups/test-text-advanced.md +169 -0
  73. package/test-server/test-tool-groups/test-text-basic.md +177 -0
  74. package/test-server/test-tool-groups/test-transactions.md +179 -0
  75. package/test-server/test-tool-groups/test-vector-read.md +130 -0
  76. package/test-server/test-tool-groups/test-vector-write.md +115 -0
  77. package/test-server/tool-reference.md +79 -58
  78. package/tsconfig.build.json +6 -0
  79. package/dist/chunk-RHVEZ42P.js +0 -873
  80. package/test-server/test-agent-experience.md +0 -243
  81. package/test-server/test-group-tools.md +0 -861
  82. package/test-server/test-tools-advanced-1.md +0 -517
  83. package/test-server/test-tools-advanced-2.md +0 -487
  84. package/test-server/test-tools-codemode.md +0 -629
@@ -0,0 +1,171 @@
1
+ # db-mcp Advanced Stress Test — [introspection]
2
+
3
+ > [!IMPORTANT]
4
+ > **Do not track progress in this file.** Track your test progress, coverage matrix, and findings in your internal task tracking system (artifact). However, you SHOULD edit this file to fix any factual errors, broken code, or incorrect assertions in the test prompts.
5
+ > If there is nothing to fix, don't update UNRELEASED.md.
6
+ > We're currently testing Native mode.
7
+
8
+ **Step 1:** Read `C:\Users\chris\Desktop\db-mcp\src\constants\server-instructions\gotchas.md` using `view_file`.
9
+
10
+ **Step 2:** Execute each numbered stress test below using `sqlite_execute_code` (code mode).
11
+
12
+ **Step 3:** The agent should update C:\Users\chris\Desktop\db-mcp\UNRELEASED.md with any/all changes/fixes.
13
+
14
+ ## WASM Mode
15
+
16
+ > When testing against a **WASM backend** (`--sqlite` / sql.js): All 9 introspection tools are fully WASM-compatible. No categories to skip.
17
+ >
18
+ > **Minor difference**: `schemaSnapshot` may report `test_articles_fts` in virtual tables but it is not queryable (FTS5 is unavailable in WASM). Treat its presence as expected but non-functional.
19
+
20
+ > **Code Mode Required:** Several optional params (`table`, `direction`, `sections`, `compact`, `checks`, `includeTableDetails`, `limit`) are defined in tool schemas but NOT exposed in MCP tool definitions. Use `sqlite_execute_code` to test these params via `sqlite.introspection.*` API.
21
+
22
+ ## Code Mode Execution
23
+
24
+ - **Code Over Docs**: Fix the handler code if standards (Structured Errors/Zod) are violated. Do NOT change docs/prompts to accommodate broken code.
25
+
26
+ All tests via `sqlite_execute_code`. Use `sqlite.introspection.*` for all introspection tools.
27
+ State persists across calls. All introspection tools are **read-only** — no cleanup needed.
28
+
29
+ ## Test Database Schema
30
+
31
+ | Table | Rows | Key Columns |
32
+ | ----------------- | ---- | ------------------------------------------------------ |
33
+ | test_products | 16 | id, name, price, category |
34
+ | test_orders | 20 | id, product_id (FK→test_products), total_price, status |
35
+ | test_measurements | 200 | id, sensor_id, temperature, humidity, pressure |
36
+
37
+ **Key FK:** `test_orders.product_id → test_products.id`
38
+ **Redundant index (intentional):** `idx_orders_status` is a prefix of `idx_orders_status_date` — used to test index audit.
39
+
40
+ ## Reporting Format
41
+
42
+ - ❌ Fail | ⚠️ Issue | 📦 Payload (monitor `metrics.tokenEstimate`) | ✅ Confirmed (inline only)
43
+
44
+ ## Structured Error Response Pattern
45
+
46
+ Handler error ✅ = JSON with `success` + `error`. MCP error ❌ = raw text, `isError: true`.
47
+
48
+ ---
49
+
50
+ ## introspection Group Tools (9)
51
+
52
+ 1. sqlite_dependency_graph
53
+ 2. sqlite_topological_sort
54
+ 3. sqlite_cascade_simulator
55
+ 4. sqlite_schema_snapshot
56
+ 5. sqlite_constraint_analysis
57
+ 6. sqlite_migration_risks
58
+ 7. sqlite_storage_analysis
59
+ 8. sqlite_index_audit
60
+ 9. sqlite_query_plan
61
+
62
+ ---
63
+
64
+ ### Category 1: Graph Analysis Edge Cases
65
+
66
+ **1.1 Full Dependency Graph**
67
+
68
+ 1. `sqlite.introspection.dependencyGraph({})` → full graph. Verify edge `test_orders → test_products` present.
69
+ 2. `sqlite.introspection.dependencyGraph({includeRowCounts: false})` → verify rowCount omitted.
70
+ 3. Verify `stats.rootTables` and `stats.leafTables` are populated and disjoint.
71
+
72
+ **1.2 Topological Sort Direction Stress**
73
+
74
+ 4. `sqlite.introspection.topologicalSort({direction: "create"})` → `test_products` BEFORE `test_orders`
75
+ 5. `sqlite.introspection.topologicalSort({direction: "drop"})` → `test_orders` BEFORE `test_products`
76
+ 6. Verify both directions list the same set of tables (just reordered)
77
+
78
+ **1.3 Cascade Chains**
79
+
80
+ 7. `sqlite.introspection.cascadeSimulator({table: "test_products"})` → affectedTables includes `test_orders` with FK action
81
+ 8. `sqlite.introspection.cascadeSimulator({table: "test_measurements"})` → affectedTables empty (leaf table)
82
+ 9. `sqlite.introspection.cascadeSimulator({table: "test_orders"})` → affectedTables empty (nothing references test_orders via FK)
83
+
84
+ ---
85
+
86
+ ### Category 2: Schema Snapshot Completeness
87
+
88
+ 10. `sqlite.introspection.schemaSnapshot({})` → full snapshot:
89
+ - tables ≥ 11 (10 regular + FTS virtual)
90
+ - indexes ≥ 4 (`idx_orders_status`, `idx_orders_date`, `idx_products_category`, `idx_orders_status_date`)
91
+ - generatedAt is valid ISO timestamp
92
+ 11. `sqlite.introspection.schemaSnapshot({sections: ["indexes"]})` → only indexes section. Tables absent.
93
+ 12. `sqlite.introspection.schemaSnapshot({sections: ["tables", "indexes"]})` → both sections present
94
+ 13. `sqlite.introspection.schemaSnapshot({compact: true})` → tables present but columns arrays absent
95
+ 14. `sqlite.introspection.schemaSnapshot({compact: false})` → column details (name, type, nullable, pk) present
96
+
97
+ ---
98
+
99
+ ### Category 3: Constraint Analysis Stress
100
+
101
+ 15. `sqlite.introspection.constraintAnalysis({})` → all tables analyzed. Verify summary.byType and summary.bySeverity keys.
102
+ 16. `sqlite.introspection.constraintAnalysis({checks: ["unindexed_fk"]})` → only unindexed FK findings.
103
+ 17. `sqlite.introspection.constraintAnalysis({table: "test_users"})` → only test_users findings. No other tables referenced.
104
+ 18. `sqlite.introspection.constraintAnalysis({table: "nonexistent_table_xyz"})` → report behavior: empty findings or structured error?
105
+
106
+ ---
107
+
108
+ ### Category 4: Storage Analysis & Index Audit Depth
109
+
110
+ **4.1 Storage Analysis Verification**
111
+
112
+ 19. `sqlite.introspection.storageAnalysis({})` → verify database.totalSizeBytes = pageSize × totalPages (arithmetic check)
113
+ 20. `sqlite.introspection.storageAnalysis({})` → verify tables sorted by size descending
114
+ 21. `sqlite.introspection.storageAnalysis({includeTableDetails: false})` → tables absent. Database-level metrics present.
115
+ 22. `sqlite.introspection.storageAnalysis({limit: 3})` → only top 3 tables (if supported)
116
+ 23. `sqlite.introspection.storageAnalysis({})` → verify fragmentationPct 0-100, journalMode and autoVacuum non-empty
117
+
118
+ **4.2 Index Audit Cross-Validation**
119
+
120
+ 24. `sqlite.introspection.indexAudit({})` → flag `idx_orders_status` as `type: "redundant"` (prefix of `idx_orders_status_date`). Field name is `index`.
121
+ 25. `sqlite.introspection.indexAudit({})` → verify `redundantOf` points to `idx_orders_status_date`
122
+ 26. `sqlite.introspection.indexAudit({})` → check for `missing_fk_index` on `test_orders.product_id`
123
+ 27. `sqlite.introspection.indexAudit({table: "test_products"})` → only test_products findings. `idx_products_category` NOT redundant.
124
+ 28. `sqlite.introspection.indexAudit({table: "test_measurements"})` → 200 rows, no secondary indexes. `unindexed_large_table` threshold is 1000 → no finding expected.
125
+
126
+ ---
127
+
128
+ ### Category 5: Query Plan Deep Analysis
129
+
130
+ 29. `sqlite.introspection.queryPlan({sql: "SELECT * FROM test_orders WHERE status = 'completed'"})` → use `idx_orders_status`
131
+ 30. `sqlite.introspection.queryPlan({sql: "SELECT * FROM test_products WHERE name = 'Laptop Pro 15'"})` → full scan (no index on name). Verify `analysis.fullScans` includes `test_products`, `suggestions` array exists.
132
+ 31. `sqlite.introspection.queryPlan({sql: "SELECT p.name, o.quantity FROM test_products p JOIN test_orders o ON o.product_id = p.id WHERE o.status = 'completed'"})` → join plan with multiple entries.
133
+ 32. `sqlite.introspection.queryPlan({sql: "SELECT * FROM test_orders WHERE status = 'completed' AND order_date > '2026-01-20'"})` → compound WHERE. Verify index choice.
134
+ 33. `sqlite.introspection.queryPlan({sql: "SELECT COUNT(*) FROM test_measurements GROUP BY sensor_id"})` → GROUP BY without dedicated index. Expect full scan.
135
+ 34. `sqlite.introspection.queryPlan({sql: "WITH top_orders AS (SELECT * FROM test_orders ORDER BY total_price DESC LIMIT 5) SELECT t.*, p.name FROM top_orders t JOIN test_products p ON p.id = t.product_id"})` → CTE + JOIN plan.
136
+
137
+ ---
138
+
139
+ ### Category 6: Migration Risk Assessment Depth
140
+
141
+ 35. `sqlite.introspection.migrationRisks({statements: ["DROP TABLE test_products"]})` → critical/high risk. Mentions FK dependents.
142
+ 36. `sqlite.introspection.migrationRisks({statements: ["ALTER TABLE test_products ADD COLUMN temp_col TEXT"]})` → low risk (additive)
143
+ 37. `sqlite.introspection.migrationRisks({statements: ["CREATE INDEX idx_temp ON test_products(name)"]})` → low risk
144
+ 38. `sqlite.introspection.migrationRisks({statements: ["DROP INDEX idx_orders_status"]})` → medium risk. Verify `riskLevel: "medium"`, `category: "index_removal"`.
145
+ 39. `sqlite.introspection.migrationRisks({statements: ["ALTER TABLE test_products ADD COLUMN temp1 TEXT", "DROP TABLE test_orders", "CREATE TABLE new_orders (id INTEGER PRIMARY KEY)"]})` → 3 statements, mixed risk. `summary.totalStatements = 3`, `summary.highestRisk ≥ "high"`.
146
+
147
+ ---
148
+
149
+ ### Category 7: Error Message Quality
150
+
151
+ 40. `sqlite.introspection.queryPlan({sql: "DELETE FROM test_products WHERE id = 1"})` → structured error rejecting non-SELECT
152
+ 41. `sqlite.introspection.queryPlan({sql: "SELECT * FROM nonexistent_table_xyz"})` → structured error mentioning table
153
+ 42. `sqlite.introspection.queryPlan({})` → Zod error for missing `sql` — must be handler error, NOT raw MCP
154
+ 43. `sqlite.introspection.cascadeSimulator({})` → Zod error for missing `table`
155
+ 44. `sqlite.introspection.migrationRisks({})` → Zod error for missing `statements`
156
+ 45. `sqlite.introspection.storageAnalysis({limit: 0})` → Zod error (min: 1)
157
+ 46. `sqlite.introspection.storageAnalysis({limit: -5})` → Zod error
158
+
159
+ ---
160
+
161
+ ### Final Cleanup
162
+
163
+ All tools read-only — no cleanup needed. Confirm `test_products` (16), `test_orders` (20), `test_measurements` (200) unchanged.
164
+
165
+ ## Post-Test Procedures
166
+
167
+ 1. **Fix EVERY finding** — ❌, ⚠️, 📦
168
+ 2. **Validate**: Instruct the user to run the test suite (Vitest/Playwright), lint, and typecheck. Do NOT run them yourself.
169
+ 3. **Commit**: Stage and commit — do NOT push
170
+ 4. **Re-test**: After server rebuild
171
+ 5. **Token audit**: Report most expensive block
@@ -0,0 +1,169 @@
1
+ # db-mcp Advanced Stress Test — [json]
2
+
3
+ > [!IMPORTANT]
4
+ > **Do not track progress in this file.** Track your test progress, coverage matrix, and findings in your internal task tracking system (artifact). However, you SHOULD edit this file to fix any factual errors, broken code, or incorrect assertions in the test prompts.
5
+ > If there is nothing to fix, don't update UNRELEASED.md.
6
+ > We're currently testing Native mode.
7
+
8
+ **Step 1:** Read `C:\Users\chris\Desktop\db-mcp\src\constants\server-instructions\gotchas.md` using `view_file`.
9
+
10
+ **Step 2:** Execute each numbered stress test below using `sqlite_execute_code` (code mode).
11
+
12
+ **Step 3:** The agent should update C:\Users\chris\Desktop\db-mcp\UNRELEASED.md with any/all changes/fixes.
13
+
14
+ ## WASM Mode
15
+
16
+ > When testing against a **WASM backend** (`--sqlite` / sql.js): All 24 JSON tools are fully WASM-compatible. No categories to skip or adjust.
17
+
18
+ ## Code Mode Execution
19
+
20
+ - **Code Over Docs**: Fix the handler code if standards (Structured Errors/Zod) are violated. Do NOT change docs/prompts to accommodate broken code.
21
+
22
+ All tests via `sqlite_execute_code`. Use `sqlite.json.*` for JSON tools, `sqlite.core.*` for read/write.
23
+ State persists across calls. Do NOT pass `readonly: true`. Group related tests into single calls.
24
+
25
+ ## Test Database Schema
26
+
27
+ | Table | Rows | Key Columns |
28
+ | --------------- | ---- | -------------------------------------------------------------- |
29
+ | test_jsonb_docs | 6 | id, doc (JSON), metadata (JSON), tags (JSON array), created_at |
30
+ | test_events | 100 | id, event_type, user_id, payload (JSON), event_date |
31
+
32
+ **test_jsonb_docs key data:**
33
+
34
+ - Row 1: `doc.type="article"`, `doc.author="Alice"`, `doc.views=1250`, `doc.rating=4.5`
35
+ - Row 4: `doc.nested.level1.level2 = "deep value"`
36
+ - Row 5: `doc.type="podcast"` (no `doc.views` field)
37
+
38
+ ## Naming & Cleanup
39
+
40
+ - **Temporary tables**: `stress_*` prefix. Drop at end.
41
+ - If DROP fails due to lock, note and move on.
42
+
43
+ ## Reporting Format
44
+
45
+ - ❌ Fail | ⚠️ Issue | 📦 Payload (monitor `metrics.tokenEstimate`) | ✅ Confirmed (inline only)
46
+
47
+ ### Error Message Quality Rating
48
+
49
+ | Level | Verdict |
50
+ | -------------------------------------- | ------- |
51
+ | 5 - Excellent (name + code + context) | ✅ |
52
+ | 4 - Good (name) | ✅ |
53
+ | 3 - Adequate (raw SQLite, informative) | ⚠️ |
54
+ | 2 - Poor (no object name) | ⚠️ |
55
+ | 1 - Useless (generic) | ❌ |
56
+
57
+ ## Structured Error Response Pattern
58
+
59
+ Handler error ✅ = JSON with `success` + `error`. MCP error ❌ = raw text, `isError: true`.
60
+
61
+ ---
62
+
63
+ ## json Group Tools (24 + codemode)
64
+
65
+ 1. sqlite_json_valid
66
+ 2. sqlite_json_extract
67
+ 3. sqlite_json_set
68
+ 4. sqlite_json_remove
69
+ 5. sqlite_json_type
70
+ 6. sqlite_json_array_length
71
+ 7. sqlite_json_array_append
72
+ 8. sqlite_json_keys
73
+ 9. sqlite_json_each
74
+ 10. sqlite_json_group_array
75
+ 11. sqlite_json_group_object
76
+ 12. sqlite_json_pretty
77
+ 13. sqlite_jsonb_convert
78
+ 14. sqlite_json_storage_info
79
+ 15. sqlite_json_normalize_column
80
+ 16. sqlite_json_insert
81
+ 17. sqlite_json_update
82
+ 18. sqlite_json_select
83
+ 19. sqlite_json_query
84
+ 20. sqlite_json_validate_path
85
+ 21. sqlite_json_merge
86
+ 22. sqlite_json_analyze_schema
87
+ 23. sqlite_create_json_collection
88
+ 24. sqlite_json_security_scan
89
+
90
+ ---
91
+
92
+ ### Category 1: Deep JSON Operations
93
+
94
+ **1.1 Deeply Nested Access**
95
+
96
+ 1. `sqlite.json.extract({table: "test_jsonb_docs", column: "doc", path: "$.nested.level1.level2", whereClause: "id = 4"})` → `"deep value"`
97
+ 2. `sqlite.json.extract({table: "test_jsonb_docs", column: "doc", path: "$.nested.level1.nonexistent", whereClause: "id = 4"})` → null or empty (not error)
98
+ 3. `sqlite.json.extract({table: "test_jsonb_docs", column: "doc", path: "$.nonexistent_key", whereClause: "id = 1"})` → null or empty
99
+
100
+ **1.2 Array Manipulation Edge Cases**
101
+
102
+ 4. `sqlite.json.arrayLength({table: "test_jsonb_docs", column: "tags", whereClause: "id = 3"})` → 3 (["mcp","protocol","ai"])
103
+ 5. Create `stress_json_test` with a row containing `tags = '[]'` (empty array) → `sqlite.json.arrayLength` → 0
104
+ 6. `sqlite.json.each` on an empty array → 0 expanded rows (not error)
105
+
106
+ **1.3 Merge Conflict Behavior**
107
+
108
+ > `sqlite_json_merge` uses `json_patch()` which follows RFC 7396 merge-patch semantics.
109
+
110
+ Insert test rows into `stress_json_test`: row 2 = `{"a": 1, "b": {"c": 2}}`, row 3 = `{"a": [1, 2]}`:
111
+
112
+ 7. `sqlite.json.merge({table: "stress_json_test", column: "tags", mergeData: {"b": {"d": 3}}, whereClause: "id = 2"})` → verify deep merge: `b.c` preserved, `b.d` added
113
+ 8. `sqlite.json.merge({table: "stress_json_test", column: "tags", mergeData: {"a": [3, 4]}, whereClause: "id = 3"})` → arrays replaced (not concatenated) per RFC 7396
114
+
115
+ **1.4 Type Coercion Edge Cases**
116
+
117
+ 9. `sqlite.json.type({table: "test_jsonb_docs", column: "doc", path: "$.views", whereClause: "id = 1"})` → `"integer"` (views=1250)
118
+ 10. `sqlite.json.type({table: "test_jsonb_docs", column: "doc", path: "$.rating", whereClause: "id = 1"})` → `"real"` (rating=4.5)
119
+ 11. `sqlite.json.type({table: "test_jsonb_docs", column: "doc", path: "$.nested", whereClause: "id = 4"})` → `"object"`
120
+
121
+ ---
122
+
123
+ ### Category 2: JSON Query & Filter Stress
124
+
125
+ > `sqlite_json_query` uses `filterPaths` (equality-only, `Record<path, value>`) and `selectPaths`.
126
+
127
+ 12. `sqlite.json.query({table: "test_jsonb_docs", column: "doc", filterPaths: {"$.type": "article"}})` → 4 rows
128
+ 13. `sqlite.json.query({table: "test_jsonb_docs", column: "doc", filterPaths: {"$.type": "article", "$.author": "Alice"}, selectPaths: ["$.title", "$.views"]})` → 1 row (Alice's article)
129
+ 14. `sqlite.json.query({table: "test_events", column: "payload", filterPaths: {"$.page": "home"}})` → 25 rows (every 4th event)
130
+
131
+ ---
132
+
133
+ ### Category 3: Error Message Quality
134
+
135
+ 15. `sqlite.json.extract({table: "nonexistent_table_xyz", column: "doc", path: "$.x"})` → structured error mentioning table name
136
+ 16. `sqlite.json.extract({table: "test_jsonb_docs", column: "nonexistent_col", path: "$.x"})` → structured error mentioning column
137
+ 17. `sqlite.json.set({table: "test_jsonb_docs", column: "doc", path: "$.author", value: "\"Modified\"", whereClause: "id = 99999"})` → report behavior for nonexistent row
138
+ 18. `sqlite.json.validatePath({path: ""})` → report behavior for empty path
139
+
140
+ ---
141
+
142
+ ### Category 4: Write Operation Safety
143
+
144
+ 19. Create `stress_json_write` table → insert 3 JSON documents → perform `sqlite.json.set`, `sqlite.json.remove`, `sqlite.json.insert` → verify mutations → cleanup
145
+ > **Note:** `sqlite_json_insert` is a **row-level INSERT** (creates new row with JSON data, provided via the `data` parameter), not a path-level JSON insert.
146
+ 20. `sqlite.json.normalizeColumn(...)` on `stress_json_write` → verify keys sorted/compacted without data loss
147
+
148
+ ---
149
+
150
+ ### Category 5: Security Scan Stress
151
+
152
+ 21. `sqlite.json.securityScan({table: "test_jsonb_docs", column: "doc"})` → scan result with `riskLevel`
153
+ 22. Create `stress_json_inject` with rows containing suspicious patterns (`<script>`, `' OR 1=1`, `${cmd}`) → `sqlite.json.securityScan` → verify detection
154
+ 23. Cleanup: drop `stress_json_inject`
155
+
156
+ ---
157
+
158
+ ### Final Cleanup
159
+
160
+ Drop all `stress_*` tables. Confirm `test_jsonb_docs` row count is still 6 and contents unchanged.
161
+
162
+ ## Post-Test Procedures
163
+
164
+ 1. **Cleanup**: Drop all `stress_*` objects
165
+ 2. **Fix EVERY finding** — ❌, ⚠️, 📦. Consistent with `code-map.md`
166
+ 3. **Validate**: Instruct the user to run the test suite (Vitest/Playwright), lint, and typecheck. Do NOT run them yourself.
167
+ 4. **Commit**: Stage and commit — do NOT push
168
+ 5. **Re-test**: After server rebuild
169
+ 6. **Token audit**: Report most expensive block
@@ -0,0 +1,160 @@
1
+ # db-mcp Advanced Stress Test — [migration]
2
+
3
+ > [!IMPORTANT]
4
+ > **Do not track progress in this file.** Track your test progress, coverage matrix, and findings in your internal task tracking system (artifact). However, you SHOULD edit this file to fix any factual errors, broken code, or incorrect assertions in the test prompts.
5
+ > If there is nothing to fix, don't update UNRELEASED.md.
6
+ > We're currently testing Native mode.
7
+
8
+ **Step 1:** Read `C:\Users\chris\Desktop\db-mcp\src\constants\server-instructions\gotchas.md` using `view_file`.
9
+
10
+ **Step 2:** Execute each numbered stress test below using `sqlite_execute_code` (code mode).
11
+
12
+ **Step 3:** The agent should update C:\Users\chris\Desktop\db-mcp\UNRELEASED.md with any/all changes/fixes.
13
+
14
+ > **⚠️ After testing, run `Set-Location C:\Users\chris\Desktop\db-mcp\test-server; .\reset-database.ps1`** — migration testing creates the `_mcp_migrations` tracking table and may ALTER/CREATE/DROP tables. Always reset after.
15
+
16
+ ## WASM Mode
17
+
18
+ > When testing against a **WASM backend** (`--sqlite` / sql.js): All 6 migration tools are fully WASM-compatible. No categories to skip or adjust.
19
+
20
+ ## Code Mode Execution
21
+
22
+ - **Code Over Docs**: Fix the handler code if standards (Structured Errors/Zod) are violated. Do NOT change docs/prompts to accommodate broken code.
23
+
24
+ All tests via `sqlite_execute_code`. Use `sqlite.migration.*` for migration tools, `sqlite.core.*` for read/write.
25
+ State persists across calls. Do NOT pass `readonly: true`.
26
+
27
+ > **⚠️ Migration tests are stateful** — each depends on state from the previous. Run lifecycle tests sequentially.
28
+
29
+ ## Test Database Schema
30
+
31
+ | Table | Rows | Key Columns |
32
+ | ------------- | ---- | ---------------------------------------- |
33
+ | test_products | 16 | id, name, price, category |
34
+ | test_orders | 20 | id, product_id (FK), total_price, status |
35
+
36
+ ## Naming & Cleanup
37
+
38
+ - **Temporary tables**: `stress_*` prefix.
39
+ - **After testing**: Run `Set-Location C:\Users\chris\Desktop\db-mcp\test-server; .\reset-database.ps1` to undo schema modifications.
40
+
41
+ ## Reporting Format
42
+
43
+ - ❌ Fail | ⚠️ Issue | 📦 Payload (monitor `metrics.tokenEstimate`) | ✅ Confirmed (inline only)
44
+
45
+ ## Structured Error Response Pattern
46
+
47
+ Handler error ✅ = JSON with `success` + `error`. MCP error ❌ = raw text, `isError: true`.
48
+
49
+ ---
50
+
51
+ ## migration Group Tools (6)
52
+
53
+ 1. sqlite_migration_init
54
+ 2. sqlite_migration_record
55
+ 3. sqlite_migration_apply
56
+ 4. sqlite_migration_rollback
57
+ 5. sqlite_migration_history
58
+ 6. sqlite_migration_status
59
+
60
+ ---
61
+
62
+ ### Category 1: Initialization & Idempotency
63
+
64
+ 1. `sqlite.migration.migrationInit({})` → success, `_mcp_migrations` table created
65
+ 2. `sqlite.migration.migrationInit({})` → idempotent: should succeed without error
66
+ 3. `sqlite.migration.migrationStatus({})` → empty/clean state
67
+ 4. `sqlite.migration.migrationHistory({})` → empty list
68
+
69
+ ---
70
+
71
+ ### Category 2: Full Lifecycle (Record → Apply → Rollback)
72
+
73
+ **2.1 Simple ALTER TABLE Migration**
74
+
75
+ 5. `sqlite.migration.migrationRecord({version: "stress_001_add_col", description: "Add stress flag", sql: "ALTER TABLE test_products ADD COLUMN stress_flag INTEGER DEFAULT 0"})` → recorded (SQL not executed)
76
+ 6. `sqlite.migration.migrationStatus({})` → shows 1 recorded migration
77
+ 7. Verify column is NOT yet added
78
+ 8. `sqlite.migration.migrationApply({version: "stress_001_add_col", description: "Add stress flag", sql: "ALTER TABLE test_products ADD COLUMN stress_flag INTEGER DEFAULT 0"})` → applied (SQL executed)
79
+ 9. `sqlite.migration.migrationStatus({})` → shows 1 applied
80
+
81
+ **2.2 CREATE TABLE Migration**
82
+
83
+ 10. `sqlite.migration.migrationApply({version: "stress_002_create_table", description: "Create data table", sql: "CREATE TABLE stress_migration_data (id INTEGER PRIMARY KEY, name TEXT NOT NULL, value REAL)", rollbackSql: "DROP TABLE IF EXISTS stress_migration_data"})` → applied
84
+ 11. `sqlite.migration.migrationHistory({})` → both migrations with timestamps
85
+
86
+ **2.3 Rollback Chain**
87
+
88
+ 12. `sqlite.migration.migrationRollback({version: "stress_002_create_table"})` → rollback with stored rollbackSql
89
+ 13. Verify `stress_migration_data` is gone
90
+ 14. `sqlite.migration.migrationHistory({})` → check status
91
+
92
+ ---
93
+
94
+ ### Category 3: State Pollution & Ordering
95
+
96
+ **3.1 Re-Record After Rollback**
97
+
98
+ 15. `sqlite.migration.migrationApply({version: "stress_003_recreate", description: "Recreate", sql: "CREATE TABLE stress_migration_data (id INTEGER PRIMARY KEY, value TEXT)", rollbackSql: "DROP TABLE IF EXISTS stress_migration_data"})` → applied
99
+ 16. `sqlite.migration.migrationStatus({})` → verify counts
100
+
101
+ **3.2 Duplicate Detection**
102
+
103
+ 17. `sqlite.migration.migrationRecord({version: "stress_001_add_col", description: "Duplicate", sql: "SELECT 1"})` → report behavior: should error (duplicate version) or allow?
104
+
105
+ **3.3 SHA-256 Duplicate SQL Detection**
106
+
107
+ 18. `sqlite.migration.migrationRecord({version: "stress_004_dup_sql", description: "Dup SQL", sql: "ALTER TABLE test_products ADD COLUMN stress_flag INTEGER DEFAULT 0"})` → report behavior: same SQL hash as stress_001
108
+
109
+ **3.4 Multi-Statement Apply Verification**
110
+
111
+ 19. `sqlite.migration.migrationApply({version: "stress_005_index", description: "Add index", sql: "CREATE INDEX stress_idx_flag ON test_products(stress_flag)", rollbackSql: "DROP INDEX IF EXISTS stress_idx_flag"})` → applied
112
+ 20. Verify index created with `sqlite.core.getIndexes({table: "test_products"})`
113
+
114
+ ---
115
+
116
+ ### Category 4: Error Paths & Recovery
117
+
118
+ **4.1 Apply Failures**
119
+
120
+ 21. `sqlite.migration.migrationApply({version: "stress_006_bad_sql", description: "Bad SQL", sql: "ALTER TABLE nonexistent_xyz ADD COLUMN foo TEXT"})` → records but execute fails
121
+ 22. `sqlite.migration.migrationStatus({})` → verify failed migration state is tracked
122
+
123
+ **4.2 Nonexistent Migration Operations**
124
+
125
+ 23. `sqlite.migration.migrationRollback({version: "nonexistent_migration_xyz"})` → structured error (not raw MCP)
126
+
127
+ **4.3 Zod Validation Errors**
128
+
129
+ 24. `sqlite.migration.migrationRecord({})` → Zod error for missing required params — must be handler error
130
+ 25. `sqlite.migration.migrationApply({})` → Zod error for missing required params
131
+ 26. `sqlite.migration.migrationRollback({})` → Zod error for missing `version`
132
+
133
+ ---
134
+
135
+ ### Category 5: Error Message Quality
136
+
137
+ Rate each error response 1-5:
138
+
139
+ 27. `sqlite.migration.migrationRollback({version: "nonexistent_migration_xyz"})` → does it mention the version?
140
+ 28. `sqlite.migration.migrationRecord({})` → does it list missing required fields?
141
+ 29. `sqlite.migration.migrationRollback({version: "stress_001_add_col"})` → rate error clarity (no rollbackSql stored)
142
+
143
+ ---
144
+
145
+ ### Final Cleanup
146
+
147
+ 1. Drop `_mcp_migrations`: `sqlite.admin.dropTable({table: "_mcp_migrations"})`
148
+ 2. Drop `stress_migration_data`: `sqlite.admin.dropTable({table: "stress_migration_data"})`
149
+ 3. Drop `stress_idx_flag`: _Handled by database reset below_
150
+ 4. **Reset database** with `Set-Location C:\Users\chris\Desktop\db-mcp\test-server; .\reset-database.ps1` to undo `stress_flag` column on `test_products`
151
+ 5. After reset, verify: `test_products` has 16 rows and original columns (no `stress_flag`)
152
+
153
+ ## Post-Test Procedures
154
+
155
+ 1. **⚠️ Reset database**: `Set-Location C:\Users\chris\Desktop\db-mcp\test-server; .\reset-database.ps1`
156
+ 2. **Fix EVERY finding** — ❌, ⚠️, 📦
157
+ 3. **Validate**: Instruct the user to run the test suite (Vitest/Playwright), lint, and typecheck. Do NOT run them yourself.
158
+ 4. **Commit**: Stage and commit — do NOT push
159
+ 5. **Re-test**: After server rebuild
160
+ 6. **Token audit**: Report most expensive block