@deepsql/mcp 0.21.0 → 0.22.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/package.json +1 -1
- package/scripts/postinstall.js +16 -46
- package/src/agent/bootstrap.js +3 -1
- package/src/cli.js +4 -4
- package/src/commands/agent.js +94 -22
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -1,57 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
// Postinstall:
|
|
5
|
-
// time so the first `deepsql` launch is instant instead of triggering a heavy
|
|
6
|
-
// one-time download. Best-effort and NON-FATAL — it never fails the npm install.
|
|
4
|
+
// Postinstall: intentionally a no-op heavy-wise.
|
|
7
5
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
6
|
+
// The DeepSQL Agent is now a THIN client — `deepsql` / `deepsql agent` talk to
|
|
7
|
+
// the server-side agent over the backend's brokered /agent/chat endpoint, so
|
|
8
|
+
// there is NO local agent runtime to download. `npm install -g @deepsql/mcp` is
|
|
9
|
+
// fast and quiet; nothing to pre-build.
|
|
10
10
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// - non-global installs (local dev `npm install`, dependency installs)
|
|
14
|
-
// The profile itself is provisioned later, on first `deepsql` after login,
|
|
15
|
-
// because it needs the user's token.
|
|
16
|
-
|
|
17
|
-
function log(msg) {
|
|
18
|
-
process.stdout.write(`${msg}\n`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function skip(reason) {
|
|
22
|
-
// Silent for the common "not global / CI" cases; only note an explicit opt-out.
|
|
23
|
-
if (reason) log(`DeepSQL Agent: ${reason}`);
|
|
24
|
-
process.exit(0);
|
|
25
|
-
}
|
|
11
|
+
// We only print a short next-steps hint on a global install (and stay silent for
|
|
12
|
+
// CI / local dependency installs so we never clutter other projects' logs).
|
|
26
13
|
|
|
27
14
|
try {
|
|
28
|
-
if (process.env.
|
|
29
|
-
skip("agent runtime setup skipped (DEEPSQL_SKIP_AGENT_SETUP set).");
|
|
30
|
-
}
|
|
31
|
-
if (process.env.CI) skip();
|
|
32
|
-
// Only pre-install on an explicit global install (or a forced run). Local /
|
|
33
|
-
// dependency installs shouldn't pull a Python+Node runtime.
|
|
15
|
+
if (process.env.CI) process.exit(0);
|
|
34
16
|
const isGlobal = String(process.env.npm_config_global || "") === "true";
|
|
35
|
-
if (!isGlobal
|
|
36
|
-
|
|
37
|
-
const { ensureHermes, hermesInstalled } = require("../src/agent/bootstrap");
|
|
38
|
-
|
|
39
|
-
if (hermesInstalled()) {
|
|
40
|
-
log("✓ DeepSQL Agent runtime already present.");
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
17
|
+
if (!isGlobal) process.exit(0);
|
|
43
18
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
log("DeepSQL Agent: runtime not pre-installed; it'll be set up on first `deepsql` run.");
|
|
52
|
-
}
|
|
53
|
-
} catch (e) {
|
|
54
|
-
// Never break the npm install over agent pre-setup.
|
|
55
|
-
log(`DeepSQL Agent: skipped runtime pre-install (${e && e.message ? e.message : e}).`);
|
|
19
|
+
process.stdout.write(
|
|
20
|
+
"\nDeepSQL installed. Next:\n" +
|
|
21
|
+
" deepsql login --url <your-deepsql-url>\n" +
|
|
22
|
+
" deepsql # chat with the DeepSQL Agent (connects to your server)\n\n"
|
|
23
|
+
);
|
|
24
|
+
} catch {
|
|
25
|
+
// Never let a cosmetic hint fail the install.
|
|
56
26
|
}
|
|
57
27
|
process.exit(0);
|
package/src/agent/bootstrap.js
CHANGED
|
@@ -113,7 +113,9 @@ function ensureHermes(io, { quiet = false } = {}) {
|
|
|
113
113
|
});
|
|
114
114
|
|
|
115
115
|
if (hermesInstalled()) {
|
|
116
|
-
err.write(
|
|
116
|
+
err.write(quiet
|
|
117
|
+
? "✓ DeepSQL Agent runtime installed.\n"
|
|
118
|
+
: "✓ DeepSQL Agent runtime installed. (Ignore any \"run hermes setup\" note above — DeepSQL configures it for you.)\n");
|
|
117
119
|
return true;
|
|
118
120
|
}
|
|
119
121
|
err.write(
|
package/src/cli.js
CHANGED
|
@@ -90,13 +90,13 @@ const GLOBAL_OPTIONS = [
|
|
|
90
90
|
|
|
91
91
|
const COMMAND_HELP = {
|
|
92
92
|
agent: {
|
|
93
|
-
description: "
|
|
94
|
-
usage:
|
|
93
|
+
description: "Chat with the DeepSQL Agent — DBA work, BI questions, and database guardrails. A thin client: it connects to your server's agent (the same one behind the web Agent tab and Slack), nothing installs locally. Bare `deepsql` in a terminal opens interactive chat; pass a question for a one-shot.",
|
|
94
|
+
usage: 'deepsql agent ["<question>"] [options] (or just: deepsql)',
|
|
95
95
|
options: [
|
|
96
96
|
["--url <url>", "DeepSQL instance to use (default: saved login)"],
|
|
97
|
-
["--connection <name>", "
|
|
97
|
+
["--connection <name>", "Connection to ground answers on (default: saved active connection)"],
|
|
98
98
|
],
|
|
99
|
-
notes: "
|
|
99
|
+
notes: "No local runtime or LLM key needed — the server runs the agent and proxies the model. Interactive chat resumes your most recent conversation (shared with the web Agent tab).",
|
|
100
100
|
},
|
|
101
101
|
|
|
102
102
|
login: {
|
package/src/commands/agent.js
CHANGED
|
@@ -1,15 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
// `deepsql agent` (and bare `deepsql` in a terminal):
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// `deepsql agent` (and bare `deepsql` in a terminal): the DeepSQL Agent, as a
|
|
4
|
+
// THIN client. It does not run an agent runtime locally — it talks to the
|
|
5
|
+
// server-side agent through the backend's brokered, authenticated
|
|
6
|
+
// `/agent/chat` endpoint (the same agent that powers the web Agent tab and
|
|
7
|
+
// Slack). Identity, profile, connection scope, and conversation history are all
|
|
8
|
+
// resolved server-side from your saved `deepsql login` — nothing heavy installs,
|
|
9
|
+
// and your terminal shares the same conversations as the web app.
|
|
10
|
+
//
|
|
11
|
+
// deepsql agent "how many active hotels?" one-shot
|
|
12
|
+
// deepsql interactive chat (resumes latest)
|
|
7
13
|
|
|
8
|
-
const
|
|
14
|
+
const readline = require("node:readline");
|
|
15
|
+
const { request } = require("../api/client");
|
|
9
16
|
const { resolveSession } = require("./_session");
|
|
10
|
-
const {
|
|
17
|
+
const { resolveConnectionId } = require("./_connections");
|
|
18
|
+
|
|
19
|
+
// The brokered turn can run a full multi-tool agent loop server-side; allow well
|
|
20
|
+
// past the backend's own per-turn ceiling so we don't abandon a valid answer.
|
|
21
|
+
const TURN_TIMEOUT_MS = 320000;
|
|
22
|
+
|
|
23
|
+
async function runTurn(session, connectionId, message, conversationId) {
|
|
24
|
+
return request(session.baseUrl, "/agent/chat", {
|
|
25
|
+
method: "POST",
|
|
26
|
+
token: session.token,
|
|
27
|
+
json: { message, connectionId, conversationId },
|
|
28
|
+
timeoutMs: TURN_TIMEOUT_MS,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function renderReply(stdout, resp) {
|
|
33
|
+
if (resp && resp.ok && resp.answer && String(resp.answer).trim()) {
|
|
34
|
+
stdout.write(`\n${String(resp.answer).trim()}\n`);
|
|
35
|
+
} else {
|
|
36
|
+
stdout.write(`\n⚠ ${(resp && resp.error) || "the agent run ended early"}\n`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
11
39
|
|
|
12
40
|
async function run(opts, io = {}) {
|
|
41
|
+
const stdout = io.stdout || process.stdout;
|
|
13
42
|
const stderr = io.stderr || process.stderr;
|
|
14
43
|
|
|
15
44
|
let session;
|
|
@@ -20,26 +49,69 @@ async function run(opts, io = {}) {
|
|
|
20
49
|
return 1;
|
|
21
50
|
}
|
|
22
51
|
|
|
23
|
-
if
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
// Connection is optional — the agent can chat without one and will ask if a
|
|
53
|
+
// question needs a database. Resolve the active default when present.
|
|
54
|
+
let connectionId = null;
|
|
55
|
+
try {
|
|
56
|
+
connectionId = await resolveConnectionId(session, opts.connection);
|
|
57
|
+
} catch {
|
|
58
|
+
/* no default connection set — fine */
|
|
26
59
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
60
|
+
|
|
61
|
+
const message = (opts.positional || []).join(" ").trim();
|
|
62
|
+
|
|
63
|
+
// One-shot: a question on the command line.
|
|
64
|
+
if (message) {
|
|
65
|
+
try {
|
|
66
|
+
const resp = await runTurn(session, connectionId, message, null);
|
|
67
|
+
renderReply(stdout, resp);
|
|
68
|
+
return resp && resp.ok ? 0 : 1;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
stderr.write(`Agent request failed: ${e.message}\n`);
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
30
73
|
}
|
|
31
|
-
// Branding is delivered via HERMES_TUI_DIR in launchEnv (Hermes runs our
|
|
32
|
-
// prebuilt branded TUI directly — no esbuild), set just below.
|
|
33
74
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
75
|
+
// Interactive: a line REPL against the server agent. Requires a TTY.
|
|
76
|
+
if (!stdout.isTTY) {
|
|
77
|
+
stderr.write('Usage: deepsql agent "<question>" (or run in a terminal for interactive chat)\n');
|
|
78
|
+
return 2;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
stdout.write("DeepSQL Agent — connected to your server agent. Type a question; `exit` or Ctrl-C to quit.\n");
|
|
82
|
+
// conversationId stays null on the first turn so the server resumes your most
|
|
83
|
+
// recent conversation for this connection (shared with the web Agent tab);
|
|
84
|
+
// subsequent turns reuse the id it returns.
|
|
85
|
+
let conversationId = null;
|
|
86
|
+
const rl = readline.createInterface({ input: process.stdin, output: stdout, prompt: "\nyou › " });
|
|
87
|
+
rl.prompt();
|
|
88
|
+
|
|
38
89
|
return await new Promise((resolve) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
90
|
+
rl.on("line", async (line) => {
|
|
91
|
+
const text = line.trim();
|
|
92
|
+
if (!text) {
|
|
93
|
+
rl.prompt();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (text === "exit" || text === "quit") {
|
|
97
|
+
rl.close();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
rl.pause();
|
|
101
|
+
stdout.write("…thinking\n");
|
|
102
|
+
try {
|
|
103
|
+
const resp = await runTurn(session, connectionId, text, conversationId);
|
|
104
|
+
if (resp && resp.conversationId) conversationId = resp.conversationId;
|
|
105
|
+
renderReply(stdout, resp);
|
|
106
|
+
} catch (e) {
|
|
107
|
+
stdout.write(`\n⚠ ${e.message}\n`);
|
|
108
|
+
}
|
|
109
|
+
rl.resume();
|
|
110
|
+
rl.prompt();
|
|
111
|
+
});
|
|
112
|
+
rl.on("close", () => {
|
|
113
|
+
stdout.write("\nBye.\n");
|
|
114
|
+
resolve(0);
|
|
43
115
|
});
|
|
44
116
|
});
|
|
45
117
|
}
|