db-mcp 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitleaks.toml +9 -0
- package/.trivyignore +8 -0
- package/README.md +193 -120
- package/dist/{chunk-DWJXQEFA.js → chunk-5Y42NPBP.js} +4168 -2411
- package/dist/{chunk-AOUL5SHS.js → chunk-645ZEFLA.js} +70 -20
- package/dist/chunk-OKOVZ5QE.js +28 -0
- package/dist/chunk-SFJQCNG7.js +131 -0
- package/dist/{chunk-S5IDDSSB.js → chunk-VIDSICEL.js} +12 -1
- package/dist/chunk-WBER5YY4.js +2053 -0
- package/dist/{chunk-4IA3DB5C.js → chunk-X3MUUOWM.js} +19 -2
- package/dist/{chunk-Z2GFQU3G.js → chunk-Z7C2TM4L.js} +114 -21
- package/dist/cli.js +79 -5
- package/dist/{http-VSB7DBJR.js → http-6KF4ULDI.js} +199 -77
- package/dist/index.d.ts +231 -8
- package/dist/index.js +6 -5
- package/dist/{sqlite-VHBA4ABV.js → sqlite-U5KSYQXK.js} +63 -140
- package/dist/{sqlite-native-D2LH5ZT7.js → sqlite-native-JXMCFQBA.js} +576 -114
- package/dist/worker-script.js +34 -10
- package/logs/.gitkeep +1 -0
- package/mcp-config-example.json +83 -0
- package/package.json +9 -8
- package/playwright.config.ts +1 -1
- package/scripts/update-badges.ts +99 -0
- package/server.json +7 -5
- package/test-server/README.md +20 -23
- package/test-server/code-map.md +45 -33
- package/test-server/reset-database.ps1 +59 -17
- package/test-server/scripts/README.md +27 -0
- package/test-server/{test-help-resources.mjs → scripts/test-help-resources.mjs} +12 -5
- package/test-server/scripts/test-prompts.mjs +251 -0
- package/test-server/{test-tool-annotations.mjs → scripts/test-tool-annotations.mjs} +9 -4
- package/test-server/test-advanced/README.md +70 -0
- package/test-server/test-advanced/test-codemode-advanced-admin.md +174 -0
- package/test-server/test-advanced/test-codemode-advanced-core.md +193 -0
- package/test-server/test-advanced/test-codemode-advanced-geo.md +157 -0
- package/test-server/test-advanced/test-codemode-advanced-introspection.md +171 -0
- package/test-server/test-advanced/test-codemode-advanced-json.md +169 -0
- package/test-server/test-advanced/test-codemode-advanced-migration.md +160 -0
- package/test-server/test-advanced/test-codemode-advanced-stats.md +185 -0
- package/test-server/test-advanced/test-codemode-advanced-text.md +183 -0
- package/test-server/test-advanced/test-codemode-advanced-transactions.md +136 -0
- package/test-server/test-advanced/test-codemode-advanced-vector.md +141 -0
- package/test-server/test-codemode/README.md +121 -0
- package/test-server/test-codemode/test-codemode-admin.md +223 -0
- package/test-server/test-codemode/test-codemode-core.md +286 -0
- package/test-server/test-codemode/test-codemode-geo.md +177 -0
- package/test-server/test-codemode/test-codemode-introspection.md +190 -0
- package/test-server/test-codemode/test-codemode-json.md +237 -0
- package/test-server/test-codemode/test-codemode-migration.md +278 -0
- package/test-server/test-codemode/test-codemode-sandbox.md +413 -0
- package/test-server/test-codemode/test-codemode-stats.md +232 -0
- package/test-server/test-codemode/test-codemode-text.md +237 -0
- package/test-server/test-codemode/test-codemode-transactions.md +236 -0
- package/test-server/test-codemode/test-codemode-vector.md +244 -0
- package/test-server/test-codemode/test-codemode-wasm-degradation.md +394 -0
- package/test-server/test-database.sql +37 -1
- package/test-server/test-resources.md +43 -16
- package/test-server/test-tool-groups/README.md +100 -0
- package/test-server/test-tool-groups/test-admin-core.md +165 -0
- package/test-server/test-tool-groups/test-admin-extensions.md +133 -0
- package/test-server/{test-tools.md → test-tool-groups/test-core-data.md} +103 -17
- package/test-server/test-tool-groups/test-core-schema.md +240 -0
- package/test-server/test-tool-groups/test-geo-haversine.md +130 -0
- package/test-server/test-tool-groups/test-geo-spatialite.md +110 -0
- package/test-server/test-tool-groups/test-introspection-diagnostics.md +123 -0
- package/test-server/test-tool-groups/test-introspection-schema.md +133 -0
- package/test-server/test-tool-groups/test-json-read.md +219 -0
- package/test-server/test-tool-groups/test-json-write.md +157 -0
- package/test-server/test-tool-groups/test-migration.md +193 -0
- package/test-server/test-tool-groups/test-stats-advanced.md +118 -0
- package/test-server/test-tool-groups/test-stats-basic.md +156 -0
- package/test-server/test-tool-groups/test-text-advanced.md +169 -0
- package/test-server/test-tool-groups/test-text-basic.md +177 -0
- package/test-server/test-tool-groups/test-transactions.md +179 -0
- package/test-server/test-tool-groups/test-vector-read.md +130 -0
- package/test-server/test-tool-groups/test-vector-write.md +115 -0
- package/test-server/tool-reference.md +79 -58
- package/tsconfig.build.json +6 -0
- package/dist/chunk-DZQLDEQS.js +0 -879
- package/test-server/test-agent-experience.md +0 -243
- package/test-server/test-group-tools.md +0 -861
- package/test-server/test-tools-advanced-1.md +0 -517
- package/test-server/test-tools-advanced-2.md +0 -487
- package/test-server/test-tools-codemode.md +0 -629
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# db-mcp Code Mode Testing: [stats]
|
|
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:** Conduct an exhaustive test of the **stats** tool group using ONLY `sqlite_execute_code`. Do not use direct tool calls or terminal.
|
|
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), apply these adjustments:
|
|
17
|
+
|
|
18
|
+
- **Skip Phase 3** entirely — all 6 window function tools (items 18-23) are `[NATIVE ONLY]`.
|
|
19
|
+
- **Phase 4**: Skip item 28 (`windowRowNumber`) — `[NATIVE ONLY]`.
|
|
20
|
+
- **Phase 5**: Skip items 45-50 (all `window*` tools) — `[NATIVE ONLY]`.
|
|
21
|
+
- **Phase 6**: Skip item 52 (`windowMovingAvg`) — `[NATIVE ONLY]`.
|
|
22
|
+
- All other phases (1, 2, 7) are fully WASM-compatible.
|
|
23
|
+
|
|
24
|
+
## Reporting Format
|
|
25
|
+
|
|
26
|
+
- ❌ Fail: Tool errors or produces incorrect results
|
|
27
|
+
- ⚠️ Issue: Unexpected behavior or improvement opportunity
|
|
28
|
+
- 📦 Payload: Unnecessarily large response — monitor `metrics.tokenEstimate`.
|
|
29
|
+
|
|
30
|
+
## Test Database Schema
|
|
31
|
+
|
|
32
|
+
| Table | Rows | Key Columns |
|
|
33
|
+
| ----------------- | ---- | ------------------------------------------------------------------------------- |
|
|
34
|
+
| test_products | 16 | id, name, price (REAL), category (3 values: electronics, accessories, office) |
|
|
35
|
+
| test_orders | 20 | id, product_id, total_price, order_date, status |
|
|
36
|
+
| test_measurements | 200 | id, sensor_id (INT 1-5), temperature, humidity, pressure, measured_at |
|
|
37
|
+
| test_events | 100 | id, event_type (page_view, click, purchase, login, search), user_id, event_date |
|
|
38
|
+
| test_locations | 15 | id, name, city (6 cities), latitude, longitude |
|
|
39
|
+
|
|
40
|
+
## Testing Requirements
|
|
41
|
+
|
|
42
|
+
> [!CAUTION]
|
|
43
|
+
> **Zero tolerance for raw MCP errors.** Report as ❌.
|
|
44
|
+
|
|
45
|
+
1. **Batched scripting**: Bundle checks with `failures` array.
|
|
46
|
+
2. **Error path testing**: Every tool with `{}` (Zod) and domain error.
|
|
47
|
+
3. **Token tracking**: Monitor `metrics.tokenEstimate`.
|
|
48
|
+
4. **Coverage Matrix**: `| Tool | Happy Path | Domain Error | Zod Error |`
|
|
49
|
+
5. **Deterministic checklist first**.
|
|
50
|
+
6. **Code Over Docs**: Fix the handler code if standards (Structured Errors/Zod) are violated. Do NOT change docs/prompts to accommodate broken code.
|
|
51
|
+
|
|
52
|
+
## Structured Error Response Pattern
|
|
53
|
+
|
|
54
|
+
Handler error ✅ = JSON with `success` + `error`. MCP error ❌ = raw text, `isError: true`.
|
|
55
|
+
|
|
56
|
+
## Cleanup
|
|
57
|
+
|
|
58
|
+
- No write operations in stats — no cleanup needed.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Phase 1: Core Stats — Happy Paths (batched)
|
|
63
|
+
|
|
64
|
+
> Bundle items 1-14 into 1-2 `sqlite_execute_code` calls.
|
|
65
|
+
|
|
66
|
+
1. `sqlite.stats.statsBasic({table: "test_measurements", column: "temperature"})` → `count: 200`, `min`, `max`, `avg` present inside `stats` object
|
|
67
|
+
2. `sqlite.stats.statsCount({table: "test_products"})` → `{count: 16}`
|
|
68
|
+
3. `sqlite.stats.statsCount({table: "test_products", column: "category", distinct: true})` → 3 (electronics, accessories, office)
|
|
69
|
+
4. `sqlite.stats.statsGroupBy({table: "test_measurements", groupByColumn: "sensor_id", valueColumn: "temperature", stat: "avg"})` → 5 results in `results` array
|
|
70
|
+
5. `sqlite.stats.statsHistogram({table: "test_measurements", column: "temperature", buckets: 5})` → 5 buckets
|
|
71
|
+
6. `sqlite.stats.statsPercentile({table: "test_measurements", column: "temperature", percentiles: [25, 50, 75, 90]})` → 4 values
|
|
72
|
+
7. `sqlite.stats.statsCorrelation({table: "test_measurements", column1: "temperature", column2: "humidity"})` → value between -1 and 1
|
|
73
|
+
8. `sqlite.stats.statsTopN({table: "test_products", column: "price", n: 3, orderDirection: "desc"})` → top 3 in `rows` array, `Laptop Pro 15` at 1299.99 first
|
|
74
|
+
9. `sqlite.stats.statsDistinct({table: "test_locations", column: "city"})` → 6 cities
|
|
75
|
+
10. `sqlite.stats.statsSummary({table: "test_measurements", columns: ["temperature", "humidity", "pressure"]})` → 3 summaries
|
|
76
|
+
11. `sqlite.stats.statsFrequency({table: "test_events", column: "event_type"})` → 5 event types in `distribution` array, ~20 each
|
|
77
|
+
12. `sqlite.stats.statsOutliers({table: "test_measurements", column: "temperature"})` → outlier detection result
|
|
78
|
+
13. `sqlite.stats.statsRegression({table: "test_measurements", xColumn: "temperature", yColumn: "humidity", degree: 1})` → regression coefficients
|
|
79
|
+
14. `sqlite.stats.statsHypothesis({table: "test_measurements", column: "temperature", testType: "ttest_one", expectedMean: 25})` → `statistic` and `pValue` present
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Phase 2: Anomaly Detection Suite — Happy Paths (batched)
|
|
84
|
+
|
|
85
|
+
15. `sqlite.stats.detectAnomalies({table: "test_measurements", column: "temperature"})` → `anomalies` array present, `riskLevel` low
|
|
86
|
+
16. `sqlite.stats.detectBloat()` → bloat detection (may return empty if no bloat)
|
|
87
|
+
17. `sqlite.stats.detectSchemaRisks()` → `tables` array present, `highRiskCount` 0
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Phase 3: Window Functions `[NATIVE ONLY]` — Happy Paths (batched)
|
|
92
|
+
|
|
93
|
+
18. `sqlite.stats.windowRowNumber({table: "test_products", orderBy: "price DESC"})` → ranked by price
|
|
94
|
+
19. `sqlite.stats.windowRank({table: "test_products", orderBy: "price DESC"})` → rank with ties
|
|
95
|
+
20. `sqlite.stats.windowRunningTotal({table: "test_orders", valueColumn: "total_price", orderBy: "order_date"})` → cumulative totals
|
|
96
|
+
21. `sqlite.stats.windowMovingAvg({table: "test_measurements", valueColumn: "temperature", windowSize: 5, orderBy: "measured_at"})` → moving averages
|
|
97
|
+
22. `sqlite.stats.windowLagLead({table: "test_orders", column: "total_price", direction: "lag", orderBy: "order_date"})` → lag values
|
|
98
|
+
23. `sqlite.stats.windowNtile({table: "test_products", buckets: 4, orderBy: "price"})` → quartiles
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Phase 4: Stats Domain Errors (batched)
|
|
103
|
+
|
|
104
|
+
🔴 24. `sqlite.stats.statsBasic({table: "nonexistent_xyz", column: "x"})` → `{success: false}`
|
|
105
|
+
🔴 25. `sqlite.stats.statsBasic({table: "test_products", column: "nonexistent_col"})` → report behavior
|
|
106
|
+
🔴 26. `sqlite.stats.statsCorrelation({table: "test_products", column1: "name", column2: "description"})` → error about non-numeric
|
|
107
|
+
🔴 27. `sqlite.stats.detectAnomalies({table: "nonexistent_xyz", column: "x"})` → `{success: false}`
|
|
108
|
+
🔴 28. `sqlite.stats.windowRowNumber({table: "nonexistent_xyz", orderBy: "x"})` `[NATIVE ONLY]` → `{success: false}`
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Phase 5: Stats Zod Validation (batched)
|
|
113
|
+
|
|
114
|
+
🔴 29. `sqlite.stats.statsBasic({})` → `{success: false}`
|
|
115
|
+
🔴 30. `sqlite.stats.statsCount({})` → `{success: false}`
|
|
116
|
+
🔴 31. `sqlite.stats.statsGroupBy({})` → `{success: false}`
|
|
117
|
+
🔴 32. `sqlite.stats.statsHistogram({})` → `{success: false}`
|
|
118
|
+
🔴 33. `sqlite.stats.statsPercentile({})` → `{success: false}`
|
|
119
|
+
🔴 34. `sqlite.stats.statsCorrelation({})` → `{success: false}`
|
|
120
|
+
🔴 35. `sqlite.stats.statsTopN({})` → `{success: false}`
|
|
121
|
+
🔴 36. `sqlite.stats.statsDistinct({})` → `{success: false}`
|
|
122
|
+
🔴 37. `sqlite.stats.statsSummary({})` → `{success: false}`
|
|
123
|
+
🔴 38. `sqlite.stats.statsFrequency({})` → `{success: false}`
|
|
124
|
+
🔴 39. `sqlite.stats.statsOutliers({})` → `{success: false}`
|
|
125
|
+
🔴 40. `sqlite.stats.statsRegression({})` → `{success: false}`
|
|
126
|
+
🔴 41. `sqlite.stats.statsHypothesis({})` → `{success: false}`
|
|
127
|
+
🔴 42. `sqlite.stats.detectAnomalies({})` → `{success: false}`
|
|
128
|
+
🔴 43. `sqlite.stats.detectBloat({})` → `{success: false}` or success (no required params)
|
|
129
|
+
🔴 44. `sqlite.stats.detectSchemaRisks({})` → `{success: false}` or success (no required params)
|
|
130
|
+
🔴 45. `sqlite.stats.windowRowNumber({})` `[NATIVE ONLY]` → `{success: false}`
|
|
131
|
+
🔴 46. `sqlite.stats.windowRank({})` `[NATIVE ONLY]` → `{success: false}`
|
|
132
|
+
🔴 47. `sqlite.stats.windowLagLead({})` `[NATIVE ONLY]` → `{success: false}`
|
|
133
|
+
🔴 48. `sqlite.stats.windowRunningTotal({})` `[NATIVE ONLY]` → `{success: false}`
|
|
134
|
+
🔴 49. `sqlite.stats.windowMovingAvg({})` `[NATIVE ONLY]` → `{success: false}`
|
|
135
|
+
🔴 50. `sqlite.stats.windowNtile({})` `[NATIVE ONLY]` → `{success: false}`
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Phase 6: Wrong-Type Numeric Coercion (batched)
|
|
140
|
+
|
|
141
|
+
🔴 51. `sqlite.stats.statsHistogram({table: "test_measurements", column: "temperature", buckets: "abc"})` → coerced default (success) or handler error, NOT raw MCP
|
|
142
|
+
🔴 52. `sqlite.stats.windowMovingAvg({table: "test_measurements", valueColumn: "temperature", windowSize: "abc", orderBy: "measured_at"})` `[NATIVE ONLY]` → coerced default (success) or handler error
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Phase 6.5: Gotcha Edge Cases (batched)
|
|
147
|
+
|
|
148
|
+
53. `sqlite.stats.statsTopN({table: "test_articles", column: "title", n: 3})` → verify auto-exclusion of long-content columns (`body`, `description`, `notes`, etc.) from output when `selectColumns` is omitted (gotcha #13)
|
|
149
|
+
54. `sqlite.stats.statsTopN({table: "test_articles", column: "title", n: 3, selectColumns: ["title", "body"]})` → explicit `selectColumns` overrides auto-exclusion — `body` should appear in results (gotcha #13)
|
|
150
|
+
55. `sqlite.stats.detectBloat({includeZeroRisk: true})` → includes zero-risk tables in output (verify param is accepted and changes result set)
|
|
151
|
+
56. `sqlite.stats.detectSchemaRisks({includeZeroRisk: true})` → includes zero-risk tables in output (verify param is accepted and changes result set)
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Phase 7: Multi-Step Workflow
|
|
156
|
+
|
|
157
|
+
### 7.1 — Statistical analysis pipeline
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const failures = [];
|
|
161
|
+
const basic = await sqlite.stats.statsBasic({
|
|
162
|
+
table: "test_measurements",
|
|
163
|
+
column: "temperature",
|
|
164
|
+
});
|
|
165
|
+
if (!basic || basic.stats.count !== 200)
|
|
166
|
+
failures.push("basic stats: count mismatch");
|
|
167
|
+
|
|
168
|
+
const pct = await sqlite.stats.statsPercentile({
|
|
169
|
+
table: "test_measurements",
|
|
170
|
+
column: "temperature",
|
|
171
|
+
percentiles: [50],
|
|
172
|
+
});
|
|
173
|
+
const corr = await sqlite.stats.statsCorrelation({
|
|
174
|
+
table: "test_measurements",
|
|
175
|
+
column1: "temperature",
|
|
176
|
+
column2: "humidity",
|
|
177
|
+
});
|
|
178
|
+
const top = await sqlite.stats.statsTopN({
|
|
179
|
+
table: "test_products",
|
|
180
|
+
column: "price",
|
|
181
|
+
n: 3,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
failures,
|
|
186
|
+
success: failures.length === 0,
|
|
187
|
+
summary: {
|
|
188
|
+
tempRange: `${basic.stats.min} - ${basic.stats.max}`,
|
|
189
|
+
median: pct.percentiles,
|
|
190
|
+
correlation: corr.correlation,
|
|
191
|
+
topProducts: top.rows,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### 7.2 — Empty table boundary
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const failures = [];
|
|
202
|
+
await sqlite.core.createTable({
|
|
203
|
+
table: "temp_cm_stats_empty",
|
|
204
|
+
columns: [
|
|
205
|
+
{ name: "id", type: "INTEGER", primaryKey: true },
|
|
206
|
+
{ name: "value", type: "REAL" },
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
const basic = await sqlite.stats.statsBasic({
|
|
210
|
+
table: "temp_cm_stats_empty",
|
|
211
|
+
column: "value",
|
|
212
|
+
});
|
|
213
|
+
// Should return count: 0 or {success: false} — must not crash
|
|
214
|
+
if (basic.success === false) {
|
|
215
|
+
// Structured error is acceptable for empty table
|
|
216
|
+
} else if (basic.stats?.count !== 0) {
|
|
217
|
+
failures.push("expected count 0 for empty table, got: " + basic.stats?.count);
|
|
218
|
+
}
|
|
219
|
+
await sqlite.core.dropTable({ table: "temp_cm_stats_empty" });
|
|
220
|
+
return { failures, success: failures.length === 0, basicResult: basic };
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Post-Test Procedures
|
|
226
|
+
|
|
227
|
+
1. **Triage findings**: Create implementation plan if issues found
|
|
228
|
+
2. **Scope of fixes**: Handler code, server-instructions, this prompt
|
|
229
|
+
3. **Validate**: Instruct the user to run the test suite (Vitest/Playwright), lint, and typecheck. Do NOT run them yourself.
|
|
230
|
+
4. **Commit**: Stage and commit — do NOT push
|
|
231
|
+
5. **Token audit**: Report most expensive block
|
|
232
|
+
6. **Final summary**: After testing/re-testing
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# db-mcp Code Mode Testing: [text]
|
|
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:** Conduct an exhaustive test of the **text** tool group using ONLY `sqlite_execute_code`. Do not use direct tool calls or terminal.
|
|
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), apply these adjustments:
|
|
17
|
+
|
|
18
|
+
- **Skip Phase 2** entirely — all FTS5 tools (items 15-23) are `[NATIVE ONLY]`.
|
|
19
|
+
- **Phase 4** (Domain Errors): Skip item 28 (`ftsSearch`) — `[NATIVE ONLY]`.
|
|
20
|
+
- **Phase 5** (Zod Validation): Skip items 43-47 (`ftsCreate`, `ftsSearch`, `ftsRebuild`, `ftsMatchInfo`, `ftsHeadline`) — `[NATIVE ONLY]`.
|
|
21
|
+
- All other phases (1, 3, 6) are fully WASM-compatible — 14 text tools work identically.
|
|
22
|
+
|
|
23
|
+
## Reporting Format
|
|
24
|
+
|
|
25
|
+
- ❌ Fail: Tool errors or produces incorrect results
|
|
26
|
+
- ⚠️ Issue: Unexpected behavior or improvement opportunity
|
|
27
|
+
- 📦 Payload: Unnecessarily large response — monitor `metrics.tokenEstimate`.
|
|
28
|
+
|
|
29
|
+
## Test Database Schema
|
|
30
|
+
|
|
31
|
+
| Table | Rows | Key Columns |
|
|
32
|
+
| ------------- | ---- | ----------------------------------------------------------------- |
|
|
33
|
+
| test_products | 16 | id, name (row 16: `Café Décor Light` — accented), price, category |
|
|
34
|
+
| test_users | 9 | id, username, email, phone, bio |
|
|
35
|
+
| test_articles | 8 | id, title, body, author, category, published_at |
|
|
36
|
+
|
|
37
|
+
**Test data:**
|
|
38
|
+
|
|
39
|
+
- Emails: `@example.com`, `@company.org`, `@gmail.com`, etc. One user (`testuser`) has `test.user@gmail.com`
|
|
40
|
+
- Phone formats: `+1-555-0101`, `+44-20-7123-4567`, `+82-2-1234-5678`
|
|
41
|
+
- FTS searchable terms: `SQLite`, `database`, `JSON`, `FTS`, `vector`, `API`, `search`, `MCP`
|
|
42
|
+
|
|
43
|
+
## Testing Requirements
|
|
44
|
+
|
|
45
|
+
> [!CAUTION]
|
|
46
|
+
> **Zero tolerance for raw MCP errors.** Report as ❌.
|
|
47
|
+
|
|
48
|
+
1. **Batched scripting**: Bundle checks with `failures` array.
|
|
49
|
+
2. **Error path testing**: Every tool with `{}` (Zod) and domain error.
|
|
50
|
+
3. **Token tracking**: Monitor `metrics.tokenEstimate`.
|
|
51
|
+
4. **Coverage Matrix**: `| Tool | Happy Path | Domain Error | Zod Error |`
|
|
52
|
+
5. **Deterministic checklist first**.
|
|
53
|
+
6. **Code Over Docs**: Fix the handler code if standards (Structured Errors/Zod) are violated. Do NOT change docs/prompts to accommodate broken code.
|
|
54
|
+
|
|
55
|
+
## Structured Error Response Pattern
|
|
56
|
+
|
|
57
|
+
Handler error ✅ = JSON with `success` + `error`. MCP error ❌ = raw text, `isError: true`.
|
|
58
|
+
|
|
59
|
+
## Cleanup
|
|
60
|
+
|
|
61
|
+
- Temporary tables: `temp_*` and any created `test_articles_fts`. Drop at end of script using `DROP TABLE IF EXISTS`.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Phase 1: Text Tools — Happy Paths (batched)
|
|
66
|
+
|
|
67
|
+
> Bundle items 1-14 into 1-2 `sqlite_execute_code` calls.
|
|
68
|
+
|
|
69
|
+
1. `sqlite.text.regexMatch({table: "test_users", column: "email", pattern: "@gmail\\.com$"})` → at least 1 result
|
|
70
|
+
2. `sqlite.text.regexExtract({table: "test_users", column: "email", pattern: "@([^.]+)\\.", groupIndex: 1})` → domain parts
|
|
71
|
+
3. `sqlite.text.fuzzyMatch({table: "test_products", column: "name", search: "Laptp", maxDistance: 3})` → `Laptop Pro 15`
|
|
72
|
+
4. `sqlite.text.phoneticMatch({table: "test_products", column: "name", search: "Labtop"})` → `Laptop Pro 15`
|
|
73
|
+
5. `sqlite.text.validate({table: "test_users", column: "email", pattern: "email"})` → all 9 valid
|
|
74
|
+
6. `sqlite.text.validate({table: "test_users", column: "phone", pattern: "phone"})` → valid/invalid counts
|
|
75
|
+
7. `sqlite.text.case({table: "test_users", column: "username", mode: "upper"})` → uppercased
|
|
76
|
+
8. `sqlite.text.normalize({table: "test_products", column: "name", mode: "strip_accents"})` → `Café Décor Light` → `Cafe Decor Light`
|
|
77
|
+
9. `sqlite.text.split({table: "test_users", column: "email", delimiter: "@"})` → local + domain parts
|
|
78
|
+
10. `sqlite.text.concat({table: "test_users", columns: ["username", "email"], separator: " - "})` → concatenated
|
|
79
|
+
11. `sqlite.text.trim({table: "test_users", column: "bio"})` → trimmed
|
|
80
|
+
12. `sqlite.text.substring({table: "test_users", column: "username", start: 1, length: 4})` → first 4 chars
|
|
81
|
+
13. `sqlite.text.sentiment({text: "I love this product"})` → sentiment scores
|
|
82
|
+
14. `sqlite.text.advancedSearch({table: "test_products", column: "name", searchTerm: "keyboard", techniques: ["exact", "fuzzy", "phonetic"]})` → finds `Mechanical Keyboard`
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Phase 2: FTS5 Tools `[NATIVE ONLY]` — Happy Paths (batched)
|
|
87
|
+
|
|
88
|
+
15. `sqlite.text.ftsCreate({sourceTable: "test_users", columns: ["username", "bio"], tableName: "temp_cm_fts"})` → created
|
|
89
|
+
16. `sqlite.text.ftsRebuild({table: "temp_cm_fts"})` → rebuilt
|
|
90
|
+
17. `sqlite.text.ftsSearch({table: "temp_cm_fts", query: "test*"})` → results
|
|
91
|
+
18. `sqlite.text.ftsMatchInfo({table: "temp_cm_fts", query: "test*"})` → match info with scoring
|
|
92
|
+
19. `sqlite.text.ftsHeadline({table: "test_articles_fts", query: "SQLite"})` → highlighted results
|
|
93
|
+
20. Cleanup: drop `temp_cm_fts` (automatically drops associated sync triggers)
|
|
94
|
+
21. `sqlite.text.ftsSearch({table: "test_articles_fts", query: "SQLite"})` → at least 1 result
|
|
95
|
+
22. `sqlite.text.ftsSearch({table: "test_articles_fts", query: "MCP protocol"})` → article 3
|
|
96
|
+
23. `sqlite.text.ftsSearch({table: "test_articles_fts", query: "nonexistent_term_xyz"})` → 0 results
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Phase 3: Text Write Tool (temp table)
|
|
101
|
+
|
|
102
|
+
24. `sqlite.text.replace({table: "test_users", column: "email", searchPattern: "@example.com", replaceWith: "@test.org", whereClause: "email LIKE '%@example.com'"})` → 1 row affected
|
|
103
|
+
25. Revert: `sqlite.text.replace({table: "test_users", column: "email", searchPattern: "@test.org", replaceWith: "@example.com", whereClause: "email LIKE '%@test.org'"})` → 1 row reverted
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Phase 4: Text Domain Errors (batched)
|
|
108
|
+
|
|
109
|
+
🔴 26. `sqlite.text.regexMatch({table: "nonexistent_xyz", column: "x", pattern: "."})` → `{success: false}`
|
|
110
|
+
🔴 27. `sqlite.text.fuzzyMatch({table: "test_users", column: "nonexistent_col", search: "test"})` → `{success: false}`
|
|
111
|
+
🔴 28. `sqlite.text.ftsSearch({table: "nonexistent_fts_xyz", query: "test"})` `[NATIVE ONLY]` → `{success: false}`
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Phase 5: Text Zod Validation (batched)
|
|
116
|
+
|
|
117
|
+
🔴 29. `sqlite.text.regexExtract({})` → `{success: false}`
|
|
118
|
+
🔴 30. `sqlite.text.regexMatch({})` → `{success: false}`
|
|
119
|
+
🔴 31. `sqlite.text.split({})` → `{success: false}`
|
|
120
|
+
🔴 32. `sqlite.text.concat({})` → `{success: false}`
|
|
121
|
+
🔴 33. `sqlite.text.replace({})` → `{success: false}`
|
|
122
|
+
🔴 34. `sqlite.text.trim({})` → `{success: false}`
|
|
123
|
+
🔴 35. `sqlite.text.case({})` → `{success: false}`
|
|
124
|
+
🔴 36. `sqlite.text.substring({})` → `{success: false}`
|
|
125
|
+
🔴 37. `sqlite.text.fuzzyMatch({})` → `{success: false}`
|
|
126
|
+
🔴 38. `sqlite.text.phoneticMatch({})` → `{success: false}`
|
|
127
|
+
🔴 39. `sqlite.text.normalize({})` → `{success: false}`
|
|
128
|
+
🔴 40. `sqlite.text.validate({})` → `{success: false}`
|
|
129
|
+
🔴 41. `sqlite.text.advancedSearch({})` → `{success: false}`
|
|
130
|
+
🔴 42. `sqlite.text.sentiment({})` → `{success: false}`
|
|
131
|
+
🔴 43. `sqlite.text.ftsCreate({})` `[NATIVE ONLY]` → `{success: false}`
|
|
132
|
+
🔴 44. `sqlite.text.ftsSearch({})` `[NATIVE ONLY]` → `{success: false}`
|
|
133
|
+
🔴 45. `sqlite.text.ftsRebuild({})` `[NATIVE ONLY]` → `{success: false}`
|
|
134
|
+
🔴 46. `sqlite.text.ftsMatchInfo({})` `[NATIVE ONLY]` → `{success: false}`
|
|
135
|
+
🔴 47. `sqlite.text.ftsHeadline({})` `[NATIVE ONLY]` → `{success: false}`
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Phase 5.5: Gotcha Edge Cases (batched)
|
|
140
|
+
|
|
141
|
+
48. `sqlite.text.fuzzyMatch({table: "test_products", column: "name", search: "Laptop Pro 15", tokenize: false, maxDistance: 3})` → full-string matching (default tokenizes into words and matches per-token, gotcha #10)
|
|
142
|
+
49. `sqlite.text.phoneticMatch({table: "test_products", column: "name", search: "Labtop", algorithm: "metaphone"})` → test Metaphone algorithm variant (default is Soundex)
|
|
143
|
+
50. `sqlite.text.advancedSearch({table: "test_products", column: "name", searchTerm: "keyboard", techniques: ["phonetic"]})` → single technique instead of all 3 — verify it works in isolation
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Phase 6: Multi-Step Workflow
|
|
148
|
+
|
|
149
|
+
### 6.1 — Text analysis pipeline
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
const failures = [];
|
|
153
|
+
// Validate emails, extract domains, search fuzzy, combine results
|
|
154
|
+
const validation = await sqlite.text.validate({
|
|
155
|
+
table: "test_users",
|
|
156
|
+
column: "email",
|
|
157
|
+
pattern: "email",
|
|
158
|
+
});
|
|
159
|
+
const domains = await sqlite.text.regexExtract({
|
|
160
|
+
table: "test_users",
|
|
161
|
+
column: "email",
|
|
162
|
+
pattern: "@([^.]+)\\.",
|
|
163
|
+
groupIndex: 1,
|
|
164
|
+
});
|
|
165
|
+
const fuzzy = await sqlite.text.fuzzyMatch({
|
|
166
|
+
table: "test_products",
|
|
167
|
+
column: "name",
|
|
168
|
+
search: "keybord",
|
|
169
|
+
maxDistance: 3,
|
|
170
|
+
});
|
|
171
|
+
if (!validation.success) failures.push("validation failed");
|
|
172
|
+
if (!domains.success) failures.push("domain extraction failed");
|
|
173
|
+
if (!fuzzy.success) failures.push("fuzzy search failed");
|
|
174
|
+
return {
|
|
175
|
+
failures,
|
|
176
|
+
success: failures.length === 0,
|
|
177
|
+
summary: {
|
|
178
|
+
validEmails: validation?.validCount,
|
|
179
|
+
domainCount: domains?.matches?.length,
|
|
180
|
+
fuzzyHits: fuzzy?.matches?.length,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### 6.2 — FTS5 rebuild requirement verification `[NATIVE ONLY]`
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
const failures = [];
|
|
191
|
+
// Create FTS5 table
|
|
192
|
+
await sqlite.text.ftsCreate({
|
|
193
|
+
sourceTable: "test_users",
|
|
194
|
+
columns: ["username", "bio"],
|
|
195
|
+
tableName: "temp_cm_fts_rebuild",
|
|
196
|
+
});
|
|
197
|
+
// Search BEFORE rebuild — should return 0 results
|
|
198
|
+
const before = await sqlite.text.ftsSearch({
|
|
199
|
+
table: "temp_cm_fts_rebuild",
|
|
200
|
+
query: "admin",
|
|
201
|
+
});
|
|
202
|
+
if (before.rows?.length > 0)
|
|
203
|
+
failures.push(
|
|
204
|
+
"FTS5 search returned results before rebuild — gotcha #5 may not apply",
|
|
205
|
+
);
|
|
206
|
+
// Rebuild to populate index
|
|
207
|
+
await sqlite.text.ftsRebuild({ table: "temp_cm_fts_rebuild" });
|
|
208
|
+
// Search AFTER rebuild — should return results
|
|
209
|
+
const after = await sqlite.text.ftsSearch({
|
|
210
|
+
table: "temp_cm_fts_rebuild",
|
|
211
|
+
query: "admin",
|
|
212
|
+
});
|
|
213
|
+
if (!after.rows || after.rows.length === 0)
|
|
214
|
+
failures.push("FTS5 search returned 0 results after rebuild");
|
|
215
|
+
// Cleanup
|
|
216
|
+
await sqlite.core.writeQuery("DROP TABLE IF EXISTS temp_cm_fts_rebuild");
|
|
217
|
+
return {
|
|
218
|
+
failures,
|
|
219
|
+
success: failures.length === 0,
|
|
220
|
+
beforeCount: before.rows?.length || 0,
|
|
221
|
+
afterCount: after.rows?.length || 0,
|
|
222
|
+
};
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Expected: `beforeCount: 0`, `afterCount: > 0` — validates that `ftsRebuild` is required after `ftsCreate` to populate the index (gotcha #5).
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Post-Test Procedures
|
|
230
|
+
|
|
231
|
+
1. **Cleanup**: Drop `temp_*` FTS tables
|
|
232
|
+
2. **Triage findings**: Create implementation plan if issues found
|
|
233
|
+
3. **Scope of fixes**: Handler code, server-instructions, this prompt
|
|
234
|
+
4. **Validate**: Instruct the user to run the test suite (Vitest/Playwright), lint, and typecheck. Do NOT run them yourself.
|
|
235
|
+
5. **Commit**: Stage and commit — do NOT push
|
|
236
|
+
6. **Token audit**: Report most expensive block
|
|
237
|
+
7. **Final summary**: After testing/re-testing
|