@cloudgrid-io/mcp 0.5.0 → 0.5.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.js +60 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "MCP server for CloudGrid. Three editions: a local stdio server (full toolset), a hosted web server (light, CLI-free toolset), and a public docs server (search_cloudgrid_documentation) over MCP Streamable HTTP.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/tools.js CHANGED
@@ -9,7 +9,6 @@
9
9
  // The difference is injected as a `ctx` object, so the tool logic is written once.
10
10
 
11
11
  import { execFile } from "node:child_process";
12
- import { createRequire } from "node:module";
13
12
  import { promisify } from "node:util";
14
13
  import { readFile } from "node:fs/promises";
15
14
  import { readFileSync, existsSync, statSync } from "node:fs";
@@ -75,24 +74,42 @@ function resolveCwd(cwd) {
75
74
  return abs;
76
75
  }
77
76
 
78
- // Resolve the bundled CLI entry point from this package's own node_modules.
79
- // Returns the absolute path to the JS entry, or null if unresolvable.
77
+ // Pin the CLI version for the lazy npx fallback so behaviour is reproducible.
78
+ const CLI_NPX_PKG = "@cloudgrid-io/cli@^0.9.20";
79
+
80
+ // Minimum CLI version that supports the flags the MCP passes (--auto etc.).
81
+ const MIN_CLI_VERSION = "0.9.20";
82
+
83
+ // Simple semver comparison: true when `version` >= MIN_CLI_VERSION.
84
+ function meetsMinVersion(version) {
85
+ if (!version) return false;
86
+ const parse = (s) => s.replace(/^v/, "").split(".").map((p) => parseInt(p, 10) || 0);
87
+ const v = parse(version);
88
+ const m = parse(MIN_CLI_VERSION);
89
+ for (let i = 0; i < 3; i++) {
90
+ if ((v[i] || 0) > (m[i] || 0)) return true;
91
+ if ((v[i] || 0) < (m[i] || 0)) return false;
92
+ }
93
+ return true;
94
+ }
95
+
96
+ // Resolve the bundled CLI from this package's OWN node_modules only — never
97
+ // walk up to parent directories (which may contain stale CLI versions).
98
+ // Returns { entry, version } or null.
80
99
  function resolveBundledCli() {
81
100
  try {
82
- const require = createRequire(import.meta.url);
83
- const pkgPath = require.resolve("@cloudgrid-io/cli/package.json");
101
+ const srcDir = dirname(new URL(import.meta.url).pathname);
102
+ const pkgPath = join(srcDir, "..", "node_modules", "@cloudgrid-io", "cli", "package.json");
103
+ if (!existsSync(pkgPath)) return null;
84
104
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
85
105
  const bin = pkg.bin && pkg.bin.cloudgrid;
86
106
  if (!bin) return null;
87
- return join(dirname(pkgPath), bin);
107
+ return { entry: join(dirname(pkgPath), bin), version: pkg.version || null };
88
108
  } catch {
89
109
  return null;
90
110
  }
91
111
  }
92
112
 
93
- // Pin the CLI version for the lazy npx fallback so behaviour is reproducible.
94
- const CLI_NPX_PKG = "@cloudgrid-io/cli@^0.9.20";
95
-
96
113
  async function runCloudgrid(args, opts = {}) {
97
114
  const cwd = resolveCwd(opts.cwd);
98
115
  const execOpts = {
@@ -110,28 +127,52 @@ async function runCloudgrid(args, opts = {}) {
110
127
  return new Error(detail || "cloudgrid command failed");
111
128
  };
112
129
 
113
- // 1. Bundled CLI (the .mcpb ships it in its node_modules)
130
+ // 1. Bundled CLI — own node_modules only, version-gated
114
131
  const bundled = resolveBundledCli();
115
- if (bundled) {
132
+ if (bundled && meetsMinVersion(bundled.version)) {
116
133
  try {
117
134
  const { stdout, stderr } = await execFileAsync(
118
135
  process.execPath,
119
- [bundled, ...args],
136
+ [bundled.entry, ...args],
120
137
  execOpts,
121
138
  );
122
139
  return (stdout || stderr || "").trim() || "Done.";
123
140
  } catch (err) {
124
141
  throw extract(err);
125
142
  }
143
+ } else if (bundled) {
144
+ console.error(
145
+ `cloudgrid-mcp: bundled CLI ${bundled.version} < ${MIN_CLI_VERSION}, skipping`,
146
+ );
126
147
  }
127
148
 
128
- // 2. Global `cloudgrid` on PATH
129
- try {
130
- const { stdout, stderr } = await execFileAsync("cloudgrid", args, execOpts);
131
- return (stdout || stderr || "").trim() || "Done.";
132
- } catch (err) {
133
- if (!(err && err.code === "ENOENT")) throw extract(err);
134
- // Not found — fall through to lazy npx fetch
149
+ // 2. Global `cloudgrid` on PATH — version-gated
150
+ {
151
+ let useGlobal = false;
152
+ try {
153
+ const { stdout: vOut } = await execFileAsync("cloudgrid", ["--version"], {
154
+ timeout: 5_000,
155
+ stdio: ["ignore", "pipe", "pipe"],
156
+ });
157
+ const ver = vOut.trim().match(/(\d+\.\d+\.\d+)/)?.[1];
158
+ if (meetsMinVersion(ver)) {
159
+ useGlobal = true;
160
+ } else {
161
+ console.error(
162
+ `cloudgrid-mcp: global CLI ${ver || vOut.trim()} < ${MIN_CLI_VERSION}, skipping`,
163
+ );
164
+ }
165
+ } catch {
166
+ // Not on PATH or --version failed — skip
167
+ }
168
+ if (useGlobal) {
169
+ try {
170
+ const { stdout, stderr } = await execFileAsync("cloudgrid", args, execOpts);
171
+ return (stdout || stderr || "").trim() || "Done.";
172
+ } catch (err) {
173
+ throw extract(err);
174
+ }
175
+ }
135
176
  }
136
177
 
137
178
  // 3. Lazy fetch: npx -y @cloudgrid-io/cli@<pinned> <args>