@cloudgrid-io/mcp 0.3.3 → 0.3.4
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/src/tools.js +53 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "MCP server for CloudGrid. Two editions: a local stdio server (full toolset) and a hosted web server (light, CLI-free toolset) over MCP Streamable HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/tools.js
CHANGED
|
@@ -107,6 +107,26 @@ async function fetchUserOrgs(token) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// After an authenticated web drop, upgrade visibility to "link" so the artifact
|
|
111
|
+
// is shareable and its preview renders without a sign-in wall. Best-effort — a
|
|
112
|
+
// failure here does not fail the drop; the user can always call cloudgrid_visibility.
|
|
113
|
+
async function upgradeVisibilityToLink(ctx, entityId, orgSlug) {
|
|
114
|
+
const token = await ctx.getToken();
|
|
115
|
+
if (!token || !entityId) return false;
|
|
116
|
+
try {
|
|
117
|
+
const hdrs = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
|
|
118
|
+
if (orgSlug) hdrs["X-CloudGrid-Org"] = orgSlug;
|
|
119
|
+
const res = await fetch(`${API_BASE}/api/v2/inspirations/${encodeURIComponent(entityId)}`, {
|
|
120
|
+
method: "PATCH",
|
|
121
|
+
headers: hdrs,
|
|
122
|
+
body: JSON.stringify({ visibility: "link" }),
|
|
123
|
+
});
|
|
124
|
+
return res.ok;
|
|
125
|
+
} catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
// ── Direct-API tools (both editions) ───────────────────────────────────────────
|
|
111
131
|
function looksLikeFullHtml(s) {
|
|
112
132
|
const head = s.replace(/^/, "").trimStart().slice(0, 256).toLowerCase();
|
|
@@ -240,9 +260,14 @@ async function runDrop(ctx, { html, path: filePath, filename, anonymous, org, fr
|
|
|
240
260
|
...(data.expires_at ? { expires_at: data.expires_at } : {}),
|
|
241
261
|
};
|
|
242
262
|
if (ctx.edition === "web") {
|
|
263
|
+
// Default authed web drops to "link" visibility so the URL is shareable
|
|
264
|
+
// and the console thumbnail renders without a sign-in wall.
|
|
265
|
+
if (data.visibility !== "link" && data.entity_id) {
|
|
266
|
+
await upgradeVisibilityToLink(ctx, data.entity_id, orgSlug);
|
|
267
|
+
}
|
|
243
268
|
lines.push(`See and manage all your apps in your grid: ${CONSOLE_URL}`);
|
|
244
|
-
const vis =
|
|
245
|
-
lines.push(`Visible to: ${VISIBILITY_LABELS[vis]
|
|
269
|
+
const vis = "link";
|
|
270
|
+
lines.push(`Visible to: ${VISIBILITY_LABELS[vis]}. Want to restrict access? I can set it to only you or your org.`);
|
|
246
271
|
structured.console_url = CONSOLE_URL;
|
|
247
272
|
structured.current_visibility = vis;
|
|
248
273
|
structured.visibility_options = Object.entries(VISIBILITY_LABELS).map(([v, l]) => ({ value: v, label: l }));
|
|
@@ -263,9 +288,13 @@ async function runDrop(ctx, { html, path: filePath, filename, anonymous, org, fr
|
|
|
263
288
|
...(data.expires_at ? { expires_at: data.expires_at } : {}),
|
|
264
289
|
};
|
|
265
290
|
if (ctx.edition === "web") {
|
|
291
|
+
// Default authed web drops to "link" visibility (same as above).
|
|
292
|
+
if (data.visibility !== "link" && data.entity_id) {
|
|
293
|
+
await upgradeVisibilityToLink(ctx, data.entity_id, orgSlug);
|
|
294
|
+
}
|
|
266
295
|
lines.push(`See and manage all your apps in your grid: ${CONSOLE_URL}`);
|
|
267
|
-
const vis =
|
|
268
|
-
lines.push(`Visible to: ${VISIBILITY_LABELS[vis]
|
|
296
|
+
const vis = "link";
|
|
297
|
+
lines.push(`Visible to: ${VISIBILITY_LABELS[vis]}. Want to restrict access? I can set it to only you or your org.`);
|
|
269
298
|
structured.console_url = CONSOLE_URL;
|
|
270
299
|
structured.current_visibility = vis;
|
|
271
300
|
structured.visibility_options = Object.entries(VISIBILITY_LABELS).map(([v, l]) => ({ value: v, label: l }));
|
|
@@ -423,7 +452,7 @@ export function registerTools(server, ctx) {
|
|
|
423
452
|
path: z.string().optional().describe("Path to a local file to upload instead of inline HTML."),
|
|
424
453
|
filename: z.string().optional().describe("Filename to present. Defaults to index.html for inline HTML."),
|
|
425
454
|
anonymous: z.boolean().optional().describe("Force an anonymous drop even if the user is signed in."),
|
|
426
|
-
org: z.string().optional().describe("
|
|
455
|
+
org: z.string().optional().describe("Leave unset; the tool will ask the user which org to publish into. Only set this after the user picks from the list the tool returns."),
|
|
427
456
|
fresh: z
|
|
428
457
|
.boolean()
|
|
429
458
|
.optional()
|
|
@@ -463,20 +492,26 @@ export function registerTools(server, ctx) {
|
|
|
463
492
|
structured: { needs_sign_in: true, login_url: url },
|
|
464
493
|
});
|
|
465
494
|
}
|
|
466
|
-
// Org disambiguation:
|
|
467
|
-
|
|
495
|
+
// Org disambiguation: always validate the org against the user's real
|
|
496
|
+
// orgs. If the LLM guessed an org slug that doesn't match, ignore it
|
|
497
|
+
// and ask — this is why the >1-org ask didn't fire in the first test.
|
|
498
|
+
{
|
|
468
499
|
const orgs = await fetchUserOrgs(token);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
500
|
+
const suppliedOrg = input?.org;
|
|
501
|
+
const validOrg = suppliedOrg && orgs.some((o) => o.slug === suppliedOrg);
|
|
502
|
+
if (!validOrg) {
|
|
503
|
+
if (orgs.length > 1) {
|
|
504
|
+
const lines = ["Which org should this be published to?"];
|
|
505
|
+
for (const o of orgs) lines.push(` ${o.slug} — ${o.name} (${o.role})`);
|
|
506
|
+
lines.push("Pass the org slug in the org parameter to publish.");
|
|
507
|
+
return okResult({
|
|
508
|
+
text: lines.join("\n"),
|
|
509
|
+
structured: { needs_org: true, orgs },
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (orgs.length === 1) {
|
|
513
|
+
input = { ...(input || {}), org: orgs[0].slug };
|
|
514
|
+
}
|
|
480
515
|
}
|
|
481
516
|
}
|
|
482
517
|
}
|