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
@@ -110,8 +110,8 @@ if ($backupFiles) {
110
110
  }
111
111
 
112
112
  # Warn about any node processes that hold the database file open (e.g. MCP servers)
113
- $dbFullPath = (Resolve-Path $DatabasePath -ErrorAction SilentlyContinue)?.Path
114
- if (-not $dbFullPath) { $dbFullPath = $DatabasePath }
113
+ $resolvePath = Resolve-Path $DatabasePath -ErrorAction SilentlyContinue
114
+ $dbFullPath = if ($resolvePath) { $resolvePath.Path } else { $DatabasePath }
115
115
  $dbNameEscaped = [regex]::Escape(($dbFullPath -replace '\\', '/'))
116
116
  $dbNameEscapedBackslash = [regex]::Escape($dbFullPath)
117
117
 
@@ -122,7 +122,7 @@ if ($nodeProcesses) {
122
122
  foreach ($proc in $nodeProcesses) {
123
123
  Write-Warn "Node process PID $($proc.ProcessId) is using this database (likely an MCP server)"
124
124
  }
125
- Write-Info "The database file will be replaced in-place existing connections will pick up the new data."
125
+ Write-Info "The database file will be replaced in-place - existing connections will pick up the new data."
126
126
  }
127
127
 
128
128
  $filesToDelete = @(
@@ -181,7 +181,7 @@ if (-not $useCli) {
181
181
  } else {
182
182
  # Use Node.js with better-sqlite3 via inline script
183
183
  $dbMcpRoot = Split-Path -Parent $ScriptDir
184
- $nodeScript = @"
184
+ $nodeScript = @'
185
185
  import Database from 'better-sqlite3';
186
186
  import { readFileSync } from 'fs';
187
187
 
@@ -198,7 +198,7 @@ try {
198
198
  console.error('ERROR:', err.message);
199
199
  process.exit(1);
200
200
  }
201
- "@
201
+ '@
202
202
 
203
203
  # Write temp script to project directory so node can resolve dependencies
204
204
  $tempScript = Join-Path $dbMcpRoot ".create-test-db.js"
@@ -269,7 +269,7 @@ if (-not $SkipVerify) {
269
269
  "_mcp_migrations"
270
270
  )
271
271
 
272
- $verifyScript = @"
272
+ $verifyScript = @'
273
273
  import Database from 'better-sqlite3';
274
274
  const db = new Database(process.argv[2], { readonly: true });
275
275
 
@@ -282,7 +282,7 @@ for (const table of tables) {
282
282
  }
283
283
 
284
284
  db.close();
285
- "@
285
+ '@
286
286
 
287
287
  $dbMcpRoot = Split-Path -Parent $ScriptDir
288
288
  # Write temp script to project directory so node can resolve dependencies
@@ -329,35 +329,77 @@ db.close();
329
329
  Write-Host "`n Artifact check:" -ForegroundColor Yellow
330
330
  $allTableNames = ($output | Where-Object { $_ -match "^TABLES:" }) -replace "^TABLES:", ""
331
331
  # Get ALL tables from sqlite_master, not just test_* ones
332
- $allTablesScript = @"
332
+ $allTablesScript = @'
333
333
  import Database from 'better-sqlite3';
334
334
  const db = new Database(process.argv[2], { readonly: true });
335
335
  const all = db.prepare("SELECT name, type FROM sqlite_master WHERE type IN ('table','view') ORDER BY name").all();
336
336
  for (const t of all) console.log('ALL:' + t.name);
337
337
  db.close();
338
- "@
338
+ '@
339
339
  $tempAllTables = Join-Path $dbMcpRoot ".check-artifacts.js"
340
340
  $allTablesScript | Out-File -FilePath $tempAllTables -Encoding utf8 -NoNewline
341
341
  $allOutput = node $tempAllTables $DatabasePath 2>&1
342
342
  Remove-Item $tempAllTables -Force -ErrorAction SilentlyContinue
343
343
 
344
+ $temporaryTables = @()
344
345
  $unexpectedTables = @()
346
+
345
347
  foreach ($line in $allOutput | Where-Object { $_ -match "^ALL:" }) {
346
348
  $name = ($line -replace "^ALL:", "").Trim()
347
349
  $isExpected = $expectedTables.ContainsKey($name) -or $knownSystemTables -contains $name
348
350
  if (-not $isExpected) {
349
- $unexpectedTables += $name
351
+ if ($name.StartsWith("stress_") -or $name.StartsWith("idx_stress_") -or $name.StartsWith("temp_") -or $name.StartsWith("idx_temp_")) {
352
+ $temporaryTables += $name
353
+ } else {
354
+ $unexpectedTables += $name
355
+ }
350
356
  }
351
357
  }
352
358
 
353
- if ($unexpectedTables.Count -gt 0) {
354
- Write-Warn "Found $($unexpectedTables.Count) unexpected table(s) — possible stale test artifacts:"
355
- foreach ($ut in $unexpectedTables) {
356
- Write-Host " [stale] " -ForegroundColor Yellow -NoNewline
357
- Write-Host $ut -ForegroundColor Gray
359
+ $allTablesToDrop = $temporaryTables + $unexpectedTables
360
+
361
+ if ($allTablesToDrop.Count -gt 0) {
362
+ if ($unexpectedTables.Count -gt 0) {
363
+ Write-Warn "Found $($unexpectedTables.Count) unexpected table(s) - stale test artifacts:"
364
+ }
365
+ if ($temporaryTables.Count -gt 0) {
366
+ Write-Info "Found $($temporaryTables.Count) temporary test table(s) (stress_*, temp_*) - cleaning up:"
367
+ }
368
+
369
+ $dropScript = "import Database from 'better-sqlite3'; const db = new Database(process.argv[2]); db.pragma('foreign_keys = OFF'); "
370
+
371
+ # First pass: drop main tables (non-shadow)
372
+ foreach ($ut in $allTablesToDrop) {
373
+ if ($ut -notmatch "_(data|idx|docsize|config|content|node|parent|rowid)$") {
374
+ $prefix = if ($unexpectedTables -contains $ut) { " [dropping] " } else { " [cleaning temp] " }
375
+ $color = if ($unexpectedTables -contains $ut) { "Yellow" } else { "Cyan" }
376
+ Write-Host $prefix -ForegroundColor $color -NoNewline
377
+ Write-Host $ut -ForegroundColor Gray
378
+ $dropScript += "try { db.exec('DROP TABLE IF EXISTS `"$ut`"'); } catch(e) {} "
379
+ }
380
+ }
381
+ # Second pass: drop any remaining tables (shadow tables left behind)
382
+ foreach ($ut in $allTablesToDrop) {
383
+ if ($ut -match "_(data|idx|docsize|config|content|node|parent|rowid)$") {
384
+ $prefix = if ($unexpectedTables -contains $ut) { " [dropping shadow] " } else { " [cleaning temp shadow] " }
385
+ $color = if ($unexpectedTables -contains $ut) { "DarkYellow" } else { "DarkCyan" }
386
+ Write-Host $prefix -ForegroundColor $color -NoNewline
387
+ Write-Host $ut -ForegroundColor Gray
388
+ $dropScript += "try { db.exec('DROP TABLE IF EXISTS `"$ut`"'); } catch(e) {} "
389
+ }
390
+ }
391
+ $dropScript += "db.pragma('foreign_keys = ON'); db.close();"
392
+
393
+ $tempDrop = Join-Path $dbMcpRoot ".drop-artifacts.js"
394
+ $dropScript | Out-File -FilePath $tempDrop -Encoding utf8 -NoNewline
395
+ node $tempDrop $DatabasePath | Out-Null
396
+ Remove-Item $tempDrop -Force -ErrorAction SilentlyContinue
397
+
398
+ if ($unexpectedTables.Count -gt 0) {
399
+ Write-Success "Cleaned up stale test artifacts"
400
+ } else {
401
+ Write-Success "Cleaned up temporary test tables"
358
402
  }
359
- Write-Info "These may be R-Tree shadow tables, _mcp_migrations, temp_* leftovers, or FTS tables from testing."
360
- Write-Info "Consider running a full reset to remove them."
361
403
  } else {
362
404
  Write-Success "No stale test artifacts found"
363
405
  }
@@ -0,0 +1,27 @@
1
+ # db-mcp Test Scripts
2
+
3
+ This directory contains integration test scripts that verify the functional parity and proper configuration of the `db-mcp` server.
4
+
5
+ ## Running the tests
6
+
7
+ You must run these scripts from the project root using Node. Ensure you have built the project (`npm run build`) before running them so that the compiled `dist/cli.js` reflects your latest changes.
8
+
9
+ ```bash
10
+ # Run from the project root:
11
+ npm run build
12
+
13
+ # 1. Test tool annotations and total tool count (Native Mode)
14
+ node test-server/scripts/test-tool-annotations.mjs
15
+
16
+ # 2. Test help resources and dynamic filtering
17
+ node test-server/scripts/test-help-resources.mjs
18
+
19
+ # 3. Test prompts registration and validation
20
+ node test-server/scripts/test-prompts.mjs
21
+ ```
22
+
23
+ ## What they verify
24
+
25
+ - **test-tool-annotations.mjs**: Spins up the server in Native Mode (`--sqlite-native`) and verifies that all 154 tools (151 Native + 3 built-in) are properly exposed, and that they all contain the required `openWorldHint: false` annotation for local database safety.
26
+ - **test-help-resources.mjs**: Starts the server with multiple `--tool-filter` configurations to verify that the core instructions remain slim (within context limits) and that the correct `sqlite://help/{group}` resources are dynamically registered only when their respective tool groups are enabled.
27
+ - **test-prompts.mjs**: Starts the server and verifies that all 10 MCP prompts are successfully registered via `prompts/list`, and performs functional execution checks on `prompts/get` handling required vs. optional arguments, payload structure correctness, and proper JSON-RPC error rendering.
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Usage:
10
10
  * npm run build
11
- * node test-server/test-help-resources.mjs
11
+ * node test-server/scripts/test-help-resources.mjs
12
12
  */
13
13
 
14
14
  import { spawn } from "child_process";
@@ -142,11 +142,15 @@ async function main() {
142
142
  console.log(` Has sqlite://help: ${coreHasBaseHelp ? "✅" : "❌"}`);
143
143
  if (!coreHasBaseHelp) allPassed = false;
144
144
 
145
- const coreNoGroupHelp = coreHelpUris.length === 1;
145
+ const coreHasCoreHelp = coreHelpUris.includes("sqlite://help/core");
146
+ console.log(` Has sqlite://help/core: ${coreHasCoreHelp ? "✅" : "❌"}`);
147
+ if (!coreHasCoreHelp) allPassed = false;
148
+
149
+ const coreNoOtherGroupHelp = coreHelpUris.length === 2;
146
150
  console.log(
147
- ` No group-specific help: ${coreNoGroupHelp ? "✅" : "❌"} (count: ${coreHelpUris.length})`,
151
+ ` No other group-specific help: ${coreNoOtherGroupHelp ? "✅" : "❌"} (count: ${coreHelpUris.length})`,
148
152
  );
149
- if (!coreNoGroupHelp) allPassed = false;
153
+ if (!coreNoOtherGroupHelp) allPassed = false;
150
154
 
151
155
  // ── Test 3: Stats Filter ──
152
156
  console.log("\n=== Test 3: Stats Filter (--tool-filter stats) ===\n");
@@ -183,6 +187,7 @@ async function main() {
183
187
 
184
188
  const expectedMulti = [
185
189
  "sqlite://help",
190
+ "sqlite://help/core",
186
191
  "sqlite://help/json",
187
192
  "sqlite://help/text",
188
193
  "sqlite://help/stats",
@@ -212,17 +217,19 @@ async function main() {
212
217
 
213
218
  const expectedFull = [
214
219
  "sqlite://help",
220
+ "sqlite://help/core",
215
221
  "sqlite://help/json",
216
222
  "sqlite://help/text",
217
223
  "sqlite://help/stats",
218
224
  "sqlite://help/vector",
219
225
  "sqlite://help/geo",
220
226
  "sqlite://help/admin",
227
+ "sqlite://help/transactions",
221
228
  "sqlite://help/introspection",
222
229
  "sqlite://help/migration",
223
230
  ];
224
231
  const fullHasAll = expectedFull.every((u) => fullHelpUris.includes(u));
225
- console.log(` Has all 9 help resources: ${fullHasAll ? "✅" : "❌"}`);
232
+ console.log(` Has all 11 help resources: ${fullHasAll ? "✅" : "❌"}`);
226
233
  if (!fullHasAll) allPassed = false;
227
234
 
228
235
  // ── Summary ──
@@ -0,0 +1,251 @@
1
+ import { spawn } from "child_process";
2
+ import { resolve, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const projectDir = resolve(__dirname, "../..");
7
+
8
+ const proc = spawn(
9
+ "node",
10
+ [
11
+ "dist/cli.js",
12
+ "--sqlite-native",
13
+ "./test-server/test.db",
14
+ "--log-level",
15
+ "error",
16
+ ],
17
+ {
18
+ cwd: projectDir,
19
+ stdio: ["pipe", "pipe", "pipe"],
20
+ },
21
+ );
22
+
23
+ let buffer = "";
24
+ const pending = new Map(); // id -> resolve
25
+
26
+ proc.stdout.on("data", (chunk) => {
27
+ buffer += chunk.toString();
28
+ const lines = buffer.split("\n");
29
+ buffer = lines.pop(); // keep incomplete
30
+ for (const line of lines) {
31
+ const trimmed = line.trim();
32
+ if (!trimmed) continue;
33
+ try {
34
+ const msg = JSON.parse(trimmed);
35
+ if (msg.id && pending.has(msg.id)) {
36
+ pending.get(msg.id)(msg);
37
+ pending.delete(msg.id);
38
+ }
39
+ } catch {}
40
+ }
41
+ });
42
+ proc.stderr.on("data", () => {});
43
+
44
+ let nextId = 1;
45
+ function rpc(method, params = {}) {
46
+ const id = nextId++;
47
+ return new Promise((resolve, reject) => {
48
+ const timer = setTimeout(() => {
49
+ pending.delete(id);
50
+ reject(new Error(`Timeout: ${method}`));
51
+ }, 20000);
52
+ pending.set(id, (msg) => {
53
+ clearTimeout(timer);
54
+ resolve(msg);
55
+ });
56
+ proc.stdin.write(
57
+ JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n",
58
+ );
59
+ });
60
+ }
61
+ function notify(method, params = {}) {
62
+ proc.stdin.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n");
63
+ }
64
+
65
+ const delay = (ms) => new Promise((r) => setTimeout(r, ms));
66
+
67
+ async function main() {
68
+ // Initialize
69
+ await rpc("initialize", {
70
+ protocolVersion: "2025-03-26",
71
+ capabilities: {},
72
+ clientInfo: { name: "test-prompts", version: "1.0" },
73
+ });
74
+ await delay(500);
75
+ notify("notifications/initialized");
76
+ await delay(1000);
77
+
78
+ // List prompts
79
+ const listResp = await rpc("prompts/list", {});
80
+ const prompts = listResp.result?.prompts ?? [];
81
+ console.log(`\n=== PROMPTS LIST: ${prompts.length} prompts ===`);
82
+ for (const p of prompts) {
83
+ const args = (p.arguments || [])
84
+ .map((a) => `${a.name}${a.required ? "*" : ""}`)
85
+ .join(", ");
86
+ console.log(` ${p.name}(${args})`);
87
+ }
88
+
89
+ const testCases = [
90
+ // No-argument prompts
91
+ { name: "sqlite_explain_schema", args: {}, expect: "" },
92
+ { name: "sqlite_optimization", args: {}, expect: "" },
93
+ { name: "sqlite_documentation", args: {}, expect: "" },
94
+
95
+ // Required-argument prompts
96
+ {
97
+ name: "sqlite_query_builder",
98
+ args: {
99
+ operation: "SELECT",
100
+ tables: "users",
101
+ description: "find active",
102
+ },
103
+ expect: "",
104
+ },
105
+ {
106
+ name: "sqlite_data_analysis",
107
+ args: { table: "users" },
108
+ expect: "",
109
+ },
110
+ {
111
+ name: "sqlite_migration",
112
+ args: { change: "add desc" },
113
+ expect: "",
114
+ },
115
+ {
116
+ name: "sqlite_debug_query",
117
+ args: { query: "SELECT * FROM test" },
118
+ expect: "",
119
+ },
120
+ {
121
+ name: "sqlite_summarize_table",
122
+ args: { table_name: "users" },
123
+ expect: "",
124
+ },
125
+ {
126
+ name: "sqlite_hybrid_search_workflow",
127
+ args: { use_case: "product_search" },
128
+ expect: "",
129
+ },
130
+ {
131
+ name: "sqlite_demo",
132
+ args: { topic: "sales" },
133
+ expect: "",
134
+ },
135
+
136
+ // Optional-argument prompts
137
+ {
138
+ name: "sqlite_data_analysis",
139
+ args: { table: "users", focus: "growth" },
140
+ expect: "",
141
+ },
142
+ {
143
+ name: "sqlite_migration",
144
+ args: { change: "add id", reversible: "true" },
145
+ expect: "",
146
+ },
147
+ ];
148
+
149
+ console.log(`\n=== TESTING ${testCases.length} PROMPT CALLS ===\n`);
150
+ let pass = 0,
151
+ fail = 0;
152
+
153
+ for (const tc of testCases) {
154
+ const argsStr = Object.keys(tc.args).length
155
+ ? JSON.stringify(tc.args)
156
+ : "(none)";
157
+ try {
158
+ const resp = await rpc("prompts/get", {
159
+ name: tc.name,
160
+ arguments: tc.args,
161
+ });
162
+ if (resp.error) {
163
+ console.log(
164
+ `FAIL ${tc.name}(${argsStr}): MCP error: ${JSON.stringify(resp.error)}`,
165
+ );
166
+ fail++;
167
+ continue;
168
+ }
169
+ const messages = resp.result?.messages;
170
+ const checks = [];
171
+ if (!Array.isArray(messages)) checks.push("messages not array");
172
+ else if (messages.length < 1) checks.push("messages empty");
173
+ else {
174
+ const m = messages[0];
175
+ if (m.role !== "user") checks.push(`role=${m.role}`);
176
+ if (!m.content) checks.push("no content");
177
+ else {
178
+ const c = m.content;
179
+ if (c.type !== "text") checks.push(`type=${c.type}`);
180
+ if (!c.text || c.text.length === 0) checks.push("text empty");
181
+ else if (
182
+ tc.expect &&
183
+ !c.text.toLowerCase().includes(tc.expect.toLowerCase())
184
+ ) {
185
+ checks.push(`missing '${tc.expect}' (len=${c.text.length})`);
186
+ }
187
+ }
188
+ }
189
+ if (checks.length === 0) {
190
+ const textLen = messages?.[0]?.content?.text?.length ?? 0;
191
+ console.log(
192
+ `PASS ${tc.name}(${argsStr}) — msgs:${messages.length}, textLen:${textLen}`,
193
+ );
194
+ pass++;
195
+ } else {
196
+ console.log(`FAIL ${tc.name}(${argsStr}): ${checks.join("; ")}`);
197
+ fail++;
198
+ }
199
+ } catch (err) {
200
+ console.log(`FAIL ${tc.name}(${argsStr}): ${err.message}`);
201
+ fail++;
202
+ }
203
+ }
204
+
205
+ // Error handling tests
206
+ try {
207
+ const resp = await rpc("prompts/get", {
208
+ name: "nonexistent-prompt",
209
+ arguments: {},
210
+ });
211
+ if (resp.error) {
212
+ console.log(
213
+ `PASS nonexistent-prompt: MCP error (code=${resp.error.code})`,
214
+ );
215
+ pass++;
216
+ } else {
217
+ console.log(`FAIL nonexistent-prompt: expected error`);
218
+ fail++;
219
+ }
220
+ } catch (err) {
221
+ console.log(`FAIL nonexistent-prompt: ${err.message}`);
222
+ fail++;
223
+ }
224
+
225
+ try {
226
+ const resp = await rpc("prompts/get", {
227
+ name: "sqlite_query_builder",
228
+ arguments: {},
229
+ });
230
+ if (resp.error) {
231
+ console.log(`PASS sqlite_query_builder(no args): error returned`);
232
+ } else {
233
+ console.log(`PASS sqlite_query_builder(no args): handled gracefully`);
234
+ }
235
+ pass++;
236
+ } catch (err) {
237
+ console.log(`FAIL sqlite_query_builder(no args): ${err.message}`);
238
+ fail++;
239
+ }
240
+
241
+ console.log(
242
+ `\n=== RESULTS: ${pass} pass, ${fail} fail (${pass + fail} total) ===`,
243
+ );
244
+ proc.kill();
245
+ process.exit(fail > 0 ? 1 : 0);
246
+ }
247
+
248
+ main().catch((err) => {
249
+ console.error(err);
250
+ process.exit(1);
251
+ });
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Usage:
9
9
  * npm run build
10
- * node test-server/test-tool-annotations.mjs
10
+ * node test-server/scripts/test-tool-annotations.mjs
11
11
  */
12
12
 
13
13
  import { spawn } from "child_process";
@@ -19,7 +19,7 @@ const proc = spawn(
19
19
  "dist/cli.js",
20
20
  "--instruction-level",
21
21
  "essential",
22
- "--sqlite",
22
+ "--sqlite-native",
23
23
  "./test-server/test.db",
24
24
  "--tool-filter",
25
25
  "+all",
@@ -90,12 +90,17 @@ proc.stdout.on("data", (chunk) => {
90
90
  }
91
91
 
92
92
  // Verification
93
+ const expectedCount = 154; // 151 Native tools + 3 built-in tools
94
+ const hasExpectedCount = tools.length === expectedCount;
93
95
  const allHaveAnnotations = missing === 0;
94
96
  const allAreFalse =
95
97
  openWorldTrue === 0 && openWorldFalse === tools.length;
96
98
 
97
99
  console.log(
98
- `\n All tools have annotations: ${allHaveAnnotations ? "✅" : "❌"}`,
100
+ `\n Has exactly ${expectedCount} tools: ${hasExpectedCount ? "✅" : "❌"} (actual: ${tools.length})`,
101
+ );
102
+ console.log(
103
+ ` All tools have annotations: ${allHaveAnnotations ? "✅" : "❌"}`,
99
104
  );
100
105
  console.log(
101
106
  ` All openWorldHint=false: ${allAreFalse ? "✅" : "⚠️ (some are true)"}`,
@@ -103,7 +108,7 @@ proc.stdout.on("data", (chunk) => {
103
108
 
104
109
  finished = true;
105
110
  proc.kill();
106
- process.exit(allHaveAnnotations ? 0 : 1);
111
+ process.exit(allHaveAnnotations && hasExpectedCount ? 0 : 1);
107
112
  }
108
113
  } catch {
109
114
  // Not complete JSON yet
@@ -0,0 +1,70 @@
1
+ # db-mcp Advanced Stress Tests
2
+
3
+ > **This document is optimized for AI agent consumption.** It provides context and execution rules for the advanced stress testing suite located in this directory.
4
+
5
+ This directory contains the "Second-Pass" advanced tests for the `db-mcp` tool groups. These tests simulate complex, edge-case, and boundary interactions using exclusively **Code Mode** (`sqlite_execute_code`).
6
+
7
+ ## Pre-requisites
8
+
9
+ 1. Basic deterministic tool group checklists (located in `../test-tool-groups/*.md`) MUST be successfully passed before running these advanced tests.
10
+ 2. Code Mode basic tests (located in `../test-tool-groups-codemode/*.md`) MUST be successfully passed.
11
+ 3. The testing database MUST be freshly seeded via `Set-Location C:\Users\chris\Desktop\db-mcp\test-server; .\reset-database.ps1` to ensure deterministic results.
12
+
13
+ ## File Inventory
14
+
15
+ | File | Primary Focus | Key Validations |
16
+ | ----------------------------------------- | ------------- | ----------------------------------------------------------------------------------------- |
17
+ | `test-codemode-advanced-core.md` | Core | Boundary values, empty states, state pollution, idempotency, error quality, payload sizes |
18
+ | `test-codemode-advanced-json.md` | JSON | Deep nesting, merge conflicts (RFC 7396), type coercion, write safety |
19
+ | `test-codemode-advanced-text.md` | Text | Regex edge cases, fuzzy/phonetic stress, FTS5 state integrity `[NATIVE ONLY]` |
20
+ | `test-codemode-advanced-stats.md` | Stats | Empty/single-row/NULL-heavy stats, extreme values, window functions `[NATIVE ONLY]` |
21
+ | `test-codemode-advanced-vector.md` | Vector | Empty tables, distance metrics, dimension mismatch, batch operations |
22
+ | `test-codemode-advanced-admin.md` | Admin | View lifecycle, virtual tables, backup/restore, pragma edge cases |
23
+ | `test-codemode-advanced-transactions.md` | Transactions | Abort recovery, savepoint stress, execute rollback `[NATIVE ONLY]` |
24
+ | `test-codemode-advanced-geo.md` | Geo | Haversine boundary conditions, nearby edge cases, SpatiaLite `[NATIVE ONLY]` |
25
+ | `test-codemode-advanced-introspection.md` | Introspection | Graph analysis, schema snapshot, storage/index audit, query plan depth |
26
+ | `test-codemode-advanced-migration.md` | Migration | Lifecycle, state pollution, SHA-256 dedup, error paths |
27
+
28
+ ## Agent Execution Protocol
29
+
30
+ 1. **Strict Code Mode Only:** All advanced stress tests must be executed entirely via `sqlite_execute_code`. Direct tool calls are forbidden unless specifically instructed for baseline comparison.
31
+ 2. **Sequential Grouping:** Execute only **one markdown file at a time**. Report findings, fix errors, apply updates to the changelog, and commit before advancing to the next file.
32
+ 3. **Payload Optimization (Token Monitoring):** Monitor `metrics.tokenEstimate` on every response. If extremely large unbounded responses are produced, flag as 📦 **Payload Issue**.
33
+ 4. **Structured Error Adherence:** When testing boundary failure parameters, assert that the handler outputs a proper structured error (`{success: false, error: "..."}`) rather than leaking raw SQLite errors.
34
+ 5. **No Persistent Pollution:** After finishing a file, verify all `stress_*` tables/views/indexes are dropped. No test state should bleed into the next run.
35
+ 6. **Code Over Docs (When Standards Violated)**: If the code deviates from established standards (e.g., throwing raw MCP errors instead of Structured Errors, or failing Zod validation), **fix the handler code**. Do not modify documentation, prompts, or `gotchas.md` to accommodate buggy code.
36
+ 7. **Documentation Parity & Test Prompt Integrity**: Only update files in `src/constants/server-instructions` (or test prompts) if the code's behavior is mathematically/logically correct and intended, but the documentation is inaccurate, outdated, or lacking specificity. You SHOULD directly edit the markdown test files in this directory to fix factual errors, broken code blocks, or incorrect test assertions.
37
+ 8. **Testing Limits**: Do not run build or tests automatically (`npm run lint`, `npm run typecheck`, `npm run test:e2e`, `vitest`, or `playwright`). The user will execute them manually. When you reach the validate step, explicitly instruct the user to run the validations.
38
+
39
+ ### 9. WASM Mode Execution
40
+
41
+ When testing against a **WASM backend** (`--sqlite` flag, sql.js adapter), follow these additional rules:
42
+
43
+ #### Skip Rules
44
+
45
+ - **`[NATIVE ONLY]` items**: Skip all categories and individual test items annotated with `[NATIVE ONLY]`.
46
+ - **Transactions prompt**: Skip entirely — all 8 transaction tools are `[NATIVE ONLY]`.
47
+ - **Window function items** (stats Category 4): Skip — 6 window tools are Native-only.
48
+ - **SpatiaLite items** (geo Category 5): Skip — 7 SpatiaLite tools are Native-only.
49
+ - **FTS5 items** (text Category 6): Skip — 5 FTS5 tools are Native-only.
50
+
51
+ #### Graceful Degradation (Don't Skip — Validate Errors)
52
+
53
+ Several admin tools are **registered in WASM mode but return structured errors**. Test these as **negative validation**:
54
+
55
+ | Tool | Expected WASM Behavior |
56
+ | ------------------------------------ | ---------------------------------------------- |
57
+ | `sqlite.admin.backup(...)` | `{success: false, error: "...WASM mode"}` |
58
+ | `sqlite.admin.restore(...)` | `{success: false, error: "...WASM mode"}` |
59
+ | `sqlite.admin.verifyBackup(...)` | `{success: false, error: "...WASM mode"}` |
60
+ | `sqlite.admin.createCsvTable(...)` | `{success: false}` — CSV extension unavailable |
61
+ | `sqlite.admin.analyzeCsvSchema(...)` | `{success: false}` — CSV extension unavailable |
62
+ | `sqlite.admin.createRtreeTable(...)` | `{success: false}` — R-Tree module unavailable |
63
+
64
+ #### Adjusted Expectations
65
+
66
+ | Item | Native Behavior | WASM Behavior |
67
+ | ---------------------------------------------------- | --------------------------- | -------------------------------- |
68
+ | `sqlite.admin.dbstat({summarize: true})` | Per-table storage breakdown | Counts-only (JS fallback) |
69
+ | `sqlite.admin.pragmaCompileOptions({filter: "FTS"})` | Matches FTS5 | Matches FTS3 |
70
+ | `test_articles_fts` in `listVirtualTables` | Present and queryable | May appear but FTS5 queries fail |