@cloudgrid-io/mcp 0.19.0 → 0.19.2
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 +3 -2
- package/src/tools.js +53 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
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": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"start:docs": "node src/docs.js",
|
|
41
41
|
"snapshot:corpus": "node scripts/snapshot-corpus.mjs",
|
|
42
42
|
"build:mcpb": "node scripts/build-mcpb.mjs",
|
|
43
|
-
"test:widgets": "node test/apps-widgets.test.mjs"
|
|
43
|
+
"test:widgets": "node test/apps-widgets.test.mjs",
|
|
44
|
+
"test:fork": "node test/fork.test.mjs"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
47
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
package/src/tools.js
CHANGED
|
@@ -1931,24 +1931,50 @@ async function authedApiCall(ctx, { method, pathName, body, verb }) {
|
|
|
1931
1931
|
}
|
|
1932
1932
|
if (!res.ok) {
|
|
1933
1933
|
const msg = data?.error?.message || data?.message || raw || `HTTP ${res.status}`;
|
|
1934
|
-
const
|
|
1935
|
-
|
|
1934
|
+
const codeStr = data?.error?.code || null;
|
|
1935
|
+
const err = new Error(`${verb} failed (HTTP ${res.status}${codeStr ? ` ${codeStr}` : ""}): ${msg}`);
|
|
1936
|
+
// Expose the structured status/code so callers can branch (e.g. runFork's
|
|
1937
|
+
// NOT_A_RUNTIME → inspiration-route fallback). Additive: existing callers
|
|
1938
|
+
// only read `.message`.
|
|
1939
|
+
err.status = res.status;
|
|
1940
|
+
err.code = codeStr;
|
|
1941
|
+
throw err;
|
|
1936
1942
|
}
|
|
1937
1943
|
return data;
|
|
1938
1944
|
}
|
|
1939
1945
|
|
|
1940
|
-
// Fork: start a NEW entity from an existing
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1946
|
+
// Fork: start a NEW entity from an existing source, copy-on-write with lineage.
|
|
1947
|
+
// Kind-aware (one fork verb, mirrors the CLI `fork` command): the caller may
|
|
1948
|
+
// not know the source's kind, so try the runtime route first and, when the
|
|
1949
|
+
// server reports the target is an inspiration (`400 NOT_A_RUNTIME`, emitted by
|
|
1950
|
+
// requireEntityAccess before the runtime handler runs), retry the inspiration
|
|
1951
|
+
// route. The inspiration remix lands in the caller's active grid, so no
|
|
1952
|
+
// `into_org_slug`; `source_version_id` has no meaning for inspirations.
|
|
1953
|
+
export async function runFork(ctx, { id, into_org_slug, name, source_version_id }) {
|
|
1954
|
+
let data;
|
|
1955
|
+
try {
|
|
1956
|
+
data = await authedApiCall(ctx, {
|
|
1957
|
+
method: "POST",
|
|
1958
|
+
pathName: `/api/v2/runtimes/${encodeURIComponent(id)}/fork`,
|
|
1959
|
+
body: {
|
|
1960
|
+
...(into_org_slug ? { into_org_slug } : {}),
|
|
1961
|
+
...(name ? { name } : {}),
|
|
1962
|
+
...(source_version_id ? { source_version_id } : {}),
|
|
1963
|
+
},
|
|
1964
|
+
verb: "Fork",
|
|
1965
|
+
});
|
|
1966
|
+
} catch (err) {
|
|
1967
|
+
if (err && err.code === "NOT_A_RUNTIME") {
|
|
1968
|
+
data = await authedApiCall(ctx, {
|
|
1969
|
+
method: "POST",
|
|
1970
|
+
pathName: `/api/v2/inspirations/${encodeURIComponent(id)}/fork`,
|
|
1971
|
+
body: { ...(name ? { name } : {}) },
|
|
1972
|
+
verb: "Fork",
|
|
1973
|
+
});
|
|
1974
|
+
} else {
|
|
1975
|
+
throw err;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1952
1978
|
const gridSlug = data?.grid_slug ?? data?.org?.slug ?? null;
|
|
1953
1979
|
const lines = [
|
|
1954
1980
|
`Forked: ${data?.name ?? id} (entity_id=${data?.entity_id ?? "?"})${gridSlug ? ` in grid ${gridSlug}` : ""}`,
|
|
@@ -2101,8 +2127,17 @@ async function readInspirationSourceViaApi(ctx, { entityId, slug, grid }) {
|
|
|
2101
2127
|
{ method: "GET", headers, signal: AbortSignal.timeout(15_000) },
|
|
2102
2128
|
);
|
|
2103
2129
|
if (!res.ok) continue;
|
|
2104
|
-
|
|
2105
|
-
|
|
2130
|
+
// The route (GET /v2/inspirations/:seg/source) serves the RAW HTML bytes
|
|
2131
|
+
// as text/html — NOT a JSON { html } envelope. Read the body as text.
|
|
2132
|
+
// (Tolerate a JSON { html } shape too, in case a variant ever returns it.)
|
|
2133
|
+
const ct = (res.headers.get("content-type") || "").toLowerCase();
|
|
2134
|
+
if (ct.includes("application/json")) {
|
|
2135
|
+
const data = await res.json().catch(() => null);
|
|
2136
|
+
if (data && typeof data.html === "string") return data.html;
|
|
2137
|
+
continue;
|
|
2138
|
+
}
|
|
2139
|
+
const html = await res.text().catch(() => null);
|
|
2140
|
+
if (typeof html === "string" && html.length > 0) return html;
|
|
2106
2141
|
} catch {
|
|
2107
2142
|
// try the next attempt, then the public-fetch fallback
|
|
2108
2143
|
}
|
|
@@ -2502,7 +2537,8 @@ export function registerTools(server, ctx) {
|
|
|
2502
2537
|
description:
|
|
2503
2538
|
"Start a NEW entity from an existing runtime (copy-on-write, lineage recorded). Lands in the " +
|
|
2504
2539
|
"source's home grid by default; cross-grid only for system templates or forkable:'public' sources. " +
|
|
2505
|
-
"Requires sign-in.
|
|
2540
|
+
"Requires sign-in. Kind-aware: forks a runtime (app/agent) OR an inspiration — " +
|
|
2541
|
+
"tries POST /api/v2/runtimes/:id/fork and falls back to /api/v2/inspirations/:id/fork.",
|
|
2506
2542
|
inputSchema: {
|
|
2507
2543
|
id: z.string().describe("The source runtime: a canonical UUID or <grid-slug>/<entity-slug>."),
|
|
2508
2544
|
into_org_slug: z.string().optional().describe("Destination grid slug. Required only when you belong to more than one grid."),
|