db-mcp 4.0.0 → 5.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/README.md +30 -25
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/database-adapter.d.ts +9 -0
- package/dist/adapters/database-adapter.d.ts.map +1 -1
- package/dist/adapters/sqlite/query-executor.d.ts.map +1 -1
- package/dist/adapters/sqlite/read-write-lock.d.ts +49 -0
- package/dist/adapters/sqlite/read-write-lock.d.ts.map +1 -0
- package/dist/adapters/sqlite/schemas/admin.d.ts +5 -5
- package/dist/adapters/sqlite/schemas/admin.d.ts.map +1 -1
- package/dist/adapters/sqlite/schemas/core.d.ts +94 -0
- package/dist/adapters/sqlite/schemas/core.d.ts.map +1 -1
- package/dist/adapters/sqlite/schemas/introspection.d.ts +8 -7
- package/dist/adapters/sqlite/schemas/introspection.d.ts.map +1 -1
- package/dist/adapters/sqlite/schemas/migration.d.ts +2 -2
- package/dist/adapters/sqlite/schemas/migration.d.ts.map +1 -1
- package/dist/adapters/sqlite/sqlite-adapter.d.ts +6 -0
- package/dist/adapters/sqlite/sqlite-adapter.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/admin/backup/create.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/admin/backup/dump.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/admin/backup/restore.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/admin/pragma.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/admin/verify.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/codemode.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/core/convenience.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/core/index.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/core/queries.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/core/versioning.d.ts +19 -0
- package/dist/adapters/sqlite/tools/core/versioning.d.ts.map +1 -0
- package/dist/adapters/sqlite/tools/introspection/graph/tools.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/json-operations/crud.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/migration/helpers.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/vector/metadata.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/vector/search.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/vector/storage.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/virtual/vtable/analyze-csv.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/virtual/vtable/csv.d.ts.map +1 -1
- package/dist/adapters/sqlite/tools/virtual/vtable/info.d.ts.map +1 -1
- package/dist/adapters/sqlite/types.d.ts +1 -1
- package/dist/adapters/sqlite/types.d.ts.map +1 -1
- package/dist/adapters/sqlite-native/index.d.ts +1 -1
- package/dist/adapters/sqlite-native/index.d.ts.map +1 -1
- package/dist/adapters/sqlite-native/tools/window/helpers.d.ts +52 -0
- package/dist/adapters/sqlite-native/tools/window/helpers.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/index.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/index.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/lag-lead.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/lag-lead.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/moving-average.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/moving-average.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/ntile.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/ntile.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/rank.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/rank.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/row-number.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/row-number.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/running-total.d.ts +7 -0
- package/dist/adapters/sqlite-native/tools/window/running-total.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/tools/window/schemas.d.ts +159 -0
- package/dist/adapters/sqlite-native/tools/window/schemas.d.ts.map +1 -0
- package/dist/adapters/sqlite-native/transaction-methods.d.ts.map +1 -1
- package/dist/audit/logger.d.ts.map +1 -1
- package/dist/auth/authorization-server-discovery.d.ts.map +1 -1
- package/dist/auth/scopes/mapping.d.ts.map +1 -1
- package/dist/auth/token-validator.d.ts.map +1 -1
- package/dist/{chunk-EQF3WZHX.js → chunk-27C5EE6U.js} +15 -2
- package/dist/{chunk-R5M4WKDM.js → chunk-2TYICG7B.js} +381 -185
- package/dist/{chunk-NIC5Z2CA.js → chunk-LLIAIJPO.js} +394 -381
- package/dist/{chunk-TGE7LFAR.js → chunk-UE7GVOBV.js} +1069 -355
- package/dist/{chunk-Y57WFKAP.js → chunk-XLIYO3YA.js} +178 -72
- package/dist/cli.js +18 -6
- package/dist/codemode/api-constants.d.ts.map +1 -1
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/sandbox-factory.d.ts.map +1 -1
- package/dist/codemode/sandbox.d.ts +0 -3
- package/dist/codemode/sandbox.d.ts.map +1 -1
- package/dist/codemode/security.d.ts.map +1 -1
- package/dist/codemode/types.d.ts +2 -0
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/constants/server-instructions.d.ts.map +1 -1
- package/dist/filtering/tool-constants.d.ts +5 -5
- package/dist/filtering/tool-constants.d.ts.map +1 -1
- package/dist/{http-2WGR3E5K.js → http-ZDMK46QU.js} +183 -32
- package/dist/index.js +4 -4
- package/dist/observability/system-db.d.ts.map +1 -1
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/registration/admin-tools.d.ts.map +1 -1
- package/dist/server/registration/audit-tools/backup.d.ts +8 -0
- package/dist/server/registration/audit-tools/backup.d.ts.map +1 -0
- package/dist/server/registration/audit-tools/helpers.d.ts +11 -0
- package/dist/server/registration/audit-tools/helpers.d.ts.map +1 -0
- package/dist/server/registration/audit-tools/index.d.ts +4 -0
- package/dist/server/registration/audit-tools/index.d.ts.map +1 -0
- package/dist/server/registration/audit-tools/resource.d.ts +8 -0
- package/dist/server/registration/audit-tools/resource.d.ts.map +1 -0
- package/dist/server/registration/audit-tools/search.d.ts +7 -0
- package/dist/server/registration/audit-tools/search.d.ts.map +1 -0
- package/dist/server/registration/index.d.ts +1 -1
- package/dist/server/registration/index.d.ts.map +1 -1
- package/dist/{sqlite-D4WCNPU4.js → sqlite-XWZPEZJO.js} +192 -19
- package/dist/{sqlite-native-DLVS2T52.js → sqlite-native-5GQ3WKCZ.js} +156 -117
- package/dist/transports/http/middleware.d.ts.map +1 -1
- package/dist/transports/http/oauth.d.ts.map +1 -1
- package/dist/transports/http/sessions/legacy-sse.d.ts.map +1 -1
- package/dist/transports/http/sessions/stateful.d.ts +5 -1
- package/dist/transports/http/sessions/stateful.d.ts.map +1 -1
- package/dist/transports/http/transport.d.ts +2 -0
- package/dist/transports/http/transport.d.ts.map +1 -1
- package/dist/transports/http/types.d.ts +9 -0
- package/dist/transports/http/types.d.ts.map +1 -1
- package/dist/types/auth.d.ts +4 -0
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/server.d.ts +3 -0
- package/dist/types/server.d.ts.map +1 -1
- package/dist/utils/annotations.d.ts +8 -0
- package/dist/utils/annotations.d.ts.map +1 -1
- package/dist/utils/errors/categories.d.ts +16 -11
- package/dist/utils/errors/categories.d.ts.map +1 -1
- package/dist/utils/errors/classes.d.ts +37 -0
- package/dist/utils/errors/classes.d.ts.map +1 -1
- package/dist/utils/errors/format.d.ts +2 -2
- package/dist/utils/errors/format.d.ts.map +1 -1
- package/dist/utils/errors/index.d.ts +1 -1
- package/dist/utils/errors/index.d.ts.map +1 -1
- package/dist/utils/errors/suggestions.d.ts +1 -0
- package/dist/utils/errors/suggestions.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/security-utils.d.ts +29 -0
- package/dist/utils/security-utils.d.ts.map +1 -0
- package/dist/utils/stream-utils.d.ts +14 -0
- package/dist/utils/stream-utils.d.ts.map +1 -0
- package/dist/utils/where-clause.d.ts +0 -13
- package/dist/utils/where-clause.d.ts.map +1 -1
- package/package.json +35 -38
- package/dist/adapters/sqlite-native/tools/window.d.ts +0 -7
- package/dist/adapters/sqlite-native/tools/window.d.ts.map +0 -1
- package/dist/server/registration/audit-tools.d.ts +0 -17
- package/dist/server/registration/audit-tools.d.ts.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VERSION, NAME, READ_ONLY, SERVER_ICONS, ASSISTANT_FOCUSED, redactObject, getRequiredScope, estimateTokens, AuditListBackupsOutputSchema, AuditListBackupsSchema, AuditGetBackupOutputSchema, AuditCleanupOutputSchema, AuditDiffBackupOutputSchema, AuditRestoreBackupOutputSchema, AuditSearchOutputSchema, AuditSearchSchema } from './chunk-
|
|
2
|
-
import { parseToolFilter, getToolFilterFromEnv,
|
|
1
|
+
import { VERSION, NAME, READ_ONLY, SERVER_ICONS, ASSISTANT_FOCUSED, toSDK, redactObject, getRequiredScope, estimateTokens, AuditListBackupsOutputSchema, AuditListBackupsSchema, AuditGetBackupOutputSchema, AuditCleanupOutputSchema, AuditDiffBackupOutputSchema, AuditRestoreBackupOutputSchema, AuditSearchOutputSchema, AuditSearchSchema } from './chunk-2TYICG7B.js';
|
|
2
|
+
import { ErrorCategory, logger, parseToolFilter, getToolFilterFromEnv, ValidationError, getFilterSummary, DbMcpError, getAuthContext, scopesGrantToolAccess, registerToolScopes, metrics, InsufficientScopeError, formatHandlerError, InternalError, sanitizeErrorMessage } from './chunk-XLIYO3YA.js';
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
5
|
import { dirname, join, basename, sep } from 'path';
|
|
@@ -34,9 +34,7 @@ Read \`sqlite://help\` for gotchas and critical usage patterns.
|
|
|
34
34
|
Read \`sqlite://help/{group}\` for group-specific tool reference (core, json, text, stats, vector, geo, admin, transactions, introspection, migration).
|
|
35
35
|
Only help resources for your enabled tool groups are registered.`;
|
|
36
36
|
var HELP_CONTENT = /* @__PURE__ */ new Map([
|
|
37
|
-
[
|
|
38
|
-
"admin",
|
|
39
|
-
`# db-mcp Help \u2014 Database Administration (31N/30W tools) + Server Audit (7 tools)
|
|
37
|
+
["admin", `# db-mcp Help \u2014 Database Administration (31N/30W tools) + Server Audit (7 tools)
|
|
40
38
|
|
|
41
39
|
## Maintenance
|
|
42
40
|
|
|
@@ -45,7 +43,7 @@ sqlite_integrity_check({ maxErrors: 10 }); // check for corruption
|
|
|
45
43
|
sqlite_optimize({ analyze: true, reindex: true }); // optimize performance
|
|
46
44
|
sqlite_vacuum(); // reclaim space
|
|
47
45
|
sqlite_analyze({ table: "orders" }); // update statistics for query planner
|
|
48
|
-
sqlite_dbstat(
|
|
46
|
+
sqlite_dbstat(); // storage stats (\u26A0\uFE0F summarize native-only; WASM returns counts only)
|
|
49
47
|
sqlite_reindex(); // rebuild all indexes
|
|
50
48
|
sqlite_reindex({ target: "idx_users_email" }); // rebuild specific index
|
|
51
49
|
sqlite_reindex({ target: "orders" }); // rebuild all indexes for a table
|
|
@@ -53,6 +51,8 @@ sqlite_reindex({ target: "orders" }); // rebuild all indexes for a table
|
|
|
53
51
|
|
|
54
52
|
## Backup/Restore (Native only)
|
|
55
53
|
|
|
54
|
+
\u26A0\uFE0F Paths must be absolute and within explicitly authorized \`ALLOWED_IO_ROOTS\`.
|
|
55
|
+
|
|
56
56
|
\`\`\`javascript
|
|
57
57
|
sqlite_backup({ targetPath: "/path/to/backup.db" });
|
|
58
58
|
sqlite_vacuum_into({ outputPath: "/path/to/compact.db" }); // create defragmented copy
|
|
@@ -70,7 +70,7 @@ sqlite_audit_get_backup({ filename: "snapshot_123.json" }); // retrieve specific
|
|
|
70
70
|
sqlite_audit_diff_backup({ filename: "snapshot_123.json" }); // compare snapshot against live schema
|
|
71
71
|
sqlite_audit_restore_backup({ filename: "snapshot_123.json", dryRun: true }); // restore schema from snapshot
|
|
72
72
|
sqlite_audit_cleanup(); // apply retention policy and delete old snapshots
|
|
73
|
-
sqlite_audit_search({
|
|
73
|
+
sqlite_audit_search({ tool: "sqlite_drop_table", limit: 10 }); // search and filter structured audit logs
|
|
74
74
|
\`\`\`
|
|
75
75
|
|
|
76
76
|
## PRAGMA
|
|
@@ -81,7 +81,7 @@ sqlite_pragma_settings({ pragma: "cache_size", value: 10000 }); // set value
|
|
|
81
81
|
sqlite_pragma_table_info({ table: "users" }); // column details
|
|
82
82
|
sqlite_pragma_compile_options({ filter: "FTS" }); // \u26A0\uFE0F WASM may show FTS3, not FTS5
|
|
83
83
|
sqlite_pragma_database_list(); // list attached databases
|
|
84
|
-
sqlite_attach_database({ filepath: "/path/to/other.db", alias: "archive" }); // attach external DB
|
|
84
|
+
sqlite_attach_database({ filepath: "/path/to/other.db", alias: "archive" }); // attach external DB (\u26A0\uFE0F subject to ALLOWED_IO_ROOTS)
|
|
85
85
|
sqlite_detach_database({ alias: "archive" }); // detach DB
|
|
86
86
|
sqlite_pragma_optimize(); // run PRAGMA optimize
|
|
87
87
|
\`\`\`
|
|
@@ -137,7 +137,7 @@ sqlite_create_rtree_table({ tableName: "locations_idx", dimensions: 2 }); // 2D:
|
|
|
137
137
|
|
|
138
138
|
## CSV Virtual Tables (Native only)
|
|
139
139
|
|
|
140
|
-
\u26A0\uFE0F Requires ABSOLUTE file paths
|
|
140
|
+
\u26A0\uFE0F Requires ABSOLUTE file paths that must fall within explicitly authorized \`ALLOWED_IO_ROOTS\`.
|
|
141
141
|
|
|
142
142
|
\`\`\`javascript
|
|
143
143
|
sqlite_analyze_csv_schema({ filePath: "/absolute/path/to/data.csv" }); // analyze CSV structure
|
|
@@ -145,16 +145,13 @@ sqlite_create_csv_table({
|
|
|
145
145
|
tableName: "csv_data",
|
|
146
146
|
filePath: "/absolute/path/to/data.csv",
|
|
147
147
|
});
|
|
148
|
-
\`\`\``
|
|
149
|
-
|
|
150
|
-
[
|
|
151
|
-
"core",
|
|
152
|
-
`# db-mcp Help \u2014 Core Operations (21 tools)
|
|
148
|
+
\`\`\``],
|
|
149
|
+
["core", `# db-mcp Help \u2014 Core Operations (25 tools)
|
|
153
150
|
|
|
154
151
|
## Basic Queries
|
|
155
152
|
|
|
156
|
-
- \`sqlite_read_query({ query: "SELECT * FROM users LIMIT 10", cursor: "..." })\` \u2014 execute SELECT, PRAGMA, EXPLAIN, or WITH statements. Supports \`cursor\` for offset-based pagination (returns \`nextCursor\`). **Agent Tip:** Avoid \`SELECT *\` on wide tables with large text/JSON columns to conserve token context; use \`sqlite_describe_table\` first and select specific columns.
|
|
157
|
-
- \`sqlite_write_query({ query: "INSERT INTO users (name) VALUES ('Alice')" })\` \u2014 execute INSERT, UPDATE, DELETE, REPLACE, or trigger DDL (CREATE/DROP TRIGGER)
|
|
153
|
+
- \`sqlite_read_query({ query: "SELECT * FROM users LIMIT 10", cursor: "...", stream: true, chunkSize: 10 })\` \u2014 execute SELECT, PRAGMA, EXPLAIN, or WITH statements. Supports \`cursor\` for offset-based pagination (returns \`nextCursor\`). Set \`stream: true\` to return row-by-row chunks via progress notifications instead of full response buffering (requires client progressToken support; gracefully falls back if unavailable). **Agent Tip:** Avoid \`SELECT *\` on wide tables with large text/JSON columns to conserve token context; use \`sqlite_describe_table\` first and select specific columns.
|
|
154
|
+
- \`sqlite_write_query({ query: "INSERT INTO users (name) VALUES ('Alice')" })\` \u2014 execute INSERT, UPDATE, DELETE, REPLACE, or trigger DDL (CREATE/DROP TRIGGER). Supports \`expectedVersion\` for OCC.
|
|
158
155
|
|
|
159
156
|
## Tables & Schema
|
|
160
157
|
|
|
@@ -174,19 +171,30 @@ sqlite_create_csv_table({
|
|
|
174
171
|
- \`sqlite_create_index({ indexName: "idx_users_email", table: "users", columns: ["email"], unique?: true })\` \u2014 create a new index
|
|
175
172
|
- \`sqlite_drop_index({ indexName: "idx_users_email", ifExists?: true })\` \u2014 drop an existing index
|
|
176
173
|
|
|
174
|
+
## Versioning & Concurrency (OCC)
|
|
175
|
+
|
|
176
|
+
- \`sqlite_enable_versioning({ table: "users" })\` \u2014 add a \`_version\` column and a BEFORE UPDATE trigger to enforce optimistic concurrency control on a table
|
|
177
|
+
- \`sqlite_disable_versioning({ table: "users" })\` \u2014 remove the \`_version\` column and concurrency trigger
|
|
178
|
+
- \`sqlite_check_version({ table: "users", rowId: 1 })\` \u2014 get the current \`_version\` of a row
|
|
179
|
+
- \`sqlite_conditional_update({ table: "users", conditions: [{ column: "id", operator: "=", value: 1 }], expectedVersion: 2, data: { name: "Bob" } })\` \u2014 safely update a row, incrementing \`_version\` atomically. Will fail if \`expectedVersion\` does not match
|
|
180
|
+
|
|
177
181
|
## Convenience Tools (High-Level Data Operations)
|
|
178
182
|
|
|
179
|
-
- \`sqlite_upsert({ table: "users", data: { id: 1, name: "Alice" }, conflictColumns: ["id"], updateColumns: ["name"], returning: true })\` \u2014 insert or update a row using \`ON CONFLICT\` (or \`REPLACE\` fallback). Supports \`returning: true\` or array of columns.
|
|
183
|
+
- \`sqlite_upsert({ table: "users", data: { id: 1, name: "Alice" }, conflictColumns: ["id"], updateColumns: ["name"], returning: true })\` \u2014 insert or update a row using \`ON CONFLICT\` (or \`REPLACE\` fallback). Supports \`returning: true\` or array of columns, and \`expectedVersion\` for OCC.
|
|
180
184
|
- \`sqlite_batch_insert({ table: "users", rows: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }], returning: true })\` \u2014 insert multiple rows in a single batch. Supports \`returning: true\` or array of columns.
|
|
181
185
|
- \`sqlite_count({ table: "users", where?: "status = 'active'" })\` \u2014 count rows in a table (faster than a full query)
|
|
182
186
|
- \`sqlite_exists({ table: "users", where: "email = 'test@example.com'" })\` \u2014 check if a row exists (stops at first match)
|
|
183
187
|
- \`sqlite_truncate({ table: "users" })\` \u2014 quickly delete all rows from a table (executes \`DELETE FROM table\`)
|
|
184
188
|
- \`sqlite_date_add({ table: "users", column: "created_at", amount: 7, unit: "days", whereClause: "id = 1" })\` \u2014 add or subtract time intervals from a date column. By default returns only the computed column; use \`selectColumns\` to return additional context.
|
|
185
|
-
- \`sqlite_date_diff({ table: "users", column1: "ended_at", column2: "started_at", unit: "days", whereClause: "id = 1" })\` \u2014 calculate the difference between two date columns. By default returns only the computed column; use \`selectColumns\` to return additional context
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
- \`sqlite_date_diff({ table: "users", column1: "ended_at", column2: "started_at", unit: "days", whereClause: "id = 1" })\` \u2014 calculate the difference between two date columns. By default returns only the computed column; use \`selectColumns\` to return additional context.
|
|
190
|
+
|
|
191
|
+
## \u26A0\uFE0F Core Gotchas
|
|
192
|
+
|
|
193
|
+
1. **sqlite_write_query**: DML only (INSERT/UPDATE/DELETE/REPLACE) \u2014 use \`sqlite_read_query\` for SELECT, and dedicated DDL tools for schema modifications.
|
|
194
|
+
2. **sqlite_upsert**: Always specify \`conflictColumns\` \u2014 without it, falls back to \`REPLACE\` which deletes and re-inserts the row, potentially losing columns not included in \`data\`.
|
|
195
|
+
3. **Optimistic Concurrency Control (OCC)**: When a table is versioned (\`sqlite_enable_versioning\`), you MUST supply \`expectedVersion\` to \`sqlite_write_query\`, \`sqlite_upsert\`, and \`sqlite_conditional_update\`. Omitting it will result in a \`ConflictError\` to strictly prevent lost updates.
|
|
196
|
+
4. **sqlite_batch_insert**: All rows must have the same keys \u2014 inconsistent column sets across rows will cause errors or unexpected NULLs.`],
|
|
197
|
+
["geo", `# db-mcp Help \u2014 Geospatial Operations (11N/4W: 4 basic + 7 SpatiaLite [NATIVE ONLY])
|
|
190
198
|
|
|
191
199
|
## Basic Geo (always available \u2014 Haversine formula)
|
|
192
200
|
|
|
@@ -291,36 +299,15 @@ sqlite_spatialite_index({
|
|
|
291
299
|
geometryColumn: "geom",
|
|
292
300
|
action: "create",
|
|
293
301
|
}); // create, drop, or check
|
|
294
|
-
\`\`\``
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
2. **Regex patterns**: Double-escape backslashes (\`\\\\\\\\\`) when passing through JSON/MCP
|
|
304
|
-
3. **FTS5 virtual tables**: \`*_fts\` and shadow tables \`*_fts_*\` are hidden from \`sqlite_list_tables\` for cleaner output
|
|
305
|
-
4. **FTS5 boolean logic**: Uses AND by default \u2014 \`"machine learning"\` = rows with BOTH words. Use OR explicitly: \`"machine OR learning"\`
|
|
306
|
-
5. **json_each row multiplication**: Expands arrays to rows \u2014 use \`limit\` param for large arrays
|
|
307
|
-
6. **json_group_object without groupByColumn**: Each row creates a key-value pair; duplicate keys result if key values aren't unique
|
|
308
|
-
7. **allowExpressions**: For column extraction ONLY (e.g., \`json_extract\`), NOT aggregate functions \u2014 use \`aggregateFunction\` param instead
|
|
309
|
-
8. **sqlite_json_normalize_column**: Defaults to \`preserve\` (maintains original format); use \`outputFormat: 'text'\` to force text
|
|
310
|
-
9. **Fuzzy matching tokenization**: Matches WORD TOKENS by default \u2014 \`"laptop"\` matches \`"Laptop Pro 15"\` (distance 0 on first token). Use \`tokenize: false\` for full-string matching
|
|
311
|
-
10. **SpatiaLite distances**: \`nearest_neighbor\`/\`distance_matrix\` return CARTESIAN distance (degrees), not geodetic (km/miles)
|
|
312
|
-
11. **SpatiaLite buffer**: Auto-simplifies output by default (tolerance=0.0001). Use \`simplifyTolerance: 0\` to disable
|
|
313
|
-
12. **sqlite_stats_top_n**: Returns all columns by default which creates large payloads for wide tables \u2014 always pass \`selectColumns\` to control output size
|
|
314
|
-
13. **CSV virtual tables**: Require ABSOLUTE file paths
|
|
315
|
-
14. **sqlite_create_series_table**: Creates a REGULAR table (not virtual) \u2014 use \`sqlite_drop_table\` to remove
|
|
316
|
-
15. **sqlite_dbstat**: \`summarize\` only works in native; WASM returns counts only
|
|
317
|
-
16. **PRAGMA compile options**: WASM may show FTS3, not FTS5
|
|
318
|
-
17. **Vector tool schemas**: Vector tools use distinct schemas for specific operations. E.g., \`sqlite.vector.dimensions\` requires \`vectorColumn\`. Additionally, \`sqlite.vector.get\` wraps metadata inside a \`metadata\` object (e.g., \`metadata.content\`), and \`sqlite.vector.stats\` returns \`sampleSize\` and \`magnitudeStats\` (not \`count\` and \`stats\`).
|
|
319
|
-
18. **FTS5 trigger cleanup**: Dropping an FTS5 table with \`sqlite_drop_table\` automatically finds and removes the associated \`_ai\`, \`_ad\`, and \`_au\` sync triggers from the source table.
|
|
320
|
-
19. **sqlite_batch_insert**: All rows must have the same keys \u2014 inconsistent column sets across rows will cause errors or unexpected NULLs
|
|
321
|
-
20. **sqlite_schema_diff**: \`baseline\` and \`target\` accept either the string \`"current"\` (queries live DB) or an inline snapshot object from a prior \`sqlite_schema_snapshot\` call. At least one side must be \`"current"\` unless doing an offline comparison
|
|
322
|
-
21. **sqlite_upsert**: Always specify \`conflictColumns\` \u2014 without it, falls back to \`REPLACE\` which deletes and re-inserts the row, potentially losing columns not included in \`data\`
|
|
323
|
-
22. **Resource Subscriptions**: The \`sqlite://schema\` and \`sqlite://health\` resources support MCP subscriptions, allowing the client to receive real-time push notifications when DDL changes occur or health metrics update without needing to poll.
|
|
302
|
+
\`\`\``],
|
|
303
|
+
["gotchas", `# db-mcp Help \u2014 Gotchas & Code Mode
|
|
304
|
+
|
|
305
|
+
## Server-Level Rules
|
|
306
|
+
|
|
307
|
+
1. **CSV virtual tables & Backups**: Require ABSOLUTE file paths. Operations will be strictly blocked if paths do not fall within the explicitly authorized \`ALLOWED_IO_ROOTS\` directory list. Unconfigured stdio transports default to no filesystem access.
|
|
308
|
+
2. **Resource Subscriptions**: The \`sqlite://schema\` and \`sqlite://health\` resources support MCP subscriptions, allowing the client to receive real-time push notifications when DDL changes occur or health metrics update without needing to poll.
|
|
309
|
+
3. **HTTP Session Timeouts**: Stateful HTTP sessions automatically expire after 30 minutes of inactivity, or 24 hours total. Sessions are swept automatically; ensure client workflows re-authenticate or handle broken sessions gracefully.
|
|
310
|
+
4. **Structured Errors**: The server returns strongly-typed JSON errors instead of generic stack traces. Execution timeouts surface as recoverable \`TimeoutError\` (category: \`timeout\`). Rate limit violations surface as \`RateLimitError\` (category: \`rate_limit\`). Input/schema rejections surface as \`ValidationError\` (category: \`validation\`). Use this metadata (\`code\`, \`category\`, \`recoverable\`) to programmatically retry or backoff.
|
|
324
311
|
|
|
325
312
|
## WASM vs Native
|
|
326
313
|
|
|
@@ -347,22 +334,19 @@ sqlite_spatialite_index({
|
|
|
347
334
|
**Usage**: \`sqlite_execute_code({ code: "const tables = await sqlite.core.listTables(); return tables;" })\`
|
|
348
335
|
**Discover**: \`sqlite.help()\` for all groups, \`sqlite.<group>.help()\` for methods.
|
|
349
336
|
**Progress**: Use \`await sqlite.reportProgress(current, total, "Message")\` for custom long-running tasks.
|
|
350
|
-
**Groups**: \`sqlite.core\`, \`sqlite.json\`, \`sqlite.text\`, \`sqlite.stats\`, \`sqlite.vector\`, \`sqlite.admin\`, \`sqlite.transactions\` (Native-only), \`sqlite.geo\`, \`sqlite.introspection\`, \`sqlite.migration\`
|
|
337
|
+
**Groups**: \`sqlite.core\`, \`sqlite.json\`, \`sqlite.text\`, \`sqlite.stats\`, \`sqlite.vector\`, \`sqlite.admin\`, \`sqlite.transactions\` (Native-only), \`sqlite.geo\`, \`sqlite.introspection\`, \`sqlite.migration\` (opt-in)
|
|
351
338
|
|
|
352
339
|
> **Note**: Code Mode dynamically filters capabilities. In WASM environments, \`sqlite.help()\` will omit unsupported groups (e.g., \`transactions\`) and tools (e.g., FTS5) to accurately reflect the active runtime.
|
|
353
340
|
|
|
354
341
|
## Code Mode API Mapping
|
|
355
342
|
|
|
356
|
-
|
|
357
|
-
**Exception**: \`stats\` and \`migration\` keep their prefix: \`sqlite_stats_basic\` \u2192 \`sqlite.stats.statsBasic()\`, \`sqlite_migration_apply\` \u2192 \`sqlite.migration.migrationApply()\`
|
|
343
|
+
Code Mode maps standard tools to an object-oriented sandbox API (\`sqlite_group_action\` \u2192 \`sqlite.group.action()\`).
|
|
358
344
|
|
|
359
|
-
**
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
[
|
|
364
|
-
"introspection",
|
|
365
|
-
`# db-mcp Help \u2014 Schema Introspection (10 tools)
|
|
345
|
+
- **Flexible Parameters**: Parameters are forgiven for casing discrepancies (e.g. \`conflict_columns\` vs \`conflictColumns\`).
|
|
346
|
+
- **Flexible Methods**: Methods mapped into the Code Mode proxy forgive casing discrepancies (e.g. \`sqlite.core.read_query\` executes \`sqlite.core.readQuery()\`).
|
|
347
|
+
- Methods support both an options object and **positional arguments** (e.g. \`sqlite.core.readQuery("SELECT...")\`, \`sqlite.json.insert("docs", "data", {...})\`).
|
|
348
|
+
- **Discovery**: Call \`sqlite.help()\` or \`sqlite.core.help()\` to see available methods directly within the sandbox.`],
|
|
349
|
+
["introspection", `# db-mcp Help \u2014 Schema Introspection (10 tools)
|
|
366
350
|
|
|
367
351
|
All introspection tools are **read-only** \u2014 they query PRAGMAs and sqlite_master, never modify data.
|
|
368
352
|
|
|
@@ -450,11 +434,9 @@ sqlite_query_plan({ sql: "SELECT * FROM orders WHERE status = 'active'" });
|
|
|
450
434
|
## \u26A0\uFE0F Gotchas
|
|
451
435
|
|
|
452
436
|
- \`excludeSystemTables\` defaults to \`true\` \u2014 SpatiaLite system tables are hidden for cleaner output. Pass \`false\` to include them
|
|
453
|
-
- \`sqlite_migration_risks\` analyzes DDL text statically \u2014 it does NOT execute the statements
|
|
454
|
-
|
|
455
|
-
[
|
|
456
|
-
"json",
|
|
457
|
-
`# db-mcp Help \u2014 JSON Operations (25 tools)
|
|
437
|
+
- \`sqlite_migration_risks\` analyzes DDL text statically \u2014 it does NOT execute the statements
|
|
438
|
+
- \`sqlite_schema_diff\`: \`baseline\` and \`target\` accept either the string \`"current"\` (queries live DB) or an inline snapshot object from a prior \`sqlite_schema_snapshot\` call. At least one side must be \`"current"\` unless doing an offline comparison.`],
|
|
439
|
+
["json", `# db-mcp Help \u2014 JSON Operations (25 tools)
|
|
458
440
|
|
|
459
441
|
## Collection & CRUD
|
|
460
442
|
|
|
@@ -528,11 +510,8 @@ sqlite_json_group_object({
|
|
|
528
510
|
|
|
529
511
|
- \`sqlite_json_storage_info({ table, column })\` \u2014 check text vs JSONB format
|
|
530
512
|
- \`sqlite_jsonb_convert({ table, column })\` \u2014 convert to JSONB for faster queries
|
|
531
|
-
- \`sqlite_json_normalize_column({ table, column, outputFormat? })\` \u2014 normalize JSON (sort keys, compact). \u26A0\uFE0F Defaults to \`"preserve"\` (maintains original format); use \`outputFormat: "text"\` to force text output`
|
|
532
|
-
|
|
533
|
-
[
|
|
534
|
-
"migration",
|
|
535
|
-
`# db-mcp Help \u2014 Migration Tracking (6 tools)
|
|
513
|
+
- \`sqlite_json_normalize_column({ table, column, outputFormat? })\` \u2014 normalize JSON (sort keys, compact). \u26A0\uFE0F Defaults to \`"preserve"\` (maintains original format); use \`outputFormat: "text"\` to force text output`],
|
|
514
|
+
["migration", `# db-mcp Help \u2014 Migration Tracking (6 tools)
|
|
536
515
|
|
|
537
516
|
\u26A0\uFE0F Must call \`sqlite_migration_init()\` before using any other migration tool \u2014 it creates the tracking table.
|
|
538
517
|
|
|
@@ -574,11 +553,8 @@ sqlite_migration_status();
|
|
|
574
553
|
## \u26A0\uFE0F Gotchas
|
|
575
554
|
|
|
576
555
|
- Rollback requires \`rollbackSql\` to have been provided when the migration was recorded/applied
|
|
577
|
-
- Migration group is **opt-in** \u2014 not included by default. Enable via \`--tool-filter\` flag: use \`dev-schema\` (core+introspection+migration+codemode) or \`full\` (all groups)`
|
|
578
|
-
]
|
|
579
|
-
[
|
|
580
|
-
"stats",
|
|
581
|
-
`# db-mcp Help \u2014 Statistical Analysis (23N/17W: 17 core + 6 window [NATIVE ONLY])
|
|
556
|
+
- Migration group is **opt-in** \u2014 not included by default. Enable via \`--tool-filter\` flag: use \`dev-schema\` (core+introspection+migration+codemode) or \`full\` (all groups)`],
|
|
557
|
+
["stats", `# db-mcp Help \u2014 Statistical Analysis (23N/17W: 17 core + 6 window [NATIVE ONLY])
|
|
582
558
|
|
|
583
559
|
## Core Statistics (always available)
|
|
584
560
|
|
|
@@ -697,11 +673,8 @@ sqlite_window_moving_avg({
|
|
|
697
673
|
windowSize: 7,
|
|
698
674
|
selectColumns: ["date"],
|
|
699
675
|
});
|
|
700
|
-
\`\`\``
|
|
701
|
-
]
|
|
702
|
-
[
|
|
703
|
-
"text",
|
|
704
|
-
`# db-mcp Help \u2014 Text Processing & FTS5 (20N/15W: 15 text + 5 FTS5 [NATIVE ONLY])
|
|
676
|
+
\`\`\``],
|
|
677
|
+
["text", `# db-mcp Help \u2014 Text Processing & FTS5 (20N/15W: 15 text + 5 FTS5 [NATIVE ONLY])
|
|
705
678
|
|
|
706
679
|
## Full-Text Search / FTS5 (5 tools, Native only)
|
|
707
680
|
|
|
@@ -840,6 +813,7 @@ sqlite_hybrid_search({
|
|
|
840
813
|
metric: "cosine",
|
|
841
814
|
limit: 10,
|
|
842
815
|
});
|
|
816
|
+
// \u26A0\uFE0F Requires an FTS5 table (Native only) and a vector column in the source table
|
|
843
817
|
|
|
844
818
|
// Sentiment analysis \u2014 text analysis (can analyze raw text or database columns)
|
|
845
819
|
sqlite_text_sentiment({ text: "This product is amazing and wonderful!" });
|
|
@@ -852,11 +826,8 @@ sqlite_text_sentiment({
|
|
|
852
826
|
returnWords: true,
|
|
853
827
|
});
|
|
854
828
|
// \u2192 { sentiment: "neutral", score: 0, matchedPositive: ["great"], matchedNegative: ["slow"] }
|
|
855
|
-
\`\`\``
|
|
856
|
-
|
|
857
|
-
[
|
|
858
|
-
"transactions",
|
|
859
|
-
`# db-mcp Help \u2014 Transactions (8 tools, Native only)
|
|
829
|
+
\`\`\``],
|
|
830
|
+
["transactions", `# db-mcp Help \u2014 Transactions (8 tools, Native only)
|
|
860
831
|
|
|
861
832
|
## Atomic Execution (preferred for simple cases)
|
|
862
833
|
|
|
@@ -887,11 +858,8 @@ sqlite_transaction_status(); // \u2192 { status: "active" | "none", active: true
|
|
|
887
858
|
|
|
888
859
|
- Transaction tools are **Native only** \u2014 WASM adapter does not support transactions
|
|
889
860
|
- Use \`sqlite_transaction_execute\` for simple multi-statement operations; manual \`begin\`/\`commit\` for complex flows with savepoints
|
|
890
|
-
- \`sqlite_transaction_status\` is read-only and requires only \`read\` scope; all other transaction tools require \`write\` scope`
|
|
891
|
-
|
|
892
|
-
[
|
|
893
|
-
"vector",
|
|
894
|
-
`# db-mcp Help \u2014 Vector/Semantic Search (11 tools)
|
|
861
|
+
- \`sqlite_transaction_status\` is read-only and requires only \`read\` scope; all other transaction tools require \`write\` scope`],
|
|
862
|
+
["vector", `# db-mcp Help \u2014 Vector/Semantic Search (11 tools)
|
|
895
863
|
|
|
896
864
|
\`\`\`javascript
|
|
897
865
|
// Create vector table with metadata columns
|
|
@@ -917,8 +885,11 @@ sqlite_vector_stats({ table: "docs", vectorColumn: "emb" }); // returns sampleSi
|
|
|
917
885
|
// Utility tools for preprocessing
|
|
918
886
|
sqlite_vector_normalize({ vector: [3, 4, 0, 0] }); // returns unit vector [0.6, 0.8, 0, 0]
|
|
919
887
|
sqlite_vector_distance({ vector1: [...], vector2: [...], metric: "cosine" }); // returns { value: <number> }
|
|
920
|
-
|
|
921
|
-
|
|
888
|
+
\`\`\`
|
|
889
|
+
|
|
890
|
+
## \u26A0\uFE0F Vector Gotchas
|
|
891
|
+
|
|
892
|
+
- **Vector tool schemas**: Vector tools use distinct schemas for specific operations. E.g., \`sqlite.vector.dimensions\` requires \`vectorColumn\`. Additionally, \`sqlite_vector_get\` wraps metadata inside a \`metadata\` object (e.g., \`metadata.content\`), and \`sqlite_vector_stats\` returns \`sampleSize\` and \`magnitudeStats\` (not \`count\` and \`stats\`).`]
|
|
922
893
|
]);
|
|
923
894
|
|
|
924
895
|
// src/audit/logger.ts
|
|
@@ -948,9 +919,9 @@ var AuditLogger = class {
|
|
|
948
919
|
/**
|
|
949
920
|
* Initialization is handled externally by SystemDb.
|
|
950
921
|
*/
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
922
|
+
init() {
|
|
923
|
+
if (this.stderrMode || !this.config.enabled) return Promise.resolve();
|
|
924
|
+
return Promise.resolve();
|
|
954
925
|
}
|
|
955
926
|
/**
|
|
956
927
|
* Append an audit entry to the buffer.
|
|
@@ -1219,7 +1190,7 @@ var SystemDb = class {
|
|
|
1219
1190
|
}
|
|
1220
1191
|
getDb() {
|
|
1221
1192
|
if (!this.db) {
|
|
1222
|
-
throw new
|
|
1193
|
+
throw new InternalError("SystemDb not initialized");
|
|
1223
1194
|
}
|
|
1224
1195
|
return this.db;
|
|
1225
1196
|
}
|
|
@@ -1757,202 +1728,7 @@ function createAuditInterceptor(auditLogger, backupManager) {
|
|
|
1757
1728
|
};
|
|
1758
1729
|
}
|
|
1759
1730
|
|
|
1760
|
-
// src/server/registration/
|
|
1761
|
-
function registerBuiltInTools(server, adaptersMap, config, toolFilter) {
|
|
1762
|
-
const serverInfoOpts = {
|
|
1763
|
-
title: "Server Info",
|
|
1764
|
-
description: "Get information about the db-mcp server and registered adapters",
|
|
1765
|
-
icons: SERVER_ICONS,
|
|
1766
|
-
annotations: READ_ONLY
|
|
1767
|
-
};
|
|
1768
|
-
server.registerTool("server_info", serverInfoOpts, () => {
|
|
1769
|
-
const authCtx = getAuthContext();
|
|
1770
|
-
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "server_info")) {
|
|
1771
|
-
throw new InsufficientScopeError(["read"], authCtx.scopes);
|
|
1772
|
-
}
|
|
1773
|
-
const adapterInfo = [];
|
|
1774
|
-
for (const [id, adapter] of adaptersMap) {
|
|
1775
|
-
adapterInfo.push({
|
|
1776
|
-
id,
|
|
1777
|
-
...adapter.getInfo()
|
|
1778
|
-
});
|
|
1779
|
-
}
|
|
1780
|
-
return {
|
|
1781
|
-
content: [
|
|
1782
|
-
{
|
|
1783
|
-
type: "text",
|
|
1784
|
-
text: JSON.stringify(
|
|
1785
|
-
{
|
|
1786
|
-
name: config.name,
|
|
1787
|
-
version: config.version,
|
|
1788
|
-
transport: config.transport,
|
|
1789
|
-
adapters: adapterInfo,
|
|
1790
|
-
toolFilter: {
|
|
1791
|
-
raw: toolFilter.raw,
|
|
1792
|
-
enabledGroups: [...toolFilter.enabledGroups]
|
|
1793
|
-
}
|
|
1794
|
-
},
|
|
1795
|
-
null,
|
|
1796
|
-
2
|
|
1797
|
-
)
|
|
1798
|
-
}
|
|
1799
|
-
]
|
|
1800
|
-
};
|
|
1801
|
-
});
|
|
1802
|
-
const healthOpts = {
|
|
1803
|
-
title: "Server Health",
|
|
1804
|
-
description: "Check health status of all database connections",
|
|
1805
|
-
icons: SERVER_ICONS,
|
|
1806
|
-
annotations: READ_ONLY
|
|
1807
|
-
};
|
|
1808
|
-
server.registerTool("server_health", healthOpts, async () => {
|
|
1809
|
-
const authCtx = getAuthContext();
|
|
1810
|
-
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "server_health")) {
|
|
1811
|
-
throw new InsufficientScopeError(["read"], authCtx.scopes);
|
|
1812
|
-
}
|
|
1813
|
-
const health = {
|
|
1814
|
-
server: "healthy",
|
|
1815
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1816
|
-
adapters: {}
|
|
1817
|
-
};
|
|
1818
|
-
for (const [id, adapter] of adaptersMap) {
|
|
1819
|
-
try {
|
|
1820
|
-
const adapterHealth = await adapter.getHealth();
|
|
1821
|
-
health["adapters"][id] = adapterHealth;
|
|
1822
|
-
} catch (error) {
|
|
1823
|
-
let errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1824
|
-
errorMsg = errorMsg.replace(/(:[^:@/]+@)/g, ":***@");
|
|
1825
|
-
health["adapters"][id] = {
|
|
1826
|
-
connected: false,
|
|
1827
|
-
error: errorMsg
|
|
1828
|
-
};
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
return {
|
|
1832
|
-
content: [
|
|
1833
|
-
{
|
|
1834
|
-
type: "text",
|
|
1835
|
-
text: JSON.stringify(health, null, 2)
|
|
1836
|
-
}
|
|
1837
|
-
]
|
|
1838
|
-
};
|
|
1839
|
-
});
|
|
1840
|
-
const listAdaptersOpts = {
|
|
1841
|
-
title: "List Adapters",
|
|
1842
|
-
description: "List all registered database adapters",
|
|
1843
|
-
icons: SERVER_ICONS,
|
|
1844
|
-
annotations: READ_ONLY
|
|
1845
|
-
};
|
|
1846
|
-
server.registerTool("list_adapters", listAdaptersOpts, () => {
|
|
1847
|
-
const authCtx = getAuthContext();
|
|
1848
|
-
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "list_adapters")) {
|
|
1849
|
-
throw new InsufficientScopeError(["read"], authCtx.scopes);
|
|
1850
|
-
}
|
|
1851
|
-
const adapters = [];
|
|
1852
|
-
for (const [, adapter] of adaptersMap) {
|
|
1853
|
-
adapters.push({
|
|
1854
|
-
id: `${adapter.type}:default`,
|
|
1855
|
-
type: adapter.type,
|
|
1856
|
-
name: adapter.name,
|
|
1857
|
-
version: adapter.version,
|
|
1858
|
-
connected: adapter.isConnected()
|
|
1859
|
-
});
|
|
1860
|
-
}
|
|
1861
|
-
return {
|
|
1862
|
-
content: [
|
|
1863
|
-
{
|
|
1864
|
-
type: "text",
|
|
1865
|
-
text: JSON.stringify(adapters, null, 2)
|
|
1866
|
-
}
|
|
1867
|
-
]
|
|
1868
|
-
};
|
|
1869
|
-
});
|
|
1870
|
-
registerToolScopes(
|
|
1871
|
-
/* @__PURE__ */ new Map([
|
|
1872
|
-
["server_info", ["read"]],
|
|
1873
|
-
["server_health", ["read"]],
|
|
1874
|
-
["list_adapters", ["read"]]
|
|
1875
|
-
])
|
|
1876
|
-
);
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
// src/server/registration/help-resources.ts
|
|
1880
|
-
function registerHelpResources(server, toolFilter) {
|
|
1881
|
-
const gotchasContent = HELP_CONTENT.get("gotchas");
|
|
1882
|
-
if (gotchasContent) {
|
|
1883
|
-
server.registerResource(
|
|
1884
|
-
"sqlite_help",
|
|
1885
|
-
"sqlite://help",
|
|
1886
|
-
{
|
|
1887
|
-
description: "Critical gotchas, WASM vs Native comparison, and Code Mode API reference",
|
|
1888
|
-
mimeType: "text/markdown",
|
|
1889
|
-
annotations: ASSISTANT_FOCUSED
|
|
1890
|
-
},
|
|
1891
|
-
() => {
|
|
1892
|
-
metrics.recordResourceRead("sqlite://help");
|
|
1893
|
-
return {
|
|
1894
|
-
contents: [
|
|
1895
|
-
{
|
|
1896
|
-
uri: "sqlite://help",
|
|
1897
|
-
mimeType: "text/markdown",
|
|
1898
|
-
text: gotchasContent
|
|
1899
|
-
}
|
|
1900
|
-
]
|
|
1901
|
-
};
|
|
1902
|
-
}
|
|
1903
|
-
);
|
|
1904
|
-
}
|
|
1905
|
-
const groupHelpKeys = [
|
|
1906
|
-
{ group: "core", key: "core" },
|
|
1907
|
-
{ group: "json", key: "json" },
|
|
1908
|
-
{ group: "text", key: "text" },
|
|
1909
|
-
{ group: "stats", key: "stats" },
|
|
1910
|
-
{ group: "vector", key: "vector" },
|
|
1911
|
-
{ group: "geo", key: "geo" },
|
|
1912
|
-
{ group: "admin", key: "admin" },
|
|
1913
|
-
{ group: "transactions", key: "transactions" },
|
|
1914
|
-
{ group: "introspection", key: "introspection" },
|
|
1915
|
-
{ group: "migration", key: "migration" }
|
|
1916
|
-
];
|
|
1917
|
-
for (const { group, key } of groupHelpKeys) {
|
|
1918
|
-
const isCodemodeOnly = toolFilter.enabledGroups.size === 1 && toolFilter.enabledGroups.has("codemode");
|
|
1919
|
-
if (!toolFilter.enabledGroups.has(group) && !isCodemodeOnly) {
|
|
1920
|
-
continue;
|
|
1921
|
-
}
|
|
1922
|
-
const content = HELP_CONTENT.get(key);
|
|
1923
|
-
if (!content) continue;
|
|
1924
|
-
server.registerResource(
|
|
1925
|
-
`sqlite_help_${key}`,
|
|
1926
|
-
`sqlite://help/${key}`,
|
|
1927
|
-
{
|
|
1928
|
-
description: `Tool reference for the ${group} tool group`,
|
|
1929
|
-
mimeType: "text/markdown",
|
|
1930
|
-
annotations: ASSISTANT_FOCUSED
|
|
1931
|
-
},
|
|
1932
|
-
() => {
|
|
1933
|
-
metrics.recordResourceRead(`sqlite://help/${key}`);
|
|
1934
|
-
return {
|
|
1935
|
-
contents: [
|
|
1936
|
-
{
|
|
1937
|
-
uri: `sqlite://help/${key}`,
|
|
1938
|
-
mimeType: "text/markdown",
|
|
1939
|
-
text: content
|
|
1940
|
-
}
|
|
1941
|
-
]
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
|
-
);
|
|
1945
|
-
}
|
|
1946
|
-
const registeredHelp = ["sqlite://help"];
|
|
1947
|
-
for (const { group, key } of groupHelpKeys) {
|
|
1948
|
-
if (toolFilter.enabledGroups.has(group)) {
|
|
1949
|
-
registeredHelp.push(`sqlite://help/${key}`);
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
logger.info(`Help resources: ${registeredHelp.join(", ")}`, {
|
|
1953
|
-
module: "SERVER"
|
|
1954
|
-
});
|
|
1955
|
-
}
|
|
1731
|
+
// src/server/registration/audit-tools/helpers.ts
|
|
1956
1732
|
function redactSqlLiterals(text) {
|
|
1957
1733
|
return text.replace(/'(?:''|[^'])*'/g, "'***'");
|
|
1958
1734
|
}
|
|
@@ -1960,16 +1736,18 @@ function validateDdl(sql) {
|
|
|
1960
1736
|
const cleanSql = sql.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "");
|
|
1961
1737
|
const upperSql = cleanSql.toUpperCase();
|
|
1962
1738
|
if (upperSql.includes("ATTACH ") || upperSql.includes("DETACH ") || upperSql.includes("PRAGMA ") || upperSql.includes("LOAD_EXTENSION(")) {
|
|
1963
|
-
throw new
|
|
1739
|
+
throw new ValidationError(
|
|
1964
1740
|
`DDL validation failed: unauthorized command or function call`
|
|
1965
1741
|
);
|
|
1966
1742
|
}
|
|
1967
1743
|
if (upperSql.includes(" ON MAIN.") || upperSql.includes(" ON TEMP.")) {
|
|
1968
|
-
throw new
|
|
1744
|
+
throw new ValidationError(
|
|
1969
1745
|
`DDL validation failed: trigger attempts to target a specific database`
|
|
1970
1746
|
);
|
|
1971
1747
|
}
|
|
1972
1748
|
}
|
|
1749
|
+
|
|
1750
|
+
// src/server/registration/audit-tools/resource.ts
|
|
1973
1751
|
function registerAuditResource(server, auditLogger, backupManager) {
|
|
1974
1752
|
if (!auditLogger) return;
|
|
1975
1753
|
server.registerResource(
|
|
@@ -2012,12 +1790,13 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2012
1790
|
description: "List pre-mutation DDL snapshots captured before destructive operations. Returns metadata for each snapshot including timestamp, tool, target, and size.",
|
|
2013
1791
|
inputSchema: AuditListBackupsSchema,
|
|
2014
1792
|
outputSchema: AuditListBackupsOutputSchema,
|
|
2015
|
-
annotations: {
|
|
1793
|
+
annotations: toSDK({
|
|
2016
1794
|
readOnlyHint: true,
|
|
2017
1795
|
destructiveHint: false,
|
|
2018
1796
|
idempotentHint: true,
|
|
2019
|
-
openWorldHint: false
|
|
2020
|
-
|
|
1797
|
+
openWorldHint: false,
|
|
1798
|
+
sensitiveHint: true
|
|
1799
|
+
})
|
|
2021
1800
|
},
|
|
2022
1801
|
async (args) => {
|
|
2023
1802
|
const authCtx = getAuthContext();
|
|
@@ -2079,12 +1858,13 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2079
1858
|
filename: z.string().default("").describe("Snapshot filename from sqlite_audit_list_backups results")
|
|
2080
1859
|
}),
|
|
2081
1860
|
outputSchema: AuditGetBackupOutputSchema,
|
|
2082
|
-
annotations: {
|
|
1861
|
+
annotations: toSDK({
|
|
2083
1862
|
readOnlyHint: true,
|
|
2084
1863
|
destructiveHint: false,
|
|
2085
1864
|
idempotentHint: true,
|
|
2086
|
-
openWorldHint: false
|
|
2087
|
-
|
|
1865
|
+
openWorldHint: false,
|
|
1866
|
+
sensitiveHint: true
|
|
1867
|
+
})
|
|
2088
1868
|
},
|
|
2089
1869
|
async (args) => {
|
|
2090
1870
|
const authCtx = getAuthContext();
|
|
@@ -2112,7 +1892,9 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2112
1892
|
if (!snapshot) {
|
|
2113
1893
|
const errRes = {
|
|
2114
1894
|
success: false,
|
|
2115
|
-
error: `Snapshot not found: ${filename}
|
|
1895
|
+
error: `Snapshot not found: ${filename}`,
|
|
1896
|
+
code: "NOT_FOUND",
|
|
1897
|
+
category: "not_found"
|
|
2116
1898
|
};
|
|
2117
1899
|
return {
|
|
2118
1900
|
content: [
|
|
@@ -2144,12 +1926,13 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2144
1926
|
title: "Cleanup Audit Backups",
|
|
2145
1927
|
description: "Apply retention policy to audit backup snapshots. Deletes snapshots exceeding age or count limits.",
|
|
2146
1928
|
outputSchema: AuditCleanupOutputSchema,
|
|
2147
|
-
annotations: {
|
|
1929
|
+
annotations: toSDK({
|
|
2148
1930
|
readOnlyHint: false,
|
|
2149
1931
|
destructiveHint: true,
|
|
2150
1932
|
idempotentHint: true,
|
|
2151
|
-
openWorldHint: false
|
|
2152
|
-
|
|
1933
|
+
openWorldHint: false,
|
|
1934
|
+
sensitiveHint: true
|
|
1935
|
+
})
|
|
2153
1936
|
},
|
|
2154
1937
|
async () => {
|
|
2155
1938
|
const authCtx = getAuthContext();
|
|
@@ -2184,12 +1967,13 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2184
1967
|
)
|
|
2185
1968
|
}),
|
|
2186
1969
|
outputSchema: AuditDiffBackupOutputSchema,
|
|
2187
|
-
annotations: {
|
|
1970
|
+
annotations: toSDK({
|
|
2188
1971
|
readOnlyHint: true,
|
|
2189
1972
|
destructiveHint: false,
|
|
2190
1973
|
idempotentHint: true,
|
|
2191
|
-
openWorldHint: false
|
|
2192
|
-
|
|
1974
|
+
openWorldHint: false,
|
|
1975
|
+
sensitiveHint: true
|
|
1976
|
+
})
|
|
2193
1977
|
},
|
|
2194
1978
|
async (args) => {
|
|
2195
1979
|
const authCtx = getAuthContext();
|
|
@@ -2217,7 +2001,9 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2217
2001
|
if (!snapshot) {
|
|
2218
2002
|
const errRes = {
|
|
2219
2003
|
success: false,
|
|
2220
|
-
error: `Snapshot not found: ${filename}
|
|
2004
|
+
error: `Snapshot not found: ${filename}`,
|
|
2005
|
+
code: "NOT_FOUND",
|
|
2006
|
+
category: "not_found"
|
|
2221
2007
|
};
|
|
2222
2008
|
return {
|
|
2223
2009
|
content: [
|
|
@@ -2234,7 +2020,9 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2234
2020
|
if (!adapter) {
|
|
2235
2021
|
const errRes = {
|
|
2236
2022
|
success: false,
|
|
2237
|
-
error: "No connected adapter available"
|
|
2023
|
+
error: "No connected adapter available",
|
|
2024
|
+
code: "INTERNAL_ERROR",
|
|
2025
|
+
category: "internal"
|
|
2238
2026
|
};
|
|
2239
2027
|
return {
|
|
2240
2028
|
content: [
|
|
@@ -2323,19 +2111,16 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2323
2111
|
structuredContent: result
|
|
2324
2112
|
};
|
|
2325
2113
|
} catch (error) {
|
|
2326
|
-
const
|
|
2327
|
-
success: false,
|
|
2328
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2329
|
-
};
|
|
2114
|
+
const structured = formatHandlerError(error);
|
|
2330
2115
|
return {
|
|
2331
2116
|
content: [
|
|
2332
2117
|
{
|
|
2333
2118
|
type: "text",
|
|
2334
|
-
text: JSON.stringify(
|
|
2119
|
+
text: JSON.stringify(structured, null, 2)
|
|
2335
2120
|
}
|
|
2336
2121
|
],
|
|
2337
2122
|
isError: true,
|
|
2338
|
-
structuredContent:
|
|
2123
|
+
structuredContent: structured
|
|
2339
2124
|
};
|
|
2340
2125
|
}
|
|
2341
2126
|
}
|
|
@@ -2350,12 +2135,13 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2350
2135
|
dryRun: z.unknown().optional().describe("If true, returns the DDL without executing it (boolean)")
|
|
2351
2136
|
}),
|
|
2352
2137
|
outputSchema: AuditRestoreBackupOutputSchema,
|
|
2353
|
-
annotations: {
|
|
2138
|
+
annotations: toSDK({
|
|
2354
2139
|
readOnlyHint: false,
|
|
2355
2140
|
destructiveHint: true,
|
|
2356
2141
|
idempotentHint: false,
|
|
2357
|
-
openWorldHint: false
|
|
2358
|
-
|
|
2142
|
+
openWorldHint: false,
|
|
2143
|
+
sensitiveHint: true
|
|
2144
|
+
})
|
|
2359
2145
|
},
|
|
2360
2146
|
async (args) => {
|
|
2361
2147
|
const authCtx = getAuthContext();
|
|
@@ -2388,7 +2174,9 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2388
2174
|
if (!snapshot) {
|
|
2389
2175
|
const errRes = {
|
|
2390
2176
|
success: false,
|
|
2391
|
-
error: `Snapshot not found: ${filename}
|
|
2177
|
+
error: `Snapshot not found: ${filename}`,
|
|
2178
|
+
code: "NOT_FOUND",
|
|
2179
|
+
category: "not_found"
|
|
2392
2180
|
};
|
|
2393
2181
|
return {
|
|
2394
2182
|
content: [
|
|
@@ -2405,7 +2193,9 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2405
2193
|
if (!ddl.trim()) {
|
|
2406
2194
|
const errRes = {
|
|
2407
2195
|
success: false,
|
|
2408
|
-
error: "Snapshot contains no DDL statements"
|
|
2196
|
+
error: "Snapshot contains no DDL statements",
|
|
2197
|
+
code: "VALIDATION_ERROR",
|
|
2198
|
+
category: "validation"
|
|
2409
2199
|
};
|
|
2410
2200
|
return {
|
|
2411
2201
|
content: [
|
|
@@ -2422,7 +2212,9 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2422
2212
|
if (!adapter) {
|
|
2423
2213
|
const errRes = {
|
|
2424
2214
|
success: false,
|
|
2425
|
-
error: "No connected adapter available"
|
|
2215
|
+
error: "No connected adapter available",
|
|
2216
|
+
code: "INTERNAL_ERROR",
|
|
2217
|
+
category: "internal"
|
|
2426
2218
|
};
|
|
2427
2219
|
return {
|
|
2428
2220
|
content: [
|
|
@@ -2499,19 +2291,16 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2499
2291
|
structuredContent: result
|
|
2500
2292
|
};
|
|
2501
2293
|
} catch (error) {
|
|
2502
|
-
const
|
|
2503
|
-
success: false,
|
|
2504
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2505
|
-
};
|
|
2294
|
+
const structured = formatHandlerError(error);
|
|
2506
2295
|
return {
|
|
2507
2296
|
content: [
|
|
2508
2297
|
{
|
|
2509
2298
|
type: "text",
|
|
2510
|
-
text: JSON.stringify(
|
|
2299
|
+
text: JSON.stringify(structured, null, 2)
|
|
2511
2300
|
}
|
|
2512
2301
|
],
|
|
2513
2302
|
isError: true,
|
|
2514
|
-
structuredContent:
|
|
2303
|
+
structuredContent: structured
|
|
2515
2304
|
};
|
|
2516
2305
|
}
|
|
2517
2306
|
}
|
|
@@ -2530,6 +2319,8 @@ function registerAuditBackupTools(server, backupManager, adaptersMap) {
|
|
|
2530
2319
|
{ module: "AUDIT" }
|
|
2531
2320
|
);
|
|
2532
2321
|
}
|
|
2322
|
+
|
|
2323
|
+
// src/server/registration/audit-tools/search.ts
|
|
2533
2324
|
function registerAuditSearchTool(server, auditLogger) {
|
|
2534
2325
|
if (!auditLogger) return;
|
|
2535
2326
|
server.registerTool(
|
|
@@ -2539,21 +2330,44 @@ function registerAuditSearchTool(server, auditLogger) {
|
|
|
2539
2330
|
description: "Search and filter structured audit logs from the System Database. Returns recent tool invocations, outcomes, token estimates, and parameters.",
|
|
2540
2331
|
inputSchema: AuditSearchSchema,
|
|
2541
2332
|
outputSchema: AuditSearchOutputSchema,
|
|
2542
|
-
annotations: {
|
|
2333
|
+
annotations: toSDK({
|
|
2543
2334
|
readOnlyHint: true,
|
|
2544
2335
|
destructiveHint: false,
|
|
2545
2336
|
idempotentHint: true,
|
|
2546
|
-
openWorldHint: false
|
|
2547
|
-
|
|
2337
|
+
openWorldHint: false,
|
|
2338
|
+
sensitiveHint: true
|
|
2339
|
+
})
|
|
2548
2340
|
},
|
|
2549
2341
|
async (args) => {
|
|
2550
|
-
const authCtx = getAuthContext();
|
|
2551
|
-
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_search")) {
|
|
2552
|
-
throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
|
|
2553
|
-
}
|
|
2554
|
-
let parsed;
|
|
2555
2342
|
try {
|
|
2556
|
-
|
|
2343
|
+
const authCtx = getAuthContext();
|
|
2344
|
+
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "sqlite_audit_search")) {
|
|
2345
|
+
throw new InsufficientScopeError(["admin", "full"], authCtx.scopes);
|
|
2346
|
+
}
|
|
2347
|
+
const parsed = AuditSearchSchema.parse(args ?? {});
|
|
2348
|
+
const { entries, totalCount } = await auditLogger.search(parsed);
|
|
2349
|
+
const result = {
|
|
2350
|
+
success: true,
|
|
2351
|
+
entries,
|
|
2352
|
+
count: entries.length,
|
|
2353
|
+
totalCount
|
|
2354
|
+
};
|
|
2355
|
+
const tokenEstimate = Math.ceil(
|
|
2356
|
+
Buffer.byteLength(JSON.stringify(result), "utf8") / 4
|
|
2357
|
+
);
|
|
2358
|
+
return {
|
|
2359
|
+
content: [
|
|
2360
|
+
{
|
|
2361
|
+
type: "text",
|
|
2362
|
+
text: JSON.stringify(
|
|
2363
|
+
{ ...result, _meta: { tokenEstimate } },
|
|
2364
|
+
null,
|
|
2365
|
+
2
|
|
2366
|
+
)
|
|
2367
|
+
}
|
|
2368
|
+
],
|
|
2369
|
+
structuredContent: result
|
|
2370
|
+
};
|
|
2557
2371
|
} catch (error) {
|
|
2558
2372
|
const structured = formatHandlerError(error);
|
|
2559
2373
|
return {
|
|
@@ -2567,29 +2381,6 @@ function registerAuditSearchTool(server, auditLogger) {
|
|
|
2567
2381
|
structuredContent: structured
|
|
2568
2382
|
};
|
|
2569
2383
|
}
|
|
2570
|
-
const { entries, totalCount } = await auditLogger.search(parsed);
|
|
2571
|
-
const result = {
|
|
2572
|
-
success: true,
|
|
2573
|
-
entries,
|
|
2574
|
-
count: entries.length,
|
|
2575
|
-
totalCount
|
|
2576
|
-
};
|
|
2577
|
-
const tokenEstimate = Math.ceil(
|
|
2578
|
-
Buffer.byteLength(JSON.stringify(result), "utf8") / 4
|
|
2579
|
-
);
|
|
2580
|
-
return {
|
|
2581
|
-
content: [
|
|
2582
|
-
{
|
|
2583
|
-
type: "text",
|
|
2584
|
-
text: JSON.stringify(
|
|
2585
|
-
{ ...result, _meta: { tokenEstimate } },
|
|
2586
|
-
null,
|
|
2587
|
-
2
|
|
2588
|
-
)
|
|
2589
|
-
}
|
|
2590
|
-
],
|
|
2591
|
-
structuredContent: result
|
|
2592
|
-
};
|
|
2593
2384
|
}
|
|
2594
2385
|
);
|
|
2595
2386
|
registerToolScopes(/* @__PURE__ */ new Map([["sqlite_audit_search", ["admin", "full"]]]));
|
|
@@ -2598,6 +2389,203 @@ function registerAuditSearchTool(server, auditLogger) {
|
|
|
2598
2389
|
});
|
|
2599
2390
|
}
|
|
2600
2391
|
|
|
2392
|
+
// src/server/registration/built-in-tools.ts
|
|
2393
|
+
function registerBuiltInTools(server, adaptersMap, config, toolFilter) {
|
|
2394
|
+
const serverInfoOpts = {
|
|
2395
|
+
title: "Server Info",
|
|
2396
|
+
description: "Get information about the db-mcp server and registered adapters",
|
|
2397
|
+
icons: SERVER_ICONS,
|
|
2398
|
+
annotations: READ_ONLY
|
|
2399
|
+
};
|
|
2400
|
+
server.registerTool("server_info", serverInfoOpts, () => {
|
|
2401
|
+
const authCtx = getAuthContext();
|
|
2402
|
+
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "server_info")) {
|
|
2403
|
+
throw new InsufficientScopeError(["read"], authCtx.scopes);
|
|
2404
|
+
}
|
|
2405
|
+
const adapterInfo = [];
|
|
2406
|
+
for (const [id, adapter] of adaptersMap) {
|
|
2407
|
+
adapterInfo.push({
|
|
2408
|
+
id,
|
|
2409
|
+
...adapter.getInfo()
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
2412
|
+
return {
|
|
2413
|
+
content: [
|
|
2414
|
+
{
|
|
2415
|
+
type: "text",
|
|
2416
|
+
text: JSON.stringify(
|
|
2417
|
+
{
|
|
2418
|
+
name: config.name,
|
|
2419
|
+
version: config.version,
|
|
2420
|
+
transport: config.transport,
|
|
2421
|
+
adapters: adapterInfo,
|
|
2422
|
+
toolFilter: {
|
|
2423
|
+
raw: toolFilter.raw,
|
|
2424
|
+
enabledGroups: [...toolFilter.enabledGroups]
|
|
2425
|
+
}
|
|
2426
|
+
},
|
|
2427
|
+
null,
|
|
2428
|
+
2
|
|
2429
|
+
)
|
|
2430
|
+
}
|
|
2431
|
+
]
|
|
2432
|
+
};
|
|
2433
|
+
});
|
|
2434
|
+
const healthOpts = {
|
|
2435
|
+
title: "Server Health",
|
|
2436
|
+
description: "Check health status of all database connections",
|
|
2437
|
+
icons: SERVER_ICONS,
|
|
2438
|
+
annotations: READ_ONLY
|
|
2439
|
+
};
|
|
2440
|
+
server.registerTool("server_health", healthOpts, async () => {
|
|
2441
|
+
const authCtx = getAuthContext();
|
|
2442
|
+
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "server_health")) {
|
|
2443
|
+
throw new InsufficientScopeError(["read"], authCtx.scopes);
|
|
2444
|
+
}
|
|
2445
|
+
const health = {
|
|
2446
|
+
server: "healthy",
|
|
2447
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2448
|
+
adapters: {}
|
|
2449
|
+
};
|
|
2450
|
+
for (const [id, adapter] of adaptersMap) {
|
|
2451
|
+
try {
|
|
2452
|
+
const adapterHealth = await adapter.getHealth();
|
|
2453
|
+
health["adapters"][id] = adapterHealth;
|
|
2454
|
+
} catch (error) {
|
|
2455
|
+
let errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2456
|
+
errorMsg = errorMsg.replace(/(:[^:@/]+@)/g, ":***@");
|
|
2457
|
+
health["adapters"][id] = {
|
|
2458
|
+
connected: false,
|
|
2459
|
+
error: errorMsg
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
return {
|
|
2464
|
+
content: [
|
|
2465
|
+
{
|
|
2466
|
+
type: "text",
|
|
2467
|
+
text: JSON.stringify(health, null, 2)
|
|
2468
|
+
}
|
|
2469
|
+
]
|
|
2470
|
+
};
|
|
2471
|
+
});
|
|
2472
|
+
const listAdaptersOpts = {
|
|
2473
|
+
title: "List Adapters",
|
|
2474
|
+
description: "List all registered database adapters",
|
|
2475
|
+
icons: SERVER_ICONS,
|
|
2476
|
+
annotations: READ_ONLY
|
|
2477
|
+
};
|
|
2478
|
+
server.registerTool("list_adapters", listAdaptersOpts, () => {
|
|
2479
|
+
const authCtx = getAuthContext();
|
|
2480
|
+
if (authCtx && !scopesGrantToolAccess(authCtx.scopes, "list_adapters")) {
|
|
2481
|
+
throw new InsufficientScopeError(["read"], authCtx.scopes);
|
|
2482
|
+
}
|
|
2483
|
+
const adapters = [];
|
|
2484
|
+
for (const [, adapter] of adaptersMap) {
|
|
2485
|
+
adapters.push({
|
|
2486
|
+
id: `${adapter.type}:default`,
|
|
2487
|
+
type: adapter.type,
|
|
2488
|
+
name: adapter.name,
|
|
2489
|
+
version: adapter.version,
|
|
2490
|
+
connected: adapter.isConnected()
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
return {
|
|
2494
|
+
content: [
|
|
2495
|
+
{
|
|
2496
|
+
type: "text",
|
|
2497
|
+
text: JSON.stringify(adapters, null, 2)
|
|
2498
|
+
}
|
|
2499
|
+
]
|
|
2500
|
+
};
|
|
2501
|
+
});
|
|
2502
|
+
registerToolScopes(
|
|
2503
|
+
/* @__PURE__ */ new Map([
|
|
2504
|
+
["server_info", ["read"]],
|
|
2505
|
+
["server_health", ["read"]],
|
|
2506
|
+
["list_adapters", ["read"]]
|
|
2507
|
+
])
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
// src/server/registration/help-resources.ts
|
|
2512
|
+
function registerHelpResources(server, toolFilter) {
|
|
2513
|
+
const gotchasContent = HELP_CONTENT.get("gotchas");
|
|
2514
|
+
if (gotchasContent) {
|
|
2515
|
+
server.registerResource(
|
|
2516
|
+
"sqlite_help",
|
|
2517
|
+
"sqlite://help",
|
|
2518
|
+
{
|
|
2519
|
+
description: "Critical gotchas, WASM vs Native comparison, and Code Mode API reference",
|
|
2520
|
+
mimeType: "text/markdown",
|
|
2521
|
+
annotations: ASSISTANT_FOCUSED
|
|
2522
|
+
},
|
|
2523
|
+
() => {
|
|
2524
|
+
metrics.recordResourceRead("sqlite://help");
|
|
2525
|
+
return {
|
|
2526
|
+
contents: [
|
|
2527
|
+
{
|
|
2528
|
+
uri: "sqlite://help",
|
|
2529
|
+
mimeType: "text/markdown",
|
|
2530
|
+
text: gotchasContent
|
|
2531
|
+
}
|
|
2532
|
+
]
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
const groupHelpKeys = [
|
|
2538
|
+
{ group: "core", key: "core" },
|
|
2539
|
+
{ group: "json", key: "json" },
|
|
2540
|
+
{ group: "text", key: "text" },
|
|
2541
|
+
{ group: "stats", key: "stats" },
|
|
2542
|
+
{ group: "vector", key: "vector" },
|
|
2543
|
+
{ group: "geo", key: "geo" },
|
|
2544
|
+
{ group: "admin", key: "admin" },
|
|
2545
|
+
{ group: "transactions", key: "transactions" },
|
|
2546
|
+
{ group: "introspection", key: "introspection" },
|
|
2547
|
+
{ group: "migration", key: "migration" }
|
|
2548
|
+
];
|
|
2549
|
+
for (const { group, key } of groupHelpKeys) {
|
|
2550
|
+
const isCodemodeOnly = toolFilter.enabledGroups.size === 1 && toolFilter.enabledGroups.has("codemode");
|
|
2551
|
+
if (!toolFilter.enabledGroups.has(group) && !isCodemodeOnly) {
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
const content = HELP_CONTENT.get(key);
|
|
2555
|
+
if (!content) continue;
|
|
2556
|
+
server.registerResource(
|
|
2557
|
+
`sqlite_help_${key}`,
|
|
2558
|
+
`sqlite://help/${key}`,
|
|
2559
|
+
{
|
|
2560
|
+
description: `Tool reference for the ${group} tool group`,
|
|
2561
|
+
mimeType: "text/markdown",
|
|
2562
|
+
annotations: ASSISTANT_FOCUSED
|
|
2563
|
+
},
|
|
2564
|
+
() => {
|
|
2565
|
+
metrics.recordResourceRead(`sqlite://help/${key}`);
|
|
2566
|
+
return {
|
|
2567
|
+
contents: [
|
|
2568
|
+
{
|
|
2569
|
+
uri: `sqlite://help/${key}`,
|
|
2570
|
+
mimeType: "text/markdown",
|
|
2571
|
+
text: content
|
|
2572
|
+
}
|
|
2573
|
+
]
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
const registeredHelp = ["sqlite://help"];
|
|
2579
|
+
for (const { group, key } of groupHelpKeys) {
|
|
2580
|
+
if (toolFilter.enabledGroups.has(group)) {
|
|
2581
|
+
registeredHelp.push(`sqlite://help/${key}`);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
logger.info(`Help resources: ${registeredHelp.join(", ")}`, {
|
|
2585
|
+
module: "SERVER"
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2601
2589
|
// src/server/registration/observability-resources.ts
|
|
2602
2590
|
function registerObservabilityResources(server) {
|
|
2603
2591
|
server.registerResource(
|
|
@@ -2634,12 +2622,13 @@ function registerAdminTools(server) {
|
|
|
2634
2622
|
"The new value for the setting (e.g., 'debug', 'info', 'warning')"
|
|
2635
2623
|
)
|
|
2636
2624
|
}),
|
|
2637
|
-
annotations: {
|
|
2625
|
+
annotations: toSDK({
|
|
2638
2626
|
readOnlyHint: false,
|
|
2639
2627
|
destructiveHint: false,
|
|
2640
2628
|
idempotentHint: false,
|
|
2641
|
-
openWorldHint: false
|
|
2642
|
-
|
|
2629
|
+
openWorldHint: false,
|
|
2630
|
+
sensitiveHint: true
|
|
2631
|
+
})
|
|
2643
2632
|
},
|
|
2644
2633
|
(args) => {
|
|
2645
2634
|
const authCtx = getAuthContext();
|
|
@@ -2693,7 +2682,8 @@ function registerAdminTools(server) {
|
|
|
2693
2682
|
{
|
|
2694
2683
|
success: false,
|
|
2695
2684
|
error: `Invalid log level: ${value}. Must be one of: ${validLevels.join(", ")}`,
|
|
2696
|
-
code: "INVALID_CONFIG"
|
|
2685
|
+
code: "INVALID_CONFIG",
|
|
2686
|
+
category: "validation"
|
|
2697
2687
|
},
|
|
2698
2688
|
null,
|
|
2699
2689
|
2
|
|
@@ -2734,7 +2724,8 @@ function registerAdminTools(server) {
|
|
|
2734
2724
|
{
|
|
2735
2725
|
success: false,
|
|
2736
2726
|
error: "Missing setting or value for set action",
|
|
2737
|
-
code: "INVALID_CONFIG"
|
|
2727
|
+
code: "INVALID_CONFIG",
|
|
2728
|
+
category: "validation"
|
|
2738
2729
|
},
|
|
2739
2730
|
null,
|
|
2740
2731
|
2
|
|
@@ -2748,27 +2739,27 @@ function registerAdminTools(server) {
|
|
|
2748
2739
|
content: [
|
|
2749
2740
|
{
|
|
2750
2741
|
type: "text",
|
|
2751
|
-
text: JSON.stringify({
|
|
2742
|
+
text: JSON.stringify({
|
|
2743
|
+
success: false,
|
|
2744
|
+
error: "Invalid action",
|
|
2745
|
+
code: "INVALID_CONFIG",
|
|
2746
|
+
category: "validation"
|
|
2747
|
+
})
|
|
2752
2748
|
}
|
|
2753
2749
|
],
|
|
2754
2750
|
isError: true
|
|
2755
2751
|
};
|
|
2756
2752
|
} catch (error) {
|
|
2753
|
+
const structured = formatHandlerError(error);
|
|
2757
2754
|
return {
|
|
2758
2755
|
content: [
|
|
2759
2756
|
{
|
|
2760
2757
|
type: "text",
|
|
2761
|
-
text: JSON.stringify(
|
|
2762
|
-
{
|
|
2763
|
-
success: false,
|
|
2764
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2765
|
-
},
|
|
2766
|
-
null,
|
|
2767
|
-
2
|
|
2768
|
-
)
|
|
2758
|
+
text: JSON.stringify(structured, null, 2)
|
|
2769
2759
|
}
|
|
2770
2760
|
],
|
|
2771
|
-
isError: true
|
|
2761
|
+
isError: true,
|
|
2762
|
+
structuredContent: structured
|
|
2772
2763
|
};
|
|
2773
2764
|
}
|
|
2774
2765
|
}
|
|
@@ -2908,7 +2899,7 @@ if (typeof proto["createToolError"] === "function") {
|
|
|
2908
2899
|
success: false,
|
|
2909
2900
|
error: cleanError,
|
|
2910
2901
|
code: "VALIDATION_ERROR",
|
|
2911
|
-
category:
|
|
2902
|
+
category: ErrorCategory.VALIDATION
|
|
2912
2903
|
};
|
|
2913
2904
|
result.content[0].text = JSON.stringify(structured, null, 2);
|
|
2914
2905
|
result.isError = true;
|
|
@@ -2930,6 +2921,25 @@ var DbMcpServer = class {
|
|
|
2930
2921
|
subscriptionManager;
|
|
2931
2922
|
constructor(config) {
|
|
2932
2923
|
this.config = config;
|
|
2924
|
+
let allowedIoRoots = config.allowedIoRoots;
|
|
2925
|
+
if (!allowedIoRoots) {
|
|
2926
|
+
if (config.transport === "http") {
|
|
2927
|
+
const errorMsg = `FATAL: Refusing to bind HTTP transport without explicit ALLOWED_IO_ROOTS. You MUST specify --allowed-io-roots (or ALLOWED_IO_ROOTS env var) to prevent ambient filesystem authority.`;
|
|
2928
|
+
logger.error(errorMsg, { module: "SERVER" });
|
|
2929
|
+
process.exit(1);
|
|
2930
|
+
}
|
|
2931
|
+
allowedIoRoots = [];
|
|
2932
|
+
logger.warning(
|
|
2933
|
+
"\u26A0\uFE0F SECURITY WARNING: ALLOWED_IO_ROOTS not explicitly provided. Defaulting to empty array (NO filesystem access). You MUST specify --allowed-io-roots (or ALLOWED_IO_ROOTS env var) to enable filesystem tools.",
|
|
2934
|
+
{ module: "SERVER" }
|
|
2935
|
+
);
|
|
2936
|
+
} else {
|
|
2937
|
+
logger.info("IO sandbox configured", {
|
|
2938
|
+
module: "SERVER",
|
|
2939
|
+
allowedRoots: allowedIoRoots
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2942
|
+
this.config.allowedIoRoots = allowedIoRoots;
|
|
2933
2943
|
this.toolFilter = config.toolFilter ? parseToolFilter(config.toolFilter) : getToolFilterFromEnv();
|
|
2934
2944
|
this.server = new McpServer(
|
|
2935
2945
|
{
|
|
@@ -2961,7 +2971,7 @@ var DbMcpServer = class {
|
|
|
2961
2971
|
if (!["sqlite://schema", "sqlite://tables", "sqlite://health"].includes(
|
|
2962
2972
|
uri
|
|
2963
2973
|
) && !uri.startsWith("sqlite://table/")) {
|
|
2964
|
-
throw new
|
|
2974
|
+
throw new ValidationError(`Resource ${uri} is not subscribable`);
|
|
2965
2975
|
}
|
|
2966
2976
|
this.subscriptionManager.subscribe(
|
|
2967
2977
|
uri,
|
|
@@ -3010,7 +3020,7 @@ var DbMcpServer = class {
|
|
|
3010
3020
|
throw new DbMcpError(
|
|
3011
3021
|
"Security: SDK _requestHandlers monkey-patch failed. Scope filtering is disabled.",
|
|
3012
3022
|
"SERVER_START_FAILED",
|
|
3013
|
-
|
|
3023
|
+
ErrorCategory.INTERNAL
|
|
3014
3024
|
);
|
|
3015
3025
|
}
|
|
3016
3026
|
const originalListToolsHandler = handlers.get("tools/list");
|
|
@@ -3045,6 +3055,9 @@ var DbMcpServer = class {
|
|
|
3045
3055
|
}
|
|
3046
3056
|
await adapter.connect(config);
|
|
3047
3057
|
this.adapters.set(adapterId, adapter);
|
|
3058
|
+
if ("setAllowedIoRoots" in adapter) {
|
|
3059
|
+
adapter.setAllowedIoRoots(this.config.allowedIoRoots);
|
|
3060
|
+
}
|
|
3048
3061
|
if (this.auditInterceptor && "setAuditInterceptor" in adapter) {
|
|
3049
3062
|
adapter.setAuditInterceptor(this.auditInterceptor);
|
|
3050
3063
|
this.auditInterceptor.setQueryAdapter({
|
|
@@ -3100,7 +3113,7 @@ var DbMcpServer = class {
|
|
|
3100
3113
|
throw new DbMcpError(
|
|
3101
3114
|
`Unsupported transport: ${this.config.transport}`,
|
|
3102
3115
|
"SERVER_START_FAILED",
|
|
3103
|
-
|
|
3116
|
+
ErrorCategory.CONFIGURATION
|
|
3104
3117
|
);
|
|
3105
3118
|
}
|
|
3106
3119
|
}
|
|
@@ -3122,7 +3135,7 @@ var DbMcpServer = class {
|
|
|
3122
3135
|
* Start server with HTTP transport (Streamable HTTP with SSE support)
|
|
3123
3136
|
*/
|
|
3124
3137
|
async startHttp() {
|
|
3125
|
-
const { HttpTransport } = await import('./http-
|
|
3138
|
+
const { HttpTransport } = await import('./http-ZDMK46QU.js');
|
|
3126
3139
|
const oauthConfig = {
|
|
3127
3140
|
enabled: this.config.oauth?.enabled ?? false,
|
|
3128
3141
|
authorizationServerUrl: this.config.oauth?.authorizationServerUrl ?? "",
|