taurusdb-mcp 0.5.0-rc.5 → 0.5.0-rc.7
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
CHANGED
|
@@ -94,6 +94,7 @@ npx taurusdb-mcp credentials check
|
|
|
94
94
|
- Recycle-bin request/status tools are visible by default: `prepare_recycle_bin_restore` performs readonly preflight and the loopback-only operator page requires the Agent-invisible HttpOnly browser session established by database login before executing one target-bound native restore with the active in-memory SQL session; no secret file, direct restore tool, or Agent-held execution token is required
|
|
95
95
|
- `analyze_mutation_sql` returns evidence-backed SQL Advice with `execution_status: not_executed`
|
|
96
96
|
- Selecting a cloud instance returns a local SQL login link immediately; login validates the connection before binding credentials, allows at most three attempts per five-minute link, and never sends the password through Agent-visible tool arguments
|
|
97
|
+
- Interactive instance selection binds the read/write public IP and fails immediately when the instance has no public IP; it never falls back to a VPC-private address that a local MCP client cannot route to
|
|
97
98
|
- Instance selection and local login tools are enabled by default; fixed static deployments may set `TAURUSDB_ENABLE_DYNAMIC_TARGETS=false`
|
|
98
99
|
- Session SQL credentials expire after 30 idle minutes and eight absolute hours; administrators may shorten these limits with `TAURUSDB_SQL_CREDENTIAL_IDLE_TTL_MINUTES` and `TAURUSDB_SQL_CREDENTIAL_MAX_TTL_MINUTES`
|
|
99
100
|
- MCP audit events are written to `~/.taurusdb-mcp/audit.jsonl` by default, with
|
|
@@ -261,6 +261,35 @@ async function readBody(req) {
|
|
|
261
261
|
function delay(ms) {
|
|
262
262
|
return ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
|
|
263
263
|
}
|
|
264
|
+
function isLoopbackHostname(hostname) {
|
|
265
|
+
const normalized = hostname.toLowerCase();
|
|
266
|
+
return normalized === "127.0.0.1" || normalized === "localhost" || normalized === "[::1]";
|
|
267
|
+
}
|
|
268
|
+
function isAllowedBrowserRequest(req, port) {
|
|
269
|
+
if (!port)
|
|
270
|
+
return false;
|
|
271
|
+
try {
|
|
272
|
+
const requestUrl = new URL(`http://${req.headers.host ?? ""}`);
|
|
273
|
+
if (!isLoopbackHostname(requestUrl.hostname) || requestUrl.port !== String(port)) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
const origin = req.headers.origin;
|
|
281
|
+
if (!origin || origin === "null")
|
|
282
|
+
return true;
|
|
283
|
+
try {
|
|
284
|
+
const originUrl = new URL(origin);
|
|
285
|
+
return originUrl.protocol === "http:"
|
|
286
|
+
&& isLoopbackHostname(originUrl.hostname)
|
|
287
|
+
&& originUrl.port === String(port);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
264
293
|
export class LocalCredentialLoginService {
|
|
265
294
|
now;
|
|
266
295
|
tokenTtlMs;
|
|
@@ -349,8 +378,7 @@ export class LocalCredentialLoginService {
|
|
|
349
378
|
respond(res, 405, page({ locale, state: "method", target: pending.target }));
|
|
350
379
|
return;
|
|
351
380
|
}
|
|
352
|
-
|
|
353
|
-
if (origin && origin !== `http://${req.headers.host}`) {
|
|
381
|
+
if (!isAllowedBrowserRequest(req, this.port)) {
|
|
354
382
|
respond(res, 403, page({ locale, state: "invalid", target: pending.target }));
|
|
355
383
|
return;
|
|
356
384
|
}
|
|
@@ -66,7 +66,7 @@ async function resolveBindingDatasource(deps, explicit) {
|
|
|
66
66
|
return deps.engine.getDefaultDataSource();
|
|
67
67
|
}
|
|
68
68
|
function selectInstanceAddress(input) {
|
|
69
|
-
return input.
|
|
69
|
+
return input.publicIps[0];
|
|
70
70
|
}
|
|
71
71
|
function normalizePort(port) {
|
|
72
72
|
if (typeof port === "number" && Number.isFinite(port)) {
|
|
@@ -327,7 +327,7 @@ export const selectCloudTaurusInstanceTool = {
|
|
|
327
327
|
throw new ToolInputError("No datasource template is available for SQL login. Configure TAURUSDB_DEFAULT_DATASOURCE or pass datasource explicitly.");
|
|
328
328
|
}
|
|
329
329
|
if (!selectedHost) {
|
|
330
|
-
throw new ToolInputError(`Cloud instance ${matched.id} does not
|
|
330
|
+
throw new ToolInputError(`Cloud instance ${matched.id} does not have a public database IP. Enable a read/write public IP and restrict port ${selectedPort ?? 3306} to the MCP client's egress IP before using local SQL login.`);
|
|
331
331
|
}
|
|
332
332
|
await updateSessionState(deps, (nextConfig) => {
|
|
333
333
|
nextConfig.cloud.projectId = projectId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taurusdb-mcp",
|
|
3
|
-
"version": "0.5.0-rc.
|
|
3
|
+
"version": "0.5.0-rc.7",
|
|
4
4
|
"description": "TaurusDB MCP server for Claude, Cursor, VS Code, and other MCP clients.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"test": "node --test tests/*.test.mjs"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"taurusdb-core": "0.5.0-rc.
|
|
47
|
+
"taurusdb-core": "0.5.0-rc.7",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.17.0",
|
|
49
49
|
"mysql2": "^3.22.1",
|
|
50
50
|
"zod": "^3.24.1"
|