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
|
@@ -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
|
-
$
|
|
114
|
-
if (
|
|
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
|
|
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
|
-
$
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
Write-
|
|
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
|
|
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: ${
|
|
151
|
+
` No other group-specific help: ${coreNoOtherGroupHelp ? "✅" : "❌"} (count: ${coreHelpUris.length})`,
|
|
148
152
|
);
|
|
149
|
-
if (!
|
|
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
|
|
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
|
|
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 |
|