@deepsql/mcp 0.2.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.
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ const store = require("../auth/store");
4
+ const { request } = require("../api/client");
5
+
6
+ async function run(opts, { stdout = process.stdout, stderr = process.stderr } = {}) {
7
+ const baseUrl = opts.url ? store.normalizeBaseUrl(opts.url) : store.defaultBaseUrl();
8
+ if (!baseUrl) {
9
+ stdout.write("No saved DeepSQL profile to log out of.\n");
10
+ return;
11
+ }
12
+ const profile = store.getProfile(baseUrl);
13
+ if (!profile) {
14
+ stdout.write(`No saved profile for ${baseUrl}.\n`);
15
+ return;
16
+ }
17
+
18
+ if (profile.tokenId) {
19
+ try {
20
+ await request(baseUrl, `/auth/mcp-tokens/${profile.tokenId}`, {
21
+ method: "DELETE",
22
+ token: profile.token,
23
+ });
24
+ } catch (err) {
25
+ stderr.write(`[deepsql] Could not revoke token server-side: ${err.message}\n`);
26
+ }
27
+ }
28
+
29
+ store.removeProfile(baseUrl);
30
+ stdout.write(`Logged out of ${baseUrl}.\n`);
31
+ }
32
+
33
+ module.exports = { run };
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ const path = require("node:path");
4
+ const { spawn } = require("node:child_process");
5
+
6
+ const { resolveSession } = require("./_session");
7
+
8
+ /**
9
+ * Launch the existing Phase 1 stdio MCP server with the saved auth token
10
+ * injected via env vars. The server is unchanged — this command is a thin
11
+ * shim that means editor configs no longer need to embed a raw token.
12
+ */
13
+ async function run(opts) {
14
+ const session = resolveSession(opts);
15
+ const serverPath = path.resolve(__dirname, "..", "..", "deepsql-phase1-server.js");
16
+ const env = {
17
+ ...process.env,
18
+ DEEPSQL_API_BASE_URL: `${session.baseUrl}/api/`,
19
+ DEEPSQL_AUTH_TOKEN: session.token,
20
+ };
21
+ const child = spawn(process.execPath, [serverPath], {
22
+ stdio: ["inherit", "inherit", "inherit"],
23
+ env,
24
+ });
25
+ child.on("exit", (code, signal) => {
26
+ if (signal) process.kill(process.pid, signal);
27
+ else process.exit(code ?? 0);
28
+ });
29
+ }
30
+
31
+ module.exports = { run };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const { request } = require("../api/client");
5
+ const { validateReadOnlySql } = require("../../deepsql-phase1-lib");
6
+ const { resolveSession } = require("./_session");
7
+
8
+ async function run(opts, { stdout = process.stdout } = {}) {
9
+ if (!opts.connection) throw new Error("--connection <id> is required.");
10
+ const sql = readSqlInput(opts);
11
+ const validation = validateReadOnlySql(sql, { allowExplain: true });
12
+ if (!validation.ok) throw new Error(validation.reason);
13
+
14
+ const session = resolveSession(opts);
15
+ const limit = clampInt(opts.limit, 1, 1000, 100);
16
+ const timeout = opts.timeoutSeconds == null ? null : clampInt(opts.timeoutSeconds, 1, 60, null);
17
+
18
+ const response = await request(session.baseUrl, "/mcp/query-readonly", {
19
+ method: "POST",
20
+ token: session.token,
21
+ json: {
22
+ connectionId: opts.connection,
23
+ query: validation.normalizedQuery,
24
+ limit,
25
+ timeoutSeconds: timeout,
26
+ },
27
+ });
28
+
29
+ if (opts.json) {
30
+ stdout.write(`${JSON.stringify(response, null, 2)}\n`);
31
+ return;
32
+ }
33
+ printRows(stdout, response);
34
+ }
35
+
36
+ function readSqlInput(opts) {
37
+ if (opts.file) return fs.readFileSync(opts.file, "utf8");
38
+ if (opts.positional.length > 0) return opts.positional.join(" ");
39
+ if (!process.stdin.isTTY) return fs.readFileSync(0, "utf8");
40
+ throw new Error("Pass SQL as an argument, via --file <path>, or pipe it to stdin.");
41
+ }
42
+
43
+ function clampInt(value, min, max, fallback) {
44
+ if (value == null) return fallback;
45
+ const n = Number.parseInt(value, 10);
46
+ if (!Number.isFinite(n)) return fallback;
47
+ return Math.min(max, Math.max(min, n));
48
+ }
49
+
50
+ function printRows(stdout, response) {
51
+ const rows = response?.rows || response?.data || [];
52
+ const columns = response?.columns || (rows[0] ? Object.keys(rows[0]) : []);
53
+ if (columns.length === 0) {
54
+ stdout.write("(no rows)\n");
55
+ return;
56
+ }
57
+ const widths = columns.map((c) => Math.max(c.length, ...rows.map((r) => String(r[c] ?? "").length)));
58
+ const sep = widths.map((w) => "-".repeat(w)).join(" ");
59
+ stdout.write(`${columns.map((c, i) => c.padEnd(widths[i])).join(" ")}\n${sep}\n`);
60
+ for (const row of rows) {
61
+ stdout.write(`${columns.map((c, i) => String(row[c] ?? "").padEnd(widths[i])).join(" ")}\n`);
62
+ }
63
+ if (response?.truncated) stdout.write(`(truncated to ${rows.length} rows)\n`);
64
+ }
65
+
66
+ module.exports = { run };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ const { request } = require("../api/client");
4
+ const { resolveSession } = require("./_session");
5
+
6
+ async function run(opts, { stdout = process.stdout } = {}) {
7
+ if (!opts.connection) throw new Error("--connection <id> is required.");
8
+ const session = resolveSession(opts);
9
+ const sub = opts.positional[0] || "tables";
10
+ const path =
11
+ sub === "objects"
12
+ ? `/connections/${encodeURIComponent(opts.connection)}/objects`
13
+ : `/connections/${encodeURIComponent(opts.connection)}/schema`;
14
+ const response = await request(session.baseUrl, path, { token: session.token });
15
+ stdout.write(`${JSON.stringify(response, null, 2)}\n`);
16
+ }
17
+
18
+ module.exports = { run };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ const store = require("../auth/store");
4
+ const { request } = require("../api/client");
5
+ const { resolveSession } = require("./_session");
6
+
7
+ async function run(opts, { stdout = process.stdout } = {}) {
8
+ const session = resolveSession(opts);
9
+ try {
10
+ const me = await request(session.baseUrl, "/auth/me", { token: session.token });
11
+ stdout.write(`Username: ${me.username || me.email || session.profile?.username || "(unknown)"}\n`);
12
+ if (me.role) stdout.write(`Role: ${me.role}\n`);
13
+ stdout.write(`URL: ${session.baseUrl}\n`);
14
+ if (session.profile?.tokenId) stdout.write(`Token id: ${session.profile.tokenId}\n`);
15
+ } catch (err) {
16
+ if (err.status === 401 || err.status === 403) {
17
+ throw new Error("Saved token is no longer valid. Run `deepsql login` again.");
18
+ }
19
+ throw err;
20
+ }
21
+ }
22
+
23
+ module.exports = { run };