grain-tools 0.10.7 → 0.10.9

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
@@ -105,38 +105,41 @@ named connection profile when you open **Grain: Show Welcome Guide** or
105
105
 
106
106
  ## Quick Start: MCP Agents
107
107
 
108
- ### One command
108
+ ### Install once, then connect every client
109
109
 
110
110
  ```
111
- npx -y grain-tools --stdio
111
+ grain --stdio
112
112
  ```
113
113
 
114
- That is the command every MCP client is given, on every platform. There is no
115
- second setup path.
114
+ This is the normal command for an installed Grain setup on every platform. It
115
+ attaches to the shared local Grain service when that service is available, so
116
+ connections and result tabs stay shared. If the service cannot be reached, the
117
+ bridge reports isolated mode and its sessions do not appear in the editor.
116
118
 
117
119
  1. Run **Grain: Manage Connections** and confirm your active connection.
118
120
  2. Open the Grain settings panel and **enable the MCP server** (`grain.mcpEnabled`).
119
121
  This is required and ships **off** — the daemon serves no MCP surface until you turn it on.
120
122
  3. Keep safe mode enabled unless you explicitly want agents to run mutating SQL.
121
123
  4. Give your client the command:
122
- - **Claude Code**: `claude mcp add grain -- npx -y grain-tools --stdio`
124
+ - **Claude Code**: `claude mcp add grain -- grain --stdio`
123
125
  - **Cursor**: Settings → MCP Servers → Add new MCP server, type `command`,
124
- command `npx -y grain-tools --stdio`
125
- - **VS Code**: open the Extensions view, search `@mcp Grain`, and install it
126
- into your user profile or the current workspace — the gallery entry carries
127
- the command
126
+ command `grain --stdio`
127
+ - **VS Code**: add `grain --stdio` through **MCP: Open User Configuration**;
128
+ VS Code uses a `servers` key rather than `mcpServers`
128
129
  - **Claude Desktop and other file-configured clients**: see
129
130
  [the setup guide](docs/engineering/guides/mcp-client-setup.md), which
130
131
  transcribes the same command into that client's config file
131
132
 
132
- This bridges to the daemon the VS Code extension is already running, so agent
133
- queries and sessions show up in the results grid alongside anything you run from
134
- the editor. It works with every MCP client — including ones that cannot send
133
+ This bridges to the shared Grain service, so agent queries and sessions show up
134
+ in the results grid alongside anything you run from the editor. It works with
135
+ every MCP client — including ones that cannot send
135
136
  custom HTTP headers, such as Claude Desktop — and it needs no credential in your
136
137
  config, because the bridge reads the daemon's access token itself.
137
138
 
138
- `-y` is not decoration: an MCP client starts the server with no terminal
139
- attached, so npm's install prompt would have nobody to answer it.
139
+ To try Grain without installing it, use `npx -y grain-tools --stdio`. This
140
+ requires Node.js and may run in isolated mode when no shared Grain service is
141
+ available, so it is a trial and headless path rather than the recommended
142
+ desktop setup.
140
143
 
141
144
  Enabling `grain.mcpEnabled` in the Grain settings panel is required
142
145
  for this stdio bridge too. Launching the bridge without it produces a clear
@@ -199,7 +202,7 @@ command carries its own connection profiles through the environment:
199
202
 
200
203
  ```bash
201
204
  GRAIN_CONNECTIONS='[{"id":"analytics","name":"Analytics","type":"postgres","host":"localhost","port":5432,"user":"analyst","database":"warehouse","password":"YOUR_PASSWORD"}]' \
202
- npx -y grain-tools --stdio
205
+ grain --stdio --embedded
203
206
  ```
204
207
 
205
208
  The same variable goes in an `env` block for a file-configured client; see
@@ -274,7 +277,7 @@ settings JSON by hand.
274
277
  | `grain.alwaysRunInNewTab` | `false` | Always open query results in a new tab |
275
278
  | `grain.mcpEnabled` | `false` | Serve the local MCP surface for agent access. Off means `/mcp` is not registered (404) |
276
279
  | `grain.mcpSafeMode` | `true` | Restrict MCP query execution to read-only statements |
277
- | `grain.telemetry.enabled` | `false` | Opt in to anonymous product telemetry |
280
+ | `grain.telemetry.enabled` | `true` | Anonymous product telemetry; follows your VS Code telemetry setting |
278
281
  | `grain.telemetryHost` | `https://us.i.posthog.com` | Advanced PostHog-compatible telemetry endpoint |
279
282
 
280
283
  Legacy connection settings such as `sqlPreview.host`, `sqlPreview.port`,
