taurusdb-mcp 0.4.0 → 0.5.0-rc.10
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/LICENSE +21 -0
- package/README.md +44 -13
- package/dist/commands/approve.d.ts +1 -0
- package/dist/commands/approve.js +66 -0
- package/dist/index.js +5 -0
- package/dist/security/database-endpoint-preflight.d.ts +8 -0
- package/dist/security/database-endpoint-preflight.js +33 -0
- package/dist/security/local-credential-login.d.ts +50 -0
- package/dist/security/local-credential-login.js +456 -0
- package/dist/security/session-coordinator.d.ts +9 -0
- package/dist/security/session-coordinator.js +46 -0
- package/dist/security/session-credential-manager.d.ts +29 -0
- package/dist/security/session-credential-manager.js +85 -0
- package/dist/server.d.ts +14 -1
- package/dist/server.js +30 -2
- package/dist/tools/common/public-formatters.d.ts +8 -0
- package/dist/tools/common/public-formatters.js +4 -0
- package/dist/tools/error-handling.js +38 -1
- package/dist/tools/query.d.ts +1 -1
- package/dist/tools/query.js +234 -47
- package/dist/tools/registry.d.ts +5 -0
- package/dist/tools/registry.js +161 -10
- package/dist/tools/taurus/cloud-context.d.ts +1 -0
- package/dist/tools/taurus/cloud-context.js +137 -49
- package/dist/tools/taurus/diagnostics.d.ts +1 -0
- package/dist/tools/taurus/diagnostics.js +31 -0
- package/dist/tools/taurus/recycle-bin.js +107 -90
- package/dist/tools/taurus/sql-login.d.ts +3 -0
- package/dist/tools/taurus/sql-login.js +200 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +3 -1
- package/package.json +14 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TaurusDB MCP contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ TaurusDB MCP server for MCP clients such as:
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm install taurusdb-mcp
|
|
13
|
+
npm install taurusdb-mcp@latest
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Run directly with:
|
|
@@ -28,17 +28,23 @@ Use this command in MCP client configs:
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Configure Huawei Cloud identity through the operating-system credential store
|
|
32
|
+
before adding the server. AK/SK values should not be copied into MCP client
|
|
33
|
+
configuration:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx -y taurusdb-mcp credentials configure
|
|
37
|
+
TAURUSDB_CLOUD_KEYCHAIN_SERVICE=taurusdb-mcp/huaweicloud \
|
|
38
|
+
npx -y taurusdb-mcp credentials check
|
|
39
|
+
```
|
|
40
|
+
|
|
31
41
|
Claude Code:
|
|
32
42
|
|
|
33
43
|
```bash
|
|
34
44
|
claude mcp add huaweicloud-taurusdb \
|
|
35
45
|
--transport stdio \
|
|
36
46
|
-e TAURUSDB_CLOUD_REGION=<your-region> \
|
|
37
|
-
-e
|
|
38
|
-
-e TAURUSDB_CLOUD_SECRET_ACCESS_KEY=<your-sk> \
|
|
39
|
-
-e TAURUSDB_SQL_DATABASE=<your-database> \
|
|
40
|
-
-e TAURUSDB_SQL_USER=<your-readonly-user> \
|
|
41
|
-
-e TAURUSDB_SQL_PASSWORD='hw-kms-file:~/.taurusdb-mcp/password.ciphertext' \
|
|
47
|
+
-e TAURUSDB_CLOUD_KEYCHAIN_SERVICE=taurusdb-mcp/huaweicloud \
|
|
42
48
|
-- npx -y taurusdb-mcp
|
|
43
49
|
```
|
|
44
50
|
|
|
@@ -47,14 +53,20 @@ Codex:
|
|
|
47
53
|
```bash
|
|
48
54
|
codex mcp add huaweicloud-taurusdb \
|
|
49
55
|
--env TAURUSDB_CLOUD_REGION=<your-region> \
|
|
50
|
-
--env
|
|
51
|
-
--env TAURUSDB_CLOUD_SECRET_ACCESS_KEY=<your-sk> \
|
|
52
|
-
--env TAURUSDB_SQL_DATABASE=<your-database> \
|
|
53
|
-
--env TAURUSDB_SQL_USER=<your-readonly-user> \
|
|
54
|
-
--env TAURUSDB_SQL_PASSWORD='hw-kms-file:~/.taurusdb-mcp/password.ciphertext' \
|
|
56
|
+
--env TAURUSDB_CLOUD_KEYCHAIN_SERVICE=taurusdb-mcp/huaweicloud \
|
|
55
57
|
-- npx -y taurusdb-mcp
|
|
56
58
|
```
|
|
57
59
|
|
|
60
|
+
Interactive connection flow:
|
|
61
|
+
|
|
62
|
+
1. Call `list_cloud_taurus_instances`.
|
|
63
|
+
2. Call `select_cloud_taurus_instance`.
|
|
64
|
+
3. Open the returned `login_url` in a browser and enter the database credentials.
|
|
65
|
+
4. Continue with `list_databases`, `set_default_database`, and readonly tools.
|
|
66
|
+
|
|
67
|
+
The browser submits credentials directly to the loopback MCP process. They are not
|
|
68
|
+
sent through Agent-visible tool arguments or persisted by MCP.
|
|
69
|
+
|
|
58
70
|
Initialize local MCP client config:
|
|
59
71
|
|
|
60
72
|
```bash
|
|
@@ -74,6 +86,25 @@ npx taurusdb-mcp credentials check
|
|
|
74
86
|
|
|
75
87
|
- Requires Node.js `>= 20`
|
|
76
88
|
- Depends on `taurusdb-core`
|
|
77
|
-
-
|
|
78
|
-
-
|
|
89
|
+
- Interactive clients do not need database usernames or passwords in MCP configuration
|
|
90
|
+
- Unattended static deployments may use `env:`, `file:`, `hw-csms:`, `hw-kms:`, or `hw-kms-file:` password references
|
|
79
91
|
- macOS Keychain, Linux Secret Service, or Windows Credential Manager cloud identity is enabled with `TAURUSDB_CLOUD_KEYCHAIN_SERVICE`
|
|
92
|
+
- SQL TLS with certificate verification is required by default
|
|
93
|
+
- General database mutation tools do not exist; the Agent-facing operating plane remains read-only regardless of database account privileges
|
|
94
|
+
- `restore_recycle_bin_table` is visible by default and directly restores one exact recycle-bin object to one explicit non-existing destination after readonly preflight; it uses the active in-memory SQL session, post-verifies the destination, and writes audit evidence without a browser approval step
|
|
95
|
+
- `analyze_mutation_sql` returns evidence-backed SQL Advice with `execution_status: not_executed`
|
|
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
|
|
98
|
+
- Before issuing a login link, instance selection performs a credential-free TCP endpoint preflight and returns actionable security-group/network guidance when the public database port is unreachable
|
|
99
|
+
- Login validation distinguishes unreachable endpoint, refused port, TLS, authentication, and database validation timeout failures instead of returning a generic HTTP 504
|
|
100
|
+
- Recoverable login errors render their structured code and remediation directly in the browser page with HTTP 200 so embedded webviews do not hide the message; cross-origin requests remain blocked with HTTP 403
|
|
101
|
+
- Instance selection and local login tools are enabled by default; fixed static deployments may set `TAURUSDB_ENABLE_DYNAMIC_TARGETS=false`
|
|
102
|
+
- 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`
|
|
103
|
+
- MCP audit events are written to `~/.taurusdb-mcp/audit.jsonl` by default, with
|
|
104
|
+
configurable size rotation for collection into centralized immutable storage
|
|
105
|
+
- Run one stdio process per customer/client trust boundary; this package is not
|
|
106
|
+
a shared multi-tenant HTTP service
|
|
107
|
+
|
|
108
|
+
Customers execute reviewed general-purpose `advised_sql` through their own controlled
|
|
109
|
+
change process. The only MCP database-state exception is the target-bound,
|
|
110
|
+
same-browser-confirmed recycle-bin recovery described above.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runApprove(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { signApprovalRequest } from "taurusdb-core";
|
|
5
|
+
function readOption(args, name) {
|
|
6
|
+
const index = args.indexOf(name);
|
|
7
|
+
if (index === -1) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const value = args[index + 1];
|
|
11
|
+
if (!value || value.startsWith("--")) {
|
|
12
|
+
throw new Error(`${name} requires a value.`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function expandTilde(input) {
|
|
17
|
+
if (input === "~") {
|
|
18
|
+
return os.homedir();
|
|
19
|
+
}
|
|
20
|
+
return input.startsWith("~/") ? path.join(os.homedir(), input.slice(2)) : input;
|
|
21
|
+
}
|
|
22
|
+
function usage() {
|
|
23
|
+
return `Usage: taurusdb-mcp approve --request <approval-request> --actor <identity> [options]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--secret-file <path> Approval secret file; defaults to
|
|
27
|
+
TAURUSDB_MUTATION_APPROVAL_SECRET_FILE
|
|
28
|
+
--request <value> approval_request returned by a mutation tool
|
|
29
|
+
--actor <identity> Human approver identity recorded in the signed token
|
|
30
|
+
|
|
31
|
+
The command prints a one-time approval_token. Run it outside the MCP client
|
|
32
|
+
after reviewing the SQL hash, datasource, database, risk, and expiry encoded
|
|
33
|
+
in approval_request.`;
|
|
34
|
+
}
|
|
35
|
+
async function readProtectedSecret(filePath) {
|
|
36
|
+
const resolved = expandTilde(filePath);
|
|
37
|
+
const info = await stat(resolved);
|
|
38
|
+
if (!info.isFile()) {
|
|
39
|
+
throw new Error("Mutation approval secret path must reference a regular file.");
|
|
40
|
+
}
|
|
41
|
+
if (process.platform !== "win32" && (info.mode & 0o077) !== 0) {
|
|
42
|
+
throw new Error("Mutation approval secret file must not be accessible by group or other users.");
|
|
43
|
+
}
|
|
44
|
+
const secret = (await readFile(resolved, "utf8")).trim();
|
|
45
|
+
if (Buffer.byteLength(secret, "utf8") < 32) {
|
|
46
|
+
throw new Error("Mutation approval secret must contain at least 32 bytes.");
|
|
47
|
+
}
|
|
48
|
+
return secret;
|
|
49
|
+
}
|
|
50
|
+
export async function runApprove(args) {
|
|
51
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
52
|
+
process.stdout.write(`${usage()}\n`);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
const request = readOption(args, "--request");
|
|
56
|
+
const actor = readOption(args, "--actor");
|
|
57
|
+
const secretFile = readOption(args, "--secret-file") ??
|
|
58
|
+
process.env.TAURUSDB_MUTATION_APPROVAL_SECRET_FILE?.trim();
|
|
59
|
+
if (!request || !actor || !secretFile) {
|
|
60
|
+
process.stderr.write(`${usage()}\n`);
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
const secret = await readProtectedSecret(secretFile);
|
|
64
|
+
process.stdout.write(`${signApprovalRequest(request, actor, secret)}\n`);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runInit } from "./commands/init.js";
|
|
3
3
|
import { runCredentials } from "./commands/credentials.js";
|
|
4
|
+
import { runApprove } from "./commands/approve.js";
|
|
4
5
|
import { startMcpServer } from "./server.js";
|
|
5
6
|
import { VERSION } from "./version.js";
|
|
6
7
|
async function main() {
|
|
@@ -14,6 +15,10 @@ async function main() {
|
|
|
14
15
|
process.exitCode = await runCredentials(args.slice(1));
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
18
|
+
if (firstArg === "approve") {
|
|
19
|
+
process.exitCode = await runApprove(args.slice(1));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
17
22
|
if (firstArg === "--version" || firstArg === "-v") {
|
|
18
23
|
process.stdout.write(`${VERSION}\n`);
|
|
19
24
|
return;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type DatabaseEndpointFailure = "refused" | "unreachable";
|
|
2
|
+
export declare class DatabaseEndpointPreflightError extends Error {
|
|
3
|
+
readonly kind: DatabaseEndpointFailure;
|
|
4
|
+
readonly host: string;
|
|
5
|
+
readonly port: number;
|
|
6
|
+
constructor(kind: DatabaseEndpointFailure, host: string, port: number);
|
|
7
|
+
}
|
|
8
|
+
export declare function preflightDatabaseEndpoint(host: string, port: number, timeoutMs?: number): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createConnection } from "node:net";
|
|
2
|
+
export class DatabaseEndpointPreflightError extends Error {
|
|
3
|
+
kind;
|
|
4
|
+
host;
|
|
5
|
+
port;
|
|
6
|
+
constructor(kind, host, port) {
|
|
7
|
+
super(`Database endpoint preflight failed: ${kind}`);
|
|
8
|
+
this.name = "DatabaseEndpointPreflightError";
|
|
9
|
+
this.kind = kind;
|
|
10
|
+
this.host = host;
|
|
11
|
+
this.port = port;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function preflightDatabaseEndpoint(host, port, timeoutMs = 3_000) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
let settled = false;
|
|
17
|
+
const finish = (error) => {
|
|
18
|
+
if (settled)
|
|
19
|
+
return;
|
|
20
|
+
settled = true;
|
|
21
|
+
socket.destroy();
|
|
22
|
+
if (error)
|
|
23
|
+
reject(error);
|
|
24
|
+
else
|
|
25
|
+
resolve();
|
|
26
|
+
};
|
|
27
|
+
const socket = createConnection({ host, port });
|
|
28
|
+
socket.setTimeout(timeoutMs);
|
|
29
|
+
socket.once("connect", () => finish());
|
|
30
|
+
socket.once("timeout", () => finish(new DatabaseEndpointPreflightError("unreachable", host, port)));
|
|
31
|
+
socket.once("error", (error) => finish(new DatabaseEndpointPreflightError(error.code === "ECONNREFUSED" ? "refused" : "unreachable", host, port)));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type SqlLoginCredentials = {
|
|
2
|
+
datasource: string;
|
|
3
|
+
username: string;
|
|
4
|
+
password: string;
|
|
5
|
+
};
|
|
6
|
+
export type SqlLoginTarget = {
|
|
7
|
+
datasource: string;
|
|
8
|
+
instanceId?: string;
|
|
9
|
+
region?: string;
|
|
10
|
+
credentialIdleTtlMinutes?: number;
|
|
11
|
+
credentialMaxTtlMinutes?: number;
|
|
12
|
+
};
|
|
13
|
+
export type SqlLoginRequest = {
|
|
14
|
+
datasource: string;
|
|
15
|
+
target?: SqlLoginTarget;
|
|
16
|
+
bind: (credentials: SqlLoginCredentials) => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export type IssuedSqlLogin = {
|
|
19
|
+
loginUrl: string;
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
};
|
|
22
|
+
export type CredentialValidationFailure = "credentials" | "unreachable" | "refused" | "connectivity" | "tls" | "timeout";
|
|
23
|
+
export declare class SqlCredentialValidationError extends Error {
|
|
24
|
+
readonly kind: CredentialValidationFailure;
|
|
25
|
+
constructor(kind: CredentialValidationFailure);
|
|
26
|
+
}
|
|
27
|
+
export interface CredentialLoginService {
|
|
28
|
+
issueSqlLogin(request: SqlLoginRequest): Promise<IssuedSqlLogin>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export type LocalCredentialLoginServiceOptions = {
|
|
32
|
+
now?: () => number;
|
|
33
|
+
tokenTtlMs?: number;
|
|
34
|
+
maxAttempts?: number;
|
|
35
|
+
failureDelayMs?: number;
|
|
36
|
+
};
|
|
37
|
+
export declare class LocalCredentialLoginService implements CredentialLoginService {
|
|
38
|
+
private readonly now;
|
|
39
|
+
private readonly tokenTtlMs;
|
|
40
|
+
private readonly maxAttempts;
|
|
41
|
+
private readonly failureDelayMs;
|
|
42
|
+
private readonly pending;
|
|
43
|
+
private server;
|
|
44
|
+
private port;
|
|
45
|
+
constructor(options?: LocalCredentialLoginServiceOptions);
|
|
46
|
+
issueSqlLogin(request: SqlLoginRequest): Promise<IssuedSqlLogin>;
|
|
47
|
+
close(): Promise<void>;
|
|
48
|
+
private ensureStarted;
|
|
49
|
+
private handle;
|
|
50
|
+
}
|