grain-tools 0.10.8 → 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
package/dist/web-app.html CHANGED
@@ -150744,8 +150744,8 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
150744
150744
  const mcpClientConfig = `{
150745
150745
  "mcpServers": {
150746
150746
  "grain": {
150747
- "command": "npx",
150748
- "args": ["-y", "grain-tools", "--stdio"]
150747
+ "command": "grain",
150748
+ "args": ["--stdio"]
150749
150749
  }
150750
150750
  }
150751
150751
  }`;
@@ -151291,7 +151291,7 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151291
151291
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Client configuration (recommended)" }),
151292
151292
  /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpClientConfig, label: "Copy client configuration" })
151293
151293
  ] }),
151294
- /* @__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." }),
151295
151295
  /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-client-config", children: mcpClientConfig })
151296
151296
  ] }),
151297
151297
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
@@ -21724,6 +21724,9 @@ async function probeRegistration(runner, command, args) {
21724
21724
  const result = await runner.run(command, args);
21725
21725
  return result.exitCode === 0;
21726
21726
  }
21727
+ function isCommandMissing(error52) {
21728
+ return error52?.code === "ENOENT";
21729
+ }
21727
21730
  function createSupervisorAdapter(platform2, runner, uid) {
21728
21731
  if (platform2 === "darwin") {
21729
21732
  const target = (identifier) => `gui/${uid}/${identifier}`;
@@ -21736,10 +21739,26 @@ function createSupervisorAdapter(platform2, runner, uid) {
21736
21739
  }
21737
21740
  if (platform2 === "linux") {
21738
21741
  return {
21739
- isRegistered: (identifier) => probeRegistration(runner, "systemctl", ["--user", "is-enabled", identifier]),
21742
+ isRegistered: async (identifier) => {
21743
+ try {
21744
+ return await probeRegistration(runner, "systemctl", ["--user", "is-enabled", identifier]);
21745
+ } catch (error52) {
21746
+ if (isCommandMissing(error52)) {
21747
+ return false;
21748
+ }
21749
+ throw error52;
21750
+ }
21751
+ },
21740
21752
  register: async (_unitPath, identifier) => {
21741
- await runRegistration(platform2, runner, "systemctl", ["--user", "daemon-reload"]);
21742
- await runRegistration(platform2, runner, "systemctl", ["--user", "enable", identifier]);
21753
+ try {
21754
+ await runRegistration(platform2, runner, "systemctl", ["--user", "daemon-reload"]);
21755
+ await runRegistration(platform2, runner, "systemctl", ["--user", "enable", identifier]);
21756
+ } catch (error52) {
21757
+ if (isCommandMissing(error52)) {
21758
+ throw new ServiceManagerMissingError("systemctl was not found");
21759
+ }
21760
+ throw error52;
21761
+ }
21743
21762
  },
21744
21763
  unregister: async (identifier) => {
21745
21764
  await runner.run("systemctl", ["--user", "stop", identifier]);
@@ -21766,13 +21785,15 @@ function createSupervisorAdapter(platform2, runner, uid) {
21766
21785
  start: (identifier) => runStart(runner, "schtasks", ["/Run", "/TN", identifier])
21767
21786
  };
21768
21787
  }
21769
- var import_child_process2, UserServicesForbiddenError;
21788
+ var import_child_process2, UserServicesForbiddenError, ServiceManagerMissingError;
21770
21789
  var init_supervisorAdapter = __esm({
21771
21790
  "src/server/install/supervisorAdapter.ts"() {
21772
21791
  "use strict";
21773
21792
  import_child_process2 = require("child_process");
21774
21793
  UserServicesForbiddenError = class extends Error {
21775
21794
  };
21795
+ ServiceManagerMissingError = class extends UserServicesForbiddenError {
21796
+ };
21776
21797
  }
21777
21798
  });
21778
21799
 
@@ -21783,7 +21804,7 @@ var require_package = __commonJS({
21783
21804
  name: "grain",
21784
21805
  displayName: "Grain SQL",
21785
21806
  description: "Run SQL in VS Code and expose safe, visible MCP database tools for Claude, Cursor, and other AI agents.",
21786
- version: "0.10.8",
21807
+ version: "0.10.9",
21787
21808
  license: "UNLICENSED",
21788
21809
  mcpName: "io.github.pattrnlabs/grain",
21789
21810
  publisher: "pattrnlabs",
@@ -22185,7 +22206,7 @@ var require_package = __commonJS({
22185
22206
  "grain.mcpEnabled": {
22186
22207
  type: "boolean",
22187
22208
  default: false,
22188
- description: "(Beta) Serve the MCP surface so LLM agents can interact with Grain. When off, the daemon does not register /mcp at all. Changing this restarts the daemon. A bridge you launch yourself with `npx -y grain-tools --stdio` is treated as explicit consent and still works; to refuse MCP everywhere, set mcp.enabled to false in ~/.grain/daemon.json.",
22209
+ description: "(Beta) Serve the MCP surface so LLM agents can interact with Grain. When off, the daemon does not register /mcp at all. Changing this restarts the daemon. An explicitly launched stdio bridge is treated as consent and still works; to refuse MCP everywhere, set mcp.enabled to false in ~/.grain/daemon.json.",
22189
22210
  order: 20
22190
22211
  },
22191
22212
  "grain.mcpSafeMode": {
@@ -249855,9 +249876,15 @@ async function installCommand(deps = {}) {
249855
249876
  } catch {
249856
249877
  }
249857
249878
  writeServiceMode(fileSystem, configHome, { mode: "spawn-on-demand" });
249858
- writeOutput(
249859
- "This machine does not permit background services. Grain will start when needed instead of staying running; the first request may take a moment longer."
249860
- );
249879
+ if (error52 instanceof ServiceManagerMissingError) {
249880
+ writeOutput(
249881
+ "This machine has no service manager (systemctl was not found). Grain will start when needed instead of staying running; the first request may take a moment longer."
249882
+ );
249883
+ } else {
249884
+ writeOutput(
249885
+ "This machine does not permit background services. Grain will start when needed instead of staying running; the first request may take a moment longer."
249886
+ );
249887
+ }
249861
249888
  return 0;
249862
249889
  }
249863
249890
  const failure = new AppError("service_supervisor_registration_failed", describeError(error52), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grain-tools",
3
- "version": "0.10.8",
3
+ "version": "0.10.9",
4
4
  "mcpName": "io.github.pattrnlabs/grain",
5
5
  "description": "Standalone Grain MCP database server for VS Code, Claude Desktop, Cursor, and other MCP clients.",
6
6
  "main": "out/server/standalone.js",
package/server.json CHANGED
@@ -8,14 +8,14 @@
8
8
  "source": "github",
9
9
  "id": "1348511592"
10
10
  },
11
- "version": "0.10.8",
11
+ "version": "0.10.9",
12
12
  "websiteUrl": "https://grain.tools",
13
13
  "packages": [
14
14
  {
15
15
  "registryType": "npm",
16
16
  "registryBaseUrl": "https://registry.npmjs.org",
17
17
  "identifier": "grain-tools",
18
- "version": "0.10.8",
18
+ "version": "0.10.9",
19
19
  "runtimeHint": "npx",
20
20
  "runtimeArguments": [
21
21
  {