package/dist/web-app.html CHANGED
@@ -1,14 +1,14 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Grain — Workbench</title>
6
+ <title>Grain</title>
7
7
  <meta name="theme-color" content="#1e1e1e" />
8
8
  <link
9
9
  rel="icon"
10
- type="image/svg+xml"
11
- href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%23007acc'/%3E%3Cpath fill='white' d='M3.75 3A1.75 1.75 0 0 0 2 4.75v6.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0 0 14 11.25v-6.5A1.75 1.75 0 0 0 12.25 3h-8.5ZM3.5 7.75v3.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5H3.5ZM12.5 6.25H3.5V4.75c0-.138.112-.25.25-.25h8.5a.25.25 0 0 1 .25.25v1.5Z'/%3E%3C/svg%3E"
10
+ type="image/png"
11
+ href="data:image/png;base64,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"
12
12
  />
13
13
  <script type="module" crossorigin>var _a2, _b2, _c2;
14
14
  (function polyfill() {
@@ -12549,10 +12549,15 @@ function useWebSession() {
12549
12549
  async function bootstrapLocalSession(location, history) {
12550
12550
  const params = new URLSearchParams(location.hash.replace(/^#/, ""));
12551
12551
  const capability = params.get("launch");
12552
- if (!capability) {
12553
- return "no-capability";
12552
+ const rawView = params.get("view");
12553
+ let view = void 0;
12554
+ if (rawView === "connections" || rawView === "connections-add") {
12555
+ view = rawView;
12554
12556
  }
12555
12557
  history.replaceState({}, "", `${location.pathname}${location.search}`);
12558
+ if (!capability) {
12559
+ return { session: "no-capability", view };
12560
+ }
12556
12561
  try {
12557
12562
  const response = await fetch(LOCAL_EXCHANGE_PATH, {
12558
12563
  method: "POST",
@@ -12563,11 +12568,11 @@ async function bootstrapLocalSession(location, history) {
12563
12568
  body: JSON.stringify({ capability })
12564
12569
  });
12565
12570
  if (response.status === 204) {
12566
- return "ok";
12571
+ return { session: "ok", view };
12567
12572
  }
12568
- return "failed";
12573
+ return { session: "failed", view };
12569
12574
  } catch {
12570
- return "failed";
12575
+ return { session: "failed", view };
12571
12576
  }
12572
12577
  }
12573
12578
  async function authorizedFetch(input, init2) {
@@ -14323,10 +14328,10 @@ function ReauthorizeNotice({
14323
14328
  reason
14324
14329
  }) {
14325
14330
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-reauth", role: "alert", children: [
14326
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-reauth-title", children: reason === "unreachable" ? "Grain is no longer answering at this address" : "Grain needs to authorize this page again" }),
14331
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-reauth-title", children: reason === "unreachable" ? "Grain is no longer answering at this address" : reason === "expired-link" ? "This link has already been used" : "Open this workspace from your terminal" }),
14327
14332
  /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
14328
- reason === "expired-link" && "This authorization link had already been used, or it expired.",
14329
- reason === "no-session" && "This page is no longer authorized. Grain restarts, a new tab, and a session left open for a while all end up here.",
14333
+ reason === "expired-link" && "An authorization link works once, and only for a short while.",
14334
+ reason === "no-session" && "A workspace session is short-lived. A new tab, a restart, or a session left open for a while all end up here.",
14330
14335
  reason === "unreachable" && "The service stopped, or it came back on a different port. This page cannot find it again on its own."
14331
14336
  ] }),
14332
14337
  /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
@@ -150739,8 +150744,8 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
150739
150744
  const mcpClientConfig = `{
150740
150745
  "mcpServers": {
150741
150746
  "grain": {
150742
- "command": "npx",
150743
- "args": ["-y", "grain-tools", "--stdio"]
150747
+ "command": "grain",
150748
+ "args": ["--stdio"]
150744
150749
  }
150745
150750
  }
150746
150751
  }`;
@@ -151286,7 +151291,7 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151286
151291
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Client configuration (recommended)" }),
151287
151292
  /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpClientConfig, label: "Copy client configuration" })
151288
151293
  ] }),
151289
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-description", children: "Works with every MCP client, including ones that cannot send custom headers. The server reads its own access token, so nothing secret goes in your config." }),
151294
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-description", children: "Uses the installed Grain service, so connections and result tabs stay shared. GUI clients that cannot find your shell PATH may need the full path to Grain." }),
151290
151295
  /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-client-config", children: mcpClientConfig })
151291
151296
  ] }),
151292
151297
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
@@ -151572,7 +151577,8 @@ function TabContextMenu({
151572
151577
  const LandingScreen = ({
151573
151578
  onNewConnection,
151574
151579
  onNewQueryFile,
151575
- onManageConnections
151580
+ onManageConnections,
151581
+ onTryDemo
151576
151582
  }) => {
151577
151583
  const { sendMessage, callServerTool, isReady } = useHostContext();
151578
151584
  const {
@@ -151629,6 +151635,13 @@ const LandingScreen = ({
151629
151635
  openSettingsPanel("connections");
151630
151636
  }
151631
151637
  };
151638
+ const handleTryDemo = () => {
151639
+ if (onTryDemo) {
151640
+ onTryDemo();
151641
+ } else {
151642
+ sendMessage("openDemo");
151643
+ }
151644
+ };
151632
151645
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-landing", role: "region", "aria-label": "Grain Welcome and Quick Actions", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-landing-card", children: [
151633
151646
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-landing-header", children: [
151634
151647
  /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "sp-landing-title", children: [
@@ -151695,6 +151708,19 @@ const LandingScreen = ({
151695
151708
  "Create Connection"
151696
151709
  ]
151697
151710
  }
151711
+ ),
151712
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
151713
+ "button",
151714
+ {
151715
+ type: "button",
151716
+ className: "sp-btn sp-landing-btn",
151717
+ onClick: handleTryDemo,
151718
+ "aria-label": "Try the Grain demo",
151719
+ children: [
151720
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CodeIcon, { size: 14 }),
151721
+ "Try the Demo"
151722
+ ]
151723
+ }
151698
151724
  )
151699
151725
  ] }),
151700
151726
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-landing-footer", children: [
@@ -152597,7 +152623,7 @@ const SIDEBAR_MIN_WIDTH = 160;
152597
152623
  const SIDEBAR_MAX_WIDTH = 400;
152598
152624
  const SIDEBAR_AUTO_COLLAPSE_QUERY = "(max-width: 640px)";
152599
152625
  const SIDEBAR_WIDTH_STORAGE_KEY = "sql-preview:web-sidebar-width";
152600
- function WebApp() {
152626
+ function WebApp({ viewHint }) {
152601
152627
  const { onMessage, isReady, sendMessage } = useHostContext();
152602
152628
  const session = useWebSession();
152603
152629
  const {
@@ -152607,7 +152633,8 @@ function WebApp() {
152607
152633
  setActiveTab,
152608
152634
  setTheme,
152609
152635
  projectionSessionId,
152610
- setProjectionSession
152636
+ setProjectionSession,
152637
+ openSettingsPanel
152611
152638
  } = useAppStore();
152612
152639
  const [sessions, setSessions] = reactExports.useState([]);
152613
152640
  const [monitorOpen, setMonitorOpen] = reactExports.useState(true);
@@ -152622,6 +152649,14 @@ function WebApp() {
152622
152649
  var _a3;
152623
152650
  (_a3 = sidebarRef.current) == null ? void 0 : _a3.style.setProperty("--sp-web-sidebar-width", `${sidebarWidth}px`);
152624
152651
  }, [sidebarWidth, monitorOpen]);
152652
+ const hasAppliedHint = reactExports.useRef(false);
152653
+ reactExports.useEffect(() => {
152654
+ if (hasAppliedHint.current || !viewHint) {
152655
+ return;
152656
+ }
152657
+ hasAppliedHint.current = true;
152658
+ openSettingsPanel("connections", viewHint === "connections-add");
152659
+ }, [viewHint, openSettingsPanel]);
152625
152660
  reactExports.useEffect(() => {
152626
152661
  const mediaQuery = window.matchMedia(SIDEBAR_AUTO_COLLAPSE_QUERY);
152627
152662
  const handleChange = (e2) => {
@@ -152964,14 +152999,14 @@ function WebApp() {
152964
152999
  }
152965
153000
  async function bootstrap() {
152966
153001
  const result = await bootstrapLocalSession(window.location, window.history);
152967
- if (result === "failed") {
153002
+ if (result.session === "failed") {
152968
153003
  ReactDOM.createRoot(document.getElementById("root")).render(
152969
153004
  /* @__PURE__ */ jsxRuntimeExports.jsx(React$2.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ReauthorizeNotice, { reason: "expired-link" }) })
152970
153005
  );
152971
153006
  return;
152972
153007
  }
152973
153008
  ReactDOM.createRoot(document.getElementById("root")).render(
152974
- /* @__PURE__ */ jsxRuntimeExports.jsx(React$2.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(WebHostProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(WebApp, {}) }) })
153009
+ /* @__PURE__ */ jsxRuntimeExports.jsx(React$2.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(WebHostProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(WebApp, { viewHint: result.view }) }) })
152975
153010
  );
152976
153011
  }
152977
153012
  bootstrap();</script